|
8 | 8 | using System.IO;
|
9 | 9 | using System.Linq;
|
10 | 10 | using System.Reflection;
|
| 11 | +using System.Text; |
11 | 12 | using System.Threading;
|
12 | 13 | using System.Threading.Tasks;
|
13 | 14 | using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
|
@@ -689,6 +690,59 @@ public void TestExecuteReader(string connection)
|
689 | 690 | });
|
690 | 691 | }
|
691 | 692 |
|
| 693 | + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] |
| 694 | + [ClassData(typeof(AEConnectionStringProvider))] |
| 695 | + public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) |
| 696 | + { |
| 697 | + string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false); |
| 698 | + if (randomName.Length > 50) |
| 699 | + { |
| 700 | + randomName = randomName.Substring(0, 50); |
| 701 | + } |
| 702 | + string tableName = $"VeryLong_{randomName}_TestTableName"; |
| 703 | + int columnsCount = 50; |
| 704 | + |
| 705 | + // Arrange - drops the table with long name and re-creates it with 52 columns (ID, name, ColumnName0..49) |
| 706 | + try |
| 707 | + { |
| 708 | + CreateTable(connectionString, tableName, columnsCount); |
| 709 | + string name = "nobody"; |
| 710 | + |
| 711 | + using (SqlConnection connection = new SqlConnection(connectionString)) |
| 712 | + { |
| 713 | + await connection.OpenAsync(); |
| 714 | + // This creates a "select top 100" query that has over 40k characters |
| 715 | + using (SqlCommand sqlCommand = new SqlCommand(GenerateSelectQuery(tableName, columnsCount, 10, "WHERE Name = @FirstName AND ID = @CustomerId"), |
| 716 | + connection, |
| 717 | + transaction: null, |
| 718 | + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) |
| 719 | + { |
| 720 | + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); |
| 721 | + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.VarChar, name.Length); |
| 722 | + |
| 723 | + sqlCommand.Parameters[0].Value = 0; |
| 724 | + sqlCommand.Parameters[1].Value = name; |
| 725 | + |
| 726 | + // Act and Assert |
| 727 | + // Test that execute reader async does not throw an exception. |
| 728 | + // The table is empty so there should be no results; however, the bug previously found is that it causes a TDS RPC exception on enclave. |
| 729 | + using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync()) |
| 730 | + { |
| 731 | + Assert.False(sqlDataReader.HasRows, "The table should be empty"); |
| 732 | + } |
| 733 | + } |
| 734 | + } |
| 735 | + } |
| 736 | + catch |
| 737 | + { |
| 738 | + throw; |
| 739 | + } |
| 740 | + finally |
| 741 | + { |
| 742 | + DropTableIfExists(connectionString, tableName); |
| 743 | + } |
| 744 | + } |
| 745 | + |
692 | 746 | [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))]
|
693 | 747 | [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet1))]
|
694 | 748 | public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehavior commandBehavior)
|
@@ -2788,6 +2842,75 @@ private void CleanUpTable(string connString, string tableName)
|
2788 | 2842 | }
|
2789 | 2843 | }
|
2790 | 2844 |
|
| 2845 | + private static void CreateTable(string connString, string tableName, int columnsCount) |
| 2846 | + => DataTestUtility.RunNonQuery(connString, GenerateCreateQuery(tableName, columnsCount)); |
| 2847 | + /// <summary> |
| 2848 | + /// Drops the table if the specified table exists |
| 2849 | + /// </summary> |
| 2850 | + /// <param name="connString">The connection string to the database</param> |
| 2851 | + /// <param name="tableName">The name of the table to be dropped</param> |
| 2852 | + private static void DropTableIfExists(string connString, string tableName) |
| 2853 | + { |
| 2854 | + using var sqlConnection = new SqlConnection(connString); |
| 2855 | + sqlConnection.Open(); |
| 2856 | + DataTestUtility.DropTable(sqlConnection, tableName); |
| 2857 | + } |
| 2858 | + |
| 2859 | + /// <summary> |
| 2860 | + /// Generates the query for creating a table with the number of bit columns specified. |
| 2861 | + /// </summary> |
| 2862 | + /// <param name="tableName">The name of the table</param> |
| 2863 | + /// <param name="columnsCount">The number of columns for the table</param> |
| 2864 | + /// <returns></returns> |
| 2865 | + private static string GenerateCreateQuery(string tableName, int columnsCount) |
| 2866 | + { |
| 2867 | + StringBuilder builder = new StringBuilder(); |
| 2868 | + builder.Append(string.Format("CREATE TABLE [dbo].[{0}]", tableName)); |
| 2869 | + builder.Append('('); |
| 2870 | + builder.AppendLine("[ID][bigint] NOT NULL,"); |
| 2871 | + builder.AppendLine("[Name] [varchar] (200) NOT NULL"); |
| 2872 | + for (int i = 0; i < columnsCount; i++) |
| 2873 | + { |
| 2874 | + builder.Append(','); |
| 2875 | + builder.Append($"[ColumnName{i}][bit] NULL"); |
| 2876 | + } |
| 2877 | + builder.Append(");"); |
| 2878 | + |
| 2879 | + return builder.ToString(); |
| 2880 | + } |
| 2881 | + |
| 2882 | + /// <summary> |
| 2883 | + /// Generates the large query with the select top 100 of all the columns repeated multiple times. |
| 2884 | + /// </summary> |
| 2885 | + /// <param name="tableName">The name of the table</param> |
| 2886 | + /// <param name="columnsCount">The number of columns to be explicitly included</param> |
| 2887 | + /// <param name="repeat">The number of times the select query is repeated</param> |
| 2888 | + /// <param name="where">A where clause for additional filters</param> |
| 2889 | + /// <returns></returns> |
| 2890 | + private static string GenerateSelectQuery(string tableName, int columnsCount, int repeat = 10, string where = "") |
| 2891 | + { |
| 2892 | + StringBuilder builder = new StringBuilder(); |
| 2893 | + builder.AppendLine($"SELECT TOP 100"); |
| 2894 | + builder.AppendLine($"[{tableName}].[ID],"); |
| 2895 | + builder.AppendLine($"[{tableName}].[Name]"); |
| 2896 | + for (int i = 0; i < columnsCount; i++) |
| 2897 | + { |
| 2898 | + builder.Append(","); |
| 2899 | + builder.AppendLine($"[{tableName}].[ColumnName{i}]"); |
| 2900 | + } |
| 2901 | + |
| 2902 | + string extra = string.IsNullOrEmpty(where) ? $"(NOLOCK) [{tableName}]" : where; |
| 2903 | + builder.AppendLine($"FROM [{tableName}] {extra};"); |
| 2904 | + |
| 2905 | + StringBuilder builder2 = new StringBuilder(); |
| 2906 | + for (int i = 0; i < repeat; i++) |
| 2907 | + { |
| 2908 | + builder2.AppendLine(builder.ToString()); |
| 2909 | + } |
| 2910 | + |
| 2911 | + return builder2.ToString(); |
| 2912 | + } |
| 2913 | + |
2791 | 2914 | /// <summary>
|
2792 | 2915 | /// An helper method to test the cancellation of the command using cancellationToken to async SqlCommand APIs.
|
2793 | 2916 | /// </summary>
|
|
0 commit comments