1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections; 6 using System.Diagnostics; 7 using System.Reflection; 8 using System.Threading.Tasks; 9 using System.Xml; 10 using Microsoft.SqlServer.TDS; 11 using Microsoft.SqlServer.TDS.Done; 12 using Microsoft.SqlServer.TDS.EndPoint; 13 using Microsoft.SqlServer.TDS.Error; 14 using Microsoft.SqlServer.TDS.Servers; 15 using Microsoft.SqlServer.TDS.SQLBatch; 16 using Xunit; 17 using System.Runtime.CompilerServices; 18 19 namespace System.Data.SqlClient.Tests 20 { 21 [ActiveIssue("dotnet/corefx #17925", TestPlatforms.Any)] 22 public class DiagnosticTest : RemoteExecutorTestBase 23 { 24 private const string BadConnectionString = "data source = bad; initial catalog = bad; uid = bad; password = bad; connection timeout = 1;"; 25 private static readonly string s_tcpConnStr = Environment.GetEnvironmentVariable("TEST_TCP_CONN_STR") ?? string.Empty; 26 IsConnectionStringConfigured()27 public static bool IsConnectionStringConfigured() => s_tcpConnStr != string.Empty; 28 29 [Fact] 30 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteScalarTest()31 public void ExecuteScalarTest() 32 { 33 RemoteInvoke(() => 34 { 35 CollectStatisticsDiagnostics(connectionString => 36 { 37 using (SqlConnection conn = new SqlConnection(connectionString)) 38 using (SqlCommand cmd = new SqlCommand()) 39 { 40 cmd.Connection = conn; 41 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 42 43 conn.Open(); 44 var output = cmd.ExecuteScalar(); 45 } 46 }); 47 return SuccessExitCode; 48 }).Dispose(); 49 } 50 51 [Fact] 52 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteScalarErrorTest()53 public void ExecuteScalarErrorTest() 54 { 55 RemoteInvoke(() => 56 { 57 CollectStatisticsDiagnostics(connectionString => 58 { 59 using (SqlConnection conn = new SqlConnection(connectionString)) 60 using (SqlCommand cmd = new SqlCommand()) 61 { 62 cmd.Connection = conn; 63 cmd.CommandText = "select 1 / 0;"; 64 65 conn.Open(); 66 67 try { var output = cmd.ExecuteScalar(); } 68 catch { } 69 } 70 }); 71 return SuccessExitCode; 72 }).Dispose(); 73 } 74 75 [Fact] 76 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteNonQueryTest()77 public void ExecuteNonQueryTest() 78 { 79 RemoteInvoke(() => 80 { 81 CollectStatisticsDiagnostics(connectionString => 82 { 83 using (SqlConnection conn = new SqlConnection(connectionString)) 84 using (SqlCommand cmd = new SqlCommand()) 85 { 86 cmd.Connection = conn; 87 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 88 89 conn.Open(); 90 var output = cmd.ExecuteNonQuery(); 91 } 92 }); 93 return SuccessExitCode; 94 }).Dispose(); 95 } 96 97 [Fact] 98 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteNonQueryErrorTest()99 public void ExecuteNonQueryErrorTest() 100 { 101 RemoteInvoke(() => 102 { 103 CollectStatisticsDiagnostics(connectionString => 104 { 105 using (SqlConnection conn = new SqlConnection(connectionString)) 106 { 107 using (SqlCommand cmd = new SqlCommand()) 108 { 109 cmd.Connection = conn; 110 cmd.CommandText = "select 1 / 0;"; 111 112 // Limiting the command timeout to 3 seconds. This should be lower than the Process timeout. 113 cmd.CommandTimeout = 3; 114 conn.Open(); 115 Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Connection Open Successful"); 116 117 try 118 { 119 var output = cmd.ExecuteNonQuery(); 120 } 121 catch (Exception e) 122 { 123 Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest " + e.Message); 124 } 125 Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Command Executed"); 126 } 127 Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Command Disposed"); 128 } 129 Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Connection Disposed"); 130 }); 131 return SuccessExitCode; 132 }).Dispose(); 133 } 134 135 [Fact] 136 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteReaderTest()137 public void ExecuteReaderTest() 138 { 139 RemoteInvoke(() => 140 { 141 CollectStatisticsDiagnostics(connectionString => 142 { 143 using (SqlConnection conn = new SqlConnection(connectionString)) 144 using (SqlCommand cmd = new SqlCommand()) 145 { 146 cmd.Connection = conn; 147 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 148 149 conn.Open(); 150 SqlDataReader reader = cmd.ExecuteReader(); 151 while (reader.Read()) { } 152 } 153 }); 154 return SuccessExitCode; 155 }).Dispose(); 156 } 157 158 [Fact] 159 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteReaderErrorTest()160 public void ExecuteReaderErrorTest() 161 { 162 RemoteInvoke(() => 163 { 164 CollectStatisticsDiagnostics(connectionString => 165 { 166 using (SqlConnection conn = new SqlConnection(connectionString)) 167 using (SqlCommand cmd = new SqlCommand()) 168 { 169 cmd.Connection = conn; 170 cmd.CommandText = "select 1 / 0;"; 171 172 try 173 { 174 SqlDataReader reader = cmd.ExecuteReader(); 175 while (reader.Read()) { } 176 } 177 catch { } 178 } 179 }); 180 return SuccessExitCode; 181 }).Dispose(); 182 } 183 184 [Fact] 185 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteReaderWithCommandBehaviorTest()186 public void ExecuteReaderWithCommandBehaviorTest() 187 { 188 RemoteInvoke(() => 189 { 190 CollectStatisticsDiagnostics(connectionString => 191 { 192 using (SqlConnection conn = new SqlConnection(connectionString)) 193 using (SqlCommand cmd = new SqlCommand()) 194 { 195 cmd.Connection = conn; 196 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 197 198 conn.Open(); 199 SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default); 200 while (reader.Read()) { } 201 } 202 }); 203 return SuccessExitCode; 204 }).Dispose(); 205 } 206 207 [ConditionalFact(nameof(IsConnectionStringConfigured))] 208 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteXmlReaderTest()209 public void ExecuteXmlReaderTest() 210 { 211 RemoteInvoke(cs => 212 { 213 CollectStatisticsDiagnostics(_ => 214 { 215 using (SqlConnection conn = new SqlConnection(cs)) 216 using (SqlCommand cmd = new SqlCommand()) 217 { 218 cmd.Connection = conn; 219 cmd.CommandText = "select * from sys.objects for xml auto, xmldata;"; 220 221 conn.Open(); 222 XmlReader reader = cmd.ExecuteXmlReader(); 223 while (reader.Read()) { } 224 } 225 }); 226 return SuccessExitCode; 227 }, s_tcpConnStr).Dispose(); 228 } 229 230 [Fact] 231 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteXmlReaderErrorTest()232 public void ExecuteXmlReaderErrorTest() 233 { 234 RemoteInvoke(() => 235 { 236 CollectStatisticsDiagnostics(connectionString => 237 { 238 using (SqlConnection conn = new SqlConnection(connectionString)) 239 using (SqlCommand cmd = new SqlCommand()) 240 { 241 cmd.Connection = conn; 242 cmd.CommandText = "select *, baddata = 1 / 0 from sys.objects for xml auto, xmldata;"; 243 244 try 245 { 246 XmlReader reader = cmd.ExecuteXmlReader(); 247 while (reader.Read()) { } 248 } 249 catch { } 250 } 251 }); 252 return SuccessExitCode; 253 }).Dispose(); 254 } 255 256 [Fact] 257 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteScalarAsyncTest()258 public void ExecuteScalarAsyncTest() 259 { 260 RemoteInvoke(() => 261 { 262 CollectStatisticsDiagnosticsAsync(async connectionString => 263 { 264 using (SqlConnection conn = new SqlConnection(connectionString)) 265 using (SqlCommand cmd = new SqlCommand()) 266 { 267 cmd.Connection = conn; 268 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 269 270 conn.Open(); 271 var output = await cmd.ExecuteScalarAsync(); 272 } 273 }).GetAwaiter().GetResult(); 274 return SuccessExitCode; 275 }).Dispose(); 276 } 277 278 [Fact] 279 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteScalarAsyncErrorTest()280 public void ExecuteScalarAsyncErrorTest() 281 { 282 RemoteInvoke(() => 283 { 284 CollectStatisticsDiagnosticsAsync(async connectionString => 285 { 286 using (SqlConnection conn = new SqlConnection(connectionString)) 287 using (SqlCommand cmd = new SqlCommand()) 288 { 289 cmd.Connection = conn; 290 cmd.CommandText = "select 1 / 0;"; 291 292 conn.Open(); 293 294 try { var output = await cmd.ExecuteScalarAsync(); } 295 catch { } 296 } 297 }).GetAwaiter().GetResult(); 298 return SuccessExitCode; 299 }).Dispose(); 300 } 301 302 [Fact] 303 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteNonQueryAsyncTest()304 public void ExecuteNonQueryAsyncTest() 305 { 306 RemoteInvoke(() => 307 { 308 CollectStatisticsDiagnosticsAsync(async connectionString => 309 { 310 using (SqlConnection conn = new SqlConnection(connectionString)) 311 using (SqlCommand cmd = new SqlCommand()) 312 { 313 cmd.Connection = conn; 314 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 315 316 conn.Open(); 317 var output = await cmd.ExecuteNonQueryAsync(); 318 } 319 }).GetAwaiter().GetResult(); 320 return SuccessExitCode; 321 }).Dispose(); 322 } 323 324 [Fact] 325 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteNonQueryAsyncErrorTest()326 public void ExecuteNonQueryAsyncErrorTest() 327 { 328 RemoteInvoke(() => 329 { 330 CollectStatisticsDiagnosticsAsync(async connectionString => 331 { 332 using (SqlConnection conn = new SqlConnection(connectionString)) 333 using (SqlCommand cmd = new SqlCommand()) 334 { 335 cmd.Connection = conn; 336 cmd.CommandText = "select 1 / 0;"; 337 338 conn.Open(); 339 try { var output = await cmd.ExecuteNonQueryAsync(); } 340 catch { } 341 } 342 }).GetAwaiter().GetResult(); 343 return SuccessExitCode; 344 }).Dispose(); 345 } 346 347 [Fact] 348 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteReaderAsyncTest()349 public void ExecuteReaderAsyncTest() 350 { 351 RemoteInvoke(() => 352 { 353 CollectStatisticsDiagnosticsAsync(async connectionString => 354 { 355 using (SqlConnection conn = new SqlConnection(connectionString)) 356 using (SqlCommand cmd = new SqlCommand()) 357 { 358 cmd.Connection = conn; 359 cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; 360 361 conn.Open(); 362 SqlDataReader reader = await cmd.ExecuteReaderAsync(); 363 while (reader.Read()) { } 364 } 365 }).GetAwaiter().GetResult(); 366 return SuccessExitCode; 367 }).Dispose(); 368 } 369 370 [Fact] 371 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteReaderAsyncErrorTest()372 public void ExecuteReaderAsyncErrorTest() 373 { 374 RemoteInvoke(() => 375 { 376 CollectStatisticsDiagnosticsAsync(async connectionString => 377 { 378 using (SqlConnection conn = new SqlConnection(connectionString)) 379 using (SqlCommand cmd = new SqlCommand()) 380 { 381 cmd.Connection = conn; 382 cmd.CommandText = "select 1 / 0;"; 383 384 try 385 { 386 SqlDataReader reader = await cmd.ExecuteReaderAsync(); 387 while (reader.Read()) { } 388 } 389 catch { } 390 } 391 }).GetAwaiter().GetResult(); 392 return SuccessExitCode; 393 }).Dispose(); 394 } 395 396 [ConditionalFact(nameof(IsConnectionStringConfigured))] 397 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteXmlReaderAsyncTest()398 public void ExecuteXmlReaderAsyncTest() 399 { 400 RemoteInvoke(cs => 401 { 402 CollectStatisticsDiagnosticsAsync(async _ => 403 { 404 using (SqlConnection conn = new SqlConnection(cs)) 405 using (SqlCommand cmd = new SqlCommand()) 406 { 407 cmd.Connection = conn; 408 cmd.CommandText = "select * from sys.objects for xml auto, xmldata;"; 409 410 conn.Open(); 411 XmlReader reader = await cmd.ExecuteXmlReaderAsync(); 412 while (reader.Read()) { } 413 } 414 }).GetAwaiter().GetResult(); 415 return SuccessExitCode; 416 }, s_tcpConnStr).Dispose(); 417 } 418 419 [ConditionalFact(nameof(IsConnectionStringConfigured))] 420 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ExecuteXmlReaderAsyncErrorTest()421 public void ExecuteXmlReaderAsyncErrorTest() 422 { 423 RemoteInvoke(cs => 424 { 425 CollectStatisticsDiagnosticsAsync(async _ => 426 { 427 using (SqlConnection conn = new SqlConnection(cs)) 428 using (SqlCommand cmd = new SqlCommand()) 429 { 430 cmd.Connection = conn; 431 cmd.CommandText = "select *, baddata = 1 / 0 from sys.objects for xml auto, xmldata;"; 432 433 try 434 { 435 XmlReader reader = await cmd.ExecuteXmlReaderAsync(); 436 while (reader.Read()) { } 437 } 438 catch { } 439 } 440 }).GetAwaiter().GetResult(); 441 return SuccessExitCode; 442 }, s_tcpConnStr).Dispose(); 443 } 444 445 [Fact] 446 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ConnectionOpenTest()447 public void ConnectionOpenTest() 448 { 449 RemoteInvoke(() => 450 { 451 CollectStatisticsDiagnostics(connectionString => 452 { 453 using (SqlConnection sqlConnection = new SqlConnection(connectionString)) 454 { 455 sqlConnection.Open(); 456 Console.WriteLine("SqlClient.DiagnosticsTest.ConnectionOpenTest:: Connection Opened "); 457 } 458 Console.WriteLine("SqlClient.DiagnosticsTest.ConnectionOpenTest:: Connection Should Be Disposed"); 459 }, true); 460 461 Console.WriteLine("SqlClient.DiagnosticsTest.ConnectionOpenTest:: Done with Diagnostics collection"); 462 return SuccessExitCode; 463 }).Dispose(); 464 } 465 466 [Fact] 467 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ConnectionOpenErrorTest()468 public void ConnectionOpenErrorTest() 469 { 470 RemoteInvoke(() => 471 { 472 CollectStatisticsDiagnostics(_ => 473 { 474 using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) 475 { 476 try { sqlConnection.Open(); } catch { } 477 } 478 }); 479 return SuccessExitCode; 480 }).Dispose(); 481 } 482 483 [Fact] 484 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ConnectionOpenAsyncTest()485 public void ConnectionOpenAsyncTest() 486 { 487 RemoteInvoke(() => 488 { 489 CollectStatisticsDiagnosticsAsync(async connectionString => 490 { 491 using (SqlConnection sqlConnection = new SqlConnection(connectionString)) 492 { 493 await sqlConnection.OpenAsync(); 494 } 495 }).GetAwaiter().GetResult(); 496 return SuccessExitCode; 497 }).Dispose(); 498 } 499 500 [Fact] 501 [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot ConnectionOpenAsyncErrorTest()502 public void ConnectionOpenAsyncErrorTest() 503 { 504 RemoteInvoke(() => 505 { 506 CollectStatisticsDiagnosticsAsync(async _ => 507 { 508 using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) 509 { 510 try { await sqlConnection.OpenAsync(); } catch { } 511 } 512 }).GetAwaiter().GetResult(); 513 return SuccessExitCode; 514 }).Dispose(); 515 } 516 CollectStatisticsDiagnostics(Action<string> sqlOperation, bool enableServerLogging = false, [CallerMemberName] string methodName = R)517 private static void CollectStatisticsDiagnostics(Action<string> sqlOperation, bool enableServerLogging = false, [CallerMemberName] string methodName = "") 518 { 519 bool statsLogged = false; 520 bool operationHasError = false; 521 Guid beginOperationId = Guid.Empty; 522 523 FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => 524 { 525 IDictionary statistics; 526 527 if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandBefore")) 528 { 529 Assert.NotNull(kvp.Value); 530 531 Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId"); 532 Assert.NotEqual(retrievedOperationId, Guid.Empty); 533 534 SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command"); 535 Assert.NotNull(sqlCommand); 536 537 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 538 Assert.False(string.IsNullOrWhiteSpace(operation)); 539 540 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 541 if (sqlCommand.Connection.State == ConnectionState.Open) 542 { 543 Assert.NotEqual(connectionId, Guid.Empty); 544 } 545 546 beginOperationId = retrievedOperationId; 547 548 statsLogged = true; 549 } 550 else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandAfter")) 551 { 552 Assert.NotNull(kvp.Value); 553 554 Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId"); 555 Assert.NotEqual(retrievedOperationId, Guid.Empty); 556 557 SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command"); 558 Assert.NotNull(sqlCommand); 559 560 statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics"); 561 if (!operationHasError) 562 Assert.NotNull(statistics); 563 564 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 565 Assert.False(string.IsNullOrWhiteSpace(operation)); 566 567 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 568 if (sqlCommand.Connection.State == ConnectionState.Open) 569 { 570 Assert.NotEqual(connectionId, Guid.Empty); 571 } 572 573 // if we get to this point, then statistics exist and this must be the "end" 574 // event, so we need to make sure the operation IDs match 575 Assert.Equal(retrievedOperationId, beginOperationId); 576 beginOperationId = Guid.Empty; 577 578 statsLogged = true; 579 } 580 else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandError")) 581 { 582 operationHasError = true; 583 Assert.NotNull(kvp.Value); 584 585 SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command"); 586 Assert.NotNull(sqlCommand); 587 588 Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception"); 589 Assert.NotNull(ex); 590 591 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 592 Assert.False(string.IsNullOrWhiteSpace(operation)); 593 594 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 595 if (sqlCommand.Connection.State == ConnectionState.Open) 596 { 597 Assert.NotEqual(connectionId, Guid.Empty); 598 } 599 600 statsLogged = true; 601 } 602 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenBefore")) 603 { 604 Assert.NotNull(kvp.Value); 605 606 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 607 Assert.NotNull(sqlConnection); 608 609 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 610 Assert.False(string.IsNullOrWhiteSpace(operation)); 611 612 statsLogged = true; 613 } 614 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenAfter")) 615 { 616 Assert.NotNull(kvp.Value); 617 618 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 619 Assert.NotNull(sqlConnection); 620 621 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 622 Assert.False(string.IsNullOrWhiteSpace(operation)); 623 624 statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics"); 625 Assert.NotNull(statistics); 626 627 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 628 Assert.NotEqual(connectionId, Guid.Empty); 629 630 statsLogged = true; 631 } 632 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenError")) 633 { 634 Assert.NotNull(kvp.Value); 635 636 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 637 Assert.NotNull(sqlConnection); 638 639 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 640 Assert.False(string.IsNullOrWhiteSpace(operation)); 641 642 Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception"); 643 Assert.NotNull(ex); 644 645 statsLogged = true; 646 } 647 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseBefore")) 648 { 649 Assert.NotNull(kvp.Value); 650 651 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 652 Assert.NotNull(sqlConnection); 653 654 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 655 Assert.False(string.IsNullOrWhiteSpace(operation)); 656 657 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 658 Assert.NotEqual(connectionId, Guid.Empty); 659 660 statsLogged = true; 661 } 662 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseAfter")) 663 { 664 Assert.NotNull(kvp.Value); 665 666 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 667 Assert.NotNull(sqlConnection); 668 669 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 670 Assert.False(string.IsNullOrWhiteSpace(operation)); 671 672 statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics"); 673 674 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 675 Assert.NotEqual(connectionId, Guid.Empty); 676 677 statsLogged = true; 678 } 679 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseError")) 680 { 681 Assert.NotNull(kvp.Value); 682 683 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 684 Assert.NotNull(sqlConnection); 685 686 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 687 Assert.False(string.IsNullOrWhiteSpace(operation)); 688 689 Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception"); 690 Assert.NotNull(ex); 691 692 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 693 Assert.NotEqual(connectionId, Guid.Empty); 694 695 statsLogged = true; 696 } 697 }); 698 699 diagnosticListenerObserver.Enable(); 700 using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) 701 { 702 703 Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); 704 using (var server = TestTdsServer.StartServerWithQueryEngine(new DiagnosticsQueryEngine(), enableLog:enableServerLogging, methodName: methodName)) 705 { 706 Console.WriteLine(string.Format("Test: {0} Started Server", methodName)); 707 sqlOperation(server.ConnectionString); 708 709 Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); 710 711 Assert.True(statsLogged); 712 713 diagnosticListenerObserver.Disable(); 714 715 Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName)); 716 } 717 Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName)); 718 } 719 Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName)); 720 } 721 CollectStatisticsDiagnosticsAsync(Func<string, Task> sqlOperation, [CallerMemberName] string methodName = R)722 private static async Task CollectStatisticsDiagnosticsAsync(Func<string, Task> sqlOperation, [CallerMemberName] string methodName = "") 723 { 724 bool statsLogged = false; 725 bool operationHasError = false; 726 Guid beginOperationId = Guid.Empty; 727 728 FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => 729 { 730 IDictionary statistics; 731 732 if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandBefore")) 733 { 734 Assert.NotNull(kvp.Value); 735 736 Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId"); 737 Assert.NotEqual(retrievedOperationId, Guid.Empty); 738 739 SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command"); 740 Assert.NotNull(sqlCommand); 741 742 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 743 Assert.False(string.IsNullOrWhiteSpace(operation)); 744 745 beginOperationId = retrievedOperationId; 746 747 statsLogged = true; 748 } 749 else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandAfter")) 750 { 751 Assert.NotNull(kvp.Value); 752 753 Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId"); 754 Assert.NotEqual(retrievedOperationId, Guid.Empty); 755 756 SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command"); 757 Assert.NotNull(sqlCommand); 758 759 statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics"); 760 if (!operationHasError) 761 Assert.NotNull(statistics); 762 763 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 764 Assert.False(string.IsNullOrWhiteSpace(operation)); 765 766 // if we get to this point, then statistics exist and this must be the "end" 767 // event, so we need to make sure the operation IDs match 768 Assert.Equal(retrievedOperationId, beginOperationId); 769 beginOperationId = Guid.Empty; 770 771 statsLogged = true; 772 } 773 else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandError")) 774 { 775 operationHasError = true; 776 Assert.NotNull(kvp.Value); 777 778 SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command"); 779 Assert.NotNull(sqlCommand); 780 781 Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception"); 782 Assert.NotNull(ex); 783 784 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 785 Assert.False(string.IsNullOrWhiteSpace(operation)); 786 787 statsLogged = true; 788 } 789 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenBefore")) 790 { 791 Assert.NotNull(kvp.Value); 792 793 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 794 Assert.NotNull(sqlConnection); 795 796 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 797 Assert.False(string.IsNullOrWhiteSpace(operation)); 798 799 statsLogged = true; 800 } 801 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenAfter")) 802 { 803 Assert.NotNull(kvp.Value); 804 805 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 806 Assert.NotNull(sqlConnection); 807 808 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 809 Assert.False(string.IsNullOrWhiteSpace(operation)); 810 811 statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics"); 812 Assert.NotNull(statistics); 813 814 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 815 if (sqlConnection.State == ConnectionState.Open) 816 { 817 Assert.NotEqual(connectionId, Guid.Empty); 818 } 819 820 statsLogged = true; 821 } 822 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenError")) 823 { 824 Assert.NotNull(kvp.Value); 825 826 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 827 Assert.NotNull(sqlConnection); 828 829 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 830 Assert.False(string.IsNullOrWhiteSpace(operation)); 831 832 Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception"); 833 Assert.NotNull(ex); 834 835 statsLogged = true; 836 } 837 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseBefore")) 838 { 839 Assert.NotNull(kvp.Value); 840 841 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 842 Assert.NotNull(sqlConnection); 843 844 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 845 Assert.False(string.IsNullOrWhiteSpace(operation)); 846 847 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 848 Assert.NotEqual(connectionId, Guid.Empty); 849 850 statsLogged = true; 851 } 852 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseAfter")) 853 { 854 Assert.NotNull(kvp.Value); 855 856 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 857 Assert.NotNull(sqlConnection); 858 859 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 860 Assert.False(string.IsNullOrWhiteSpace(operation)); 861 862 statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics"); 863 864 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 865 Assert.NotEqual(connectionId, Guid.Empty); 866 867 statsLogged = true; 868 } 869 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseError")) 870 { 871 Assert.NotNull(kvp.Value); 872 873 SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection"); 874 Assert.NotNull(sqlConnection); 875 876 string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation"); 877 Assert.False(string.IsNullOrWhiteSpace(operation)); 878 879 Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception"); 880 Assert.NotNull(ex); 881 882 Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId"); 883 Assert.NotEqual(connectionId, Guid.Empty); 884 885 statsLogged = true; 886 } 887 }); 888 889 diagnosticListenerObserver.Enable(); 890 using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) 891 { 892 Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); 893 using (var server = TestTdsServer.StartServerWithQueryEngine(new DiagnosticsQueryEngine(), methodName: methodName)) 894 { 895 Console.WriteLine(string.Format("Test: {0} Started Server", methodName)); 896 897 await sqlOperation(server.ConnectionString); 898 899 Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); 900 901 Assert.True(statsLogged); 902 903 diagnosticListenerObserver.Disable(); 904 905 Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName)); 906 } 907 Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName)); 908 } 909 Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName)); 910 } 911 GetPropertyValueFromType(object obj, string propName)912 private static T GetPropertyValueFromType<T>(object obj, string propName) 913 { 914 Type type = obj.GetType(); 915 PropertyInfo pi = type.GetRuntimeProperty(propName); 916 917 var propertyValue = pi.GetValue(obj); 918 return (T)propertyValue; 919 } 920 } 921 922 public class DiagnosticsQueryEngine : QueryEngine 923 { DiagnosticsQueryEngine()924 public DiagnosticsQueryEngine() : base(new TDSServerArguments()) 925 { 926 } 927 CreateQueryResponse(ITDSServerSession session, TDSSQLBatchToken batchRequest)928 protected override TDSMessageCollection CreateQueryResponse(ITDSServerSession session, TDSSQLBatchToken batchRequest) 929 { 930 string lowerBatchText = batchRequest.Text.ToLowerInvariant(); 931 932 if (lowerBatchText.Contains("1 / 0")) // SELECT 1/0 933 { 934 TDSErrorToken errorToken = new TDSErrorToken(8134, 1, 16, "Divide by zero error encountered."); 935 TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Count, TDSDoneTokenCommandType.Select, 1); 936 TDSMessage responseMessage = new TDSMessage(TDSMessageType.Response, errorToken, doneToken); 937 return new TDSMessageCollection(responseMessage); 938 } 939 else 940 { 941 return base.CreateQueryResponse(session, batchRequest); 942 } 943 } 944 } 945 } 946