|
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.ManualTesting.Tests.AlwaysEncrypted.Setup; |
@@ -728,6 +729,59 @@ public void TestExecuteReader(string connection) |
728 | 729 | }); |
729 | 730 | } |
730 | 731 |
|
| 732 | + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] |
| 733 | + [ClassData(typeof(AEConnectionStringProvider))] |
| 734 | + public async void TestExecuteReaderAsyncWithLargeQuery(string connectionString) |
| 735 | + { |
| 736 | + string randomName = DataTestUtility.GetUniqueName(Guid.NewGuid().ToString().Replace("-", ""), false); |
| 737 | + if (randomName.Length > 50) |
| 738 | + { |
| 739 | + randomName = randomName.Substring(0, 50); |
| 740 | + } |
| 741 | + string tableName = $"VeryLong_{randomName}_TestTableName"; |
| 742 | + int columnsCount = 50; |
| 743 | + |
| 744 | + // Arrange - drops the table with long name and re-creates it with 52 columns (ID, name, ColumnName0..49) |
| 745 | + try |
| 746 | + { |
| 747 | + CreateTable(connectionString, tableName, columnsCount); |
| 748 | + string name = "nobody"; |
| 749 | + |
| 750 | + using (SqlConnection connection = new SqlConnection(connectionString)) |
| 751 | + { |
| 752 | + await connection.OpenAsync(); |
| 753 | + // This creates a "select top 100" query that has over 40k characters |
| 754 | + using (SqlCommand sqlCommand = new SqlCommand(GenerateSelectQuery(tableName, columnsCount, 10, "WHERE Name = @FirstName AND ID = @CustomerId"), |
| 755 | + connection, |
| 756 | + transaction: null, |
| 757 | + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) |
| 758 | + { |
| 759 | + sqlCommand.Parameters.Add(@"CustomerId", SqlDbType.Int); |
| 760 | + sqlCommand.Parameters.Add(@"FirstName", SqlDbType.VarChar, name.Length); |
| 761 | + |
| 762 | + sqlCommand.Parameters[0].Value = 0; |
| 763 | + sqlCommand.Parameters[1].Value = name; |
| 764 | + |
| 765 | + // Act and Assert |
| 766 | + // Test that execute reader async does not throw an exception. |
| 767 | + // 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. |
| 768 | + using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync()) |
| 769 | + { |
| 770 | + Assert.False(sqlDataReader.HasRows, "The table should be empty"); |
| 771 | + } |
| 772 | + } |
| 773 | + } |
| 774 | + } |
| 775 | + catch |
| 776 | + { |
| 777 | + throw; |
| 778 | + } |
| 779 | + finally |
| 780 | + { |
| 781 | + DropTableIfExists(connectionString, tableName); |
| 782 | + } |
| 783 | + } |
| 784 | + |
731 | 785 | [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] |
732 | 786 | [ClassData(typeof(AEConnectionStringProviderWithCommandBehaviorSet1))] |
733 | 787 | public void TestExecuteReaderWithCommandBehavior(string connection, CommandBehavior commandBehavior) |
@@ -2552,6 +2606,75 @@ private void CleanUpTable(string connString, string tableName) |
2552 | 2606 | } |
2553 | 2607 | } |
2554 | 2608 |
|
| 2609 | + private static void CreateTable(string connString, string tableName, int columnsCount) |
| 2610 | + => DataTestUtility.RunNonQuery(connString, GenerateCreateQuery(tableName, columnsCount)); |
| 2611 | + /// <summary> |
| 2612 | + /// Drops the table if the specified table exists |
| 2613 | + /// </summary> |
| 2614 | + /// <param name="connString">The connection string to the database</param> |
| 2615 | + /// <param name="tableName">The name of the table to be dropped</param> |
| 2616 | + private static void DropTableIfExists(string connString, string tableName) |
| 2617 | + { |
| 2618 | + using var sqlConnection = new SqlConnection(connString); |
| 2619 | + sqlConnection.Open(); |
| 2620 | + DataTestUtility.DropTable(sqlConnection, tableName); |
| 2621 | + } |
| 2622 | + |
| 2623 | + /// <summary> |
| 2624 | + /// Generates the query for creating a table with the number of bit columns specified. |
| 2625 | + /// </summary> |
| 2626 | + /// <param name="tableName">The name of the table</param> |
| 2627 | + /// <param name="columnsCount">The number of columns for the table</param> |
| 2628 | + /// <returns></returns> |
| 2629 | + private static string GenerateCreateQuery(string tableName, int columnsCount) |
| 2630 | + { |
| 2631 | + StringBuilder builder = new StringBuilder(); |
| 2632 | + builder.Append(string.Format("CREATE TABLE [dbo].[{0}]", tableName)); |
| 2633 | + builder.Append('('); |
| 2634 | + builder.AppendLine("[ID][bigint] NOT NULL,"); |
| 2635 | + builder.AppendLine("[Name] [varchar] (200) NOT NULL"); |
| 2636 | + for (int i = 0; i < columnsCount; i++) |
| 2637 | + { |
| 2638 | + builder.Append(','); |
| 2639 | + builder.Append($"[ColumnName{i}][bit] NULL"); |
| 2640 | + } |
| 2641 | + builder.Append(");"); |
| 2642 | + |
| 2643 | + return builder.ToString(); |
| 2644 | + } |
| 2645 | + |
| 2646 | + /// <summary> |
| 2647 | + /// Generates the large query with the select top 100 of all the columns repeated multiple times. |
| 2648 | + /// </summary> |
| 2649 | + /// <param name="tableName">The name of the table</param> |
| 2650 | + /// <param name="columnsCount">The number of columns to be explicitly included</param> |
| 2651 | + /// <param name="repeat">The number of times the select query is repeated</param> |
| 2652 | + /// <param name="where">A where clause for additional filters</param> |
| 2653 | + /// <returns></returns> |
| 2654 | + private static string GenerateSelectQuery(string tableName, int columnsCount, int repeat = 10, string where = "") |
| 2655 | + { |
| 2656 | + StringBuilder builder = new StringBuilder(); |
| 2657 | + builder.AppendLine($"SELECT TOP 100"); |
| 2658 | + builder.AppendLine($"[{tableName}].[ID],"); |
| 2659 | + builder.AppendLine($"[{tableName}].[Name]"); |
| 2660 | + for (int i = 0; i < columnsCount; i++) |
| 2661 | + { |
| 2662 | + builder.Append(","); |
| 2663 | + builder.AppendLine($"[{tableName}].[ColumnName{i}]"); |
| 2664 | + } |
| 2665 | + |
| 2666 | + string extra = string.IsNullOrEmpty(where) ? $"(NOLOCK) [{tableName}]" : where; |
| 2667 | + builder.AppendLine($"FROM [{tableName}] {extra};"); |
| 2668 | + |
| 2669 | + StringBuilder builder2 = new StringBuilder(); |
| 2670 | + for (int i = 0; i < repeat; i++) |
| 2671 | + { |
| 2672 | + builder2.AppendLine(builder.ToString()); |
| 2673 | + } |
| 2674 | + |
| 2675 | + return builder2.ToString(); |
| 2676 | + } |
| 2677 | + |
2555 | 2678 | /// <summary> |
2556 | 2679 | /// An helper method to test the cancellation of the command using cancellationToken to async SqlCommand APIs. |
2557 | 2680 | /// </summary> |
|
0 commit comments