[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlConnection.cs
blob92158691a5ef3d681e0d9f13bb8f2c5d7256834e
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.Data.DataSetExtensions, PublicKey="+AssemblyRef.EcmaPublicKeyFull)] // DevDiv Bugs 92166
11 namespace System.Data.SqlClient
13 using System;
14 using System.Collections;
15 using System.Collections.Concurrent;
16 using System.Collections.Generic;
17 using System.Collections.ObjectModel;
18 using System.Configuration.Assemblies;
19 using System.ComponentModel;
20 using System.Data;
21 using System.Data.Common;
22 using System.Data.ProviderBase;
23 using System.Data.Sql;
24 using System.Data.SqlTypes;
25 using System.Diagnostics;
26 using System.Globalization;
27 using System.IO;
28 using System.Linq;
29 using System.Runtime.CompilerServices;
30 using System.Runtime.ConstrainedExecution;
31 using System.Runtime.InteropServices;
32 using System.Runtime.Remoting;
33 using System.Runtime.Serialization.Formatters;
34 using System.Text;
35 using System.Threading;
36 using System.Threading.Tasks;
37 using System.Security;
38 using System.Security.Permissions;
39 using System.Reflection;
40 using System.Runtime.Versioning;
42 using Microsoft.SqlServer.Server;
43 using System.Security.Principal;
44 using System.Diagnostics.CodeAnalysis;
46 [DefaultEvent("InfoMessage")]
47 public sealed partial class SqlConnection: DbConnection, ICloneable {
49 static private readonly object EventInfoMessage = new object();
51 // System column encryption key store providers are added by default
52 static private readonly Dictionary<string, SqlColumnEncryptionKeyStoreProvider> _SystemColumnEncryptionKeyStoreProviders
53 = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
55 {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()},
56 {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()},
57 {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()}
60 /// <summary>
61 /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary.
62 /// Custom provider list can only supplied once per application.
63 /// </summary>
64 static private ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _CustomColumnEncryptionKeyStoreProviders;
66 // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders
67 static private readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object();
69 /// <summary>
70 /// Dictionary object holding trusted key paths for various SQL Servers.
71 /// Key to the dictionary is a SQL Server Name
72 /// IList contains a list of trusted key paths.
73 /// </summary>
74 static private readonly ConcurrentDictionary<string, IList<string>> _ColumnEncryptionTrustedMasterKeyPaths
75 = new ConcurrentDictionary<string, IList<string>>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
76 capacity: 1,
77 comparer: StringComparer.OrdinalIgnoreCase);
80 DefaultValue(null),
81 ResCategoryAttribute(Res.DataCategory_Data),
82 ResDescriptionAttribute(Res.TCE_SqlConnection_TrustedColumnMasterKeyPaths),
84 static public IDictionary<string, IList<string>> ColumnEncryptionTrustedMasterKeyPaths
86 get
88 return _ColumnEncryptionTrustedMasterKeyPaths;
92 /// <summary>
93 /// Defines whether query metadata caching is enabled.
94 /// </summary>
95 static private bool _ColumnEncryptionQueryMetadataCacheEnabled = true;
98 DefaultValue(null),
99 ResCategoryAttribute(Res.DataCategory_Data),
100 ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled),
102 static public bool ColumnEncryptionQueryMetadataCacheEnabled
106 return _ColumnEncryptionQueryMetadataCacheEnabled;
110 _ColumnEncryptionQueryMetadataCacheEnabled = value;
114 /// <summary>
115 /// Defines whether query metadata caching is enabled.
116 /// </summary>
117 static private TimeSpan _ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2);
120 DefaultValue(null),
121 ResCategoryAttribute(Res.DataCategory_Data),
122 ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl),
124 static public TimeSpan ColumnEncryptionKeyCacheTtl
128 return _ColumnEncryptionKeyCacheTtl;
132 _ColumnEncryptionKeyCacheTtl = value;
136 /// <summary>
137 /// This function should only be called once in an app. This does shallow copying of the dictionary so that
138 /// the app cannot alter the custom provider list once it has been set.
139 ///
140 /// Example:
141 ///
142 /// Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
143 /// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
144 /// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
145 /// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
146 /// </summary>
147 /// <param name="customProviders">Custom column encryption key provider dictionary</param>
148 static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders)
151 // Return when the provided dictionary is null.
152 if (customProviders == null)
154 throw SQL.NullCustomKeyStoreProviderDictionary();
157 // Validate that custom provider list doesn't contain any of system provider list
158 foreach (string key in customProviders.Keys)
160 // Validate the provider name
162 // Check for null or empty
163 if (string.IsNullOrWhiteSpace(key))
165 throw SQL.EmptyProviderName();
168 // Check if the name starts with MSSQL_, since this is reserved namespace for system providers.
169 if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase))
171 throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix);
174 // Validate the provider value
175 if (customProviders[key] == null)
177 throw SQL.NullProviderValue(key);
181 lock (_CustomColumnEncryptionKeyProvidersLock)
183 // Provider list can only be set once
184 if (_CustomColumnEncryptionKeyStoreProviders != null)
186 throw SQL.CanOnlyCallOnce();
189 // Create a temporary dictionary and then add items from the provided dictionary.
190 // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs
191 // in the provided customerProviders dictionary.
192 Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customColumnEncryptionKeyStoreProviders =
193 new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(customProviders, StringComparer.OrdinalIgnoreCase);
195 // Set the dictionary to the ReadOnly dictionary.
196 _CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider>(customColumnEncryptionKeyStoreProviders);
200 /// <summary>
201 /// This function walks through both system and custom column encryption key store providers and returns an object if found.
202 /// </summary>
203 /// <param name="providerName">Provider Name to be searched in System Provider diction and Custom provider dictionary.</param>
204 /// <param name="columnKeyStoreProvider">If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance.</param>
205 /// <returns>true if the provider is found, else returns false</returns>
206 static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) {
207 Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid");
209 // Initialize the out parameter
210 columnKeyStoreProvider = null;
212 // Search in the sytem provider list.
213 if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
215 return true;
218 lock (_CustomColumnEncryptionKeyProvidersLock)
220 // If custom provider is not set, then return false
221 if (_CustomColumnEncryptionKeyStoreProviders == null)
223 return false;
226 // Search in the custom provider list
227 return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
231 /// <summary>
232 /// This function returns a list of system provider dictionary currently supported by this driver.
233 /// </summary>
234 /// <returns>Combined list of provider names</returns>
235 static internal List<string> GetColumnEncryptionSystemKeyStoreProviders() {
236 HashSet<string> providerNames = new HashSet<string>(_SystemColumnEncryptionKeyStoreProviders.Keys);
237 return providerNames.ToList();
240 /// <summary>
241 /// This function returns a list of custom provider dictionary currently registered.
242 /// </summary>
243 /// <returns>Combined list of provider names</returns>
244 static internal List<string> GetColumnEncryptionCustomKeyStoreProviders() {
245 if(_CustomColumnEncryptionKeyStoreProviders != null)
247 HashSet<string> providerNames = new HashSet<string>(_CustomColumnEncryptionKeyStoreProviders.Keys);
248 return providerNames.ToList();
251 return new List<string>();
254 private SqlDebugContext _sdc; // SQL Debugging support
256 private bool _AsyncCommandInProgress;
258 // SQLStatistics support
259 internal SqlStatistics _statistics;
260 private bool _collectstats;
262 private bool _fireInfoMessageEventOnUserErrors; // False by default
264 // root task associated with current async invocation
265 Tuple<TaskCompletionSource<DbConnectionInternal>, Task> _currentCompletion;
267 private SqlCredential _credential; // SQL authentication password stored in SecureString
268 private string _connectionString;
269 private int _connectRetryCount;
271 private string _accessToken; // Access Token to be used for token based authententication
273 // connection resiliency
274 private object _reconnectLock = new object();
275 internal Task _currentReconnectionTask;
276 private Task _asyncWaitingForReconnection; // current async task waiting for reconnection in non-MARS connections
277 private Guid _originalConnectionId = Guid.Empty;
278 private CancellationTokenSource _reconnectionCancellationSource;
279 internal SessionData _recoverySessionData;
280 internal WindowsIdentity _lastIdentity;
281 internal WindowsIdentity _impersonateIdentity;
282 private int _reconnectCount;
284 // Transient Fault handling flag. This is needed to convey to the downstream mechanism of connection establishment, if Transient Fault handling should be used or not
285 // The downstream handling of Connection open is the same for idle connection resiliency. Currently we want to apply transient fault handling only to the connections opened
286 // using SqlConnection.Open() method.
287 internal bool _applyTransientFaultHandling = false;
289 public SqlConnection(string connectionString) : this(connectionString, null) {
292 public SqlConnection(string connectionString, SqlCredential credential) : this() {
293 ConnectionString = connectionString; // setting connection string first so that ConnectionOption is available
294 if (credential != null)
296 // The following checks are necessary as setting Credential property will call CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential
297 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential it will throw InvalidOperationException rather than Arguemtn exception
298 // Need to call setter on Credential property rather than setting _credential directly as pool groups need to be checked
299 SqlConnectionString connectionOptions = (SqlConnectionString) ConnectionOptions;
300 if (UsesClearUserIdOrPassword(connectionOptions))
302 throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
305 if (UsesIntegratedSecurity(connectionOptions))
307 throw ADP.InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity();
310 if (UsesContextConnection(connectionOptions))
312 throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
315 if (UsesActiveDirectoryIntegrated(connectionOptions))
317 throw SQL.SettingCredentialWithIntegratedArgument();
320 Credential = credential;
322 // else
323 // credential == null: we should not set "Credential" as this will do additional validation check and
324 // checking pool groups which is not necessary. All necessary operation is already done by calling "ConnectionString = connectionString"
325 CacheConnectionStringProperties();
328 private SqlConnection(SqlConnection connection) { // Clone
329 GC.SuppressFinalize(this);
330 CopyFrom(connection);
331 _connectionString = connection._connectionString;
332 if (connection._credential != null)
334 SecureString password = connection._credential.Password.Copy();
335 password.MakeReadOnly();
336 _credential = new SqlCredential(connection._credential.UserId, password);
338 _accessToken = connection._accessToken;
339 CacheConnectionStringProperties();
342 // This method will be called once connection string is set or changed.
343 private void CacheConnectionStringProperties() {
344 SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
345 if (connString != null) {
346 _connectRetryCount = connString.ConnectRetryCount;
351 // PUBLIC PROPERTIES
354 // used to start/stop collection of statistics data and do verify the current state
356 // devnote: start/stop should not performed using a property since it requires execution of code
358 // start statistics
359 // set the internal flag (_statisticsEnabled) to true.
360 // Create a new SqlStatistics object if not already there.
361 // connect the parser to the object.
362 // if there is no parser at this time we need to connect it after creation.
366 DefaultValue(false),
367 ResCategoryAttribute(Res.DataCategory_Data),
368 ResDescriptionAttribute(Res.SqlConnection_StatisticsEnabled),
370 public bool StatisticsEnabled {
371 get {
372 return (_collectstats);
374 set {
375 if (IsContextConnection) {
376 if (value) {
377 throw SQL.NotAvailableOnContextConnection();
380 else {
381 if (value) {
382 // start
383 if (ConnectionState.Open == State) {
384 if (null == _statistics) {
385 _statistics = new SqlStatistics();
386 ADP.TimerCurrent(out _statistics._openTimestamp);
388 // set statistics on the parser
389 // update timestamp;
390 Debug.Assert(Parser != null, "Where's the parser?");
391 Parser.Statistics = _statistics;
394 else {
395 // stop
396 if (null != _statistics) {
397 if (ConnectionState.Open == State) {
398 // remove statistics from parser
399 // update timestamp;
400 TdsParser parser = Parser;
401 Debug.Assert(parser != null, "Where's the parser?");
402 parser.Statistics = null;
403 ADP.TimerCurrent(out _statistics._closeTimestamp);
407 this._collectstats = value;
412 internal bool AsyncCommandInProgress {
413 get {
414 return (_AsyncCommandInProgress);
416 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
417 set {
418 _AsyncCommandInProgress = value;
422 internal bool IsContextConnection {
423 get {
424 SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
425 return UsesContextConnection(opt);
429 /// <summary>
430 /// Is this connection using column encryption ?
431 /// </summary>
432 internal bool IsColumnEncryptionSettingEnabled {
433 get {
434 SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
435 return opt != null ? opt.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled : false;
439 // Is this connection is a Context Connection?
440 private bool UsesContextConnection(SqlConnectionString opt)
442 return opt != null ? opt.ContextConnection : false;
445 private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt)
447 return opt != null ? opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated : false;
450 private bool UsesAuthentication(SqlConnectionString opt) {
451 return opt != null ? opt.Authentication != SqlAuthenticationMethod.NotSpecified : false;
454 // Does this connection uses Integrated Security?
455 private bool UsesIntegratedSecurity(SqlConnectionString opt) {
456 return opt != null ? opt.IntegratedSecurity : false;
459 // Does this connection uses old style of clear userID or Password in connection string?
460 private bool UsesClearUserIdOrPassword(SqlConnectionString opt) {
461 bool result = false;
462 if (null != opt) {
463 result = (!ADP.IsEmpty(opt.UserID) || !ADP.IsEmpty(opt.Password));
465 return result;
468 internal SqlConnectionString.TransactionBindingEnum TransactionBinding {
469 get {
470 return ((SqlConnectionString)ConnectionOptions).TransactionBinding;
474 internal SqlConnectionString.TypeSystem TypeSystem {
475 get {
476 return ((SqlConnectionString)ConnectionOptions).TypeSystemVersion;
480 internal Version TypeSystemAssemblyVersion {
481 get {
482 return ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion;
486 internal PoolBlockingPeriod PoolBlockingPeriod
490 return ((SqlConnectionString)ConnectionOptions).PoolBlockingPeriod;
494 internal int ConnectRetryInterval {
495 get {
496 return ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval;
500 override protected DbProviderFactory DbProviderFactory {
501 get {
502 return SqlClientFactory.Instance;
506 // AccessToken: To be used for token based authentication
508 Browsable(false),
509 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
510 ResDescriptionAttribute(Res.SqlConnection_AccessToken),
512 public string AccessToken {
513 get {
514 string result = _accessToken;
515 // When a connection is connecting or is ever opened, make AccessToken available only if "Persist Security Info" is set to true
516 // otherwise, return null
517 SqlConnectionString connectionOptions = (SqlConnectionString)UserConnectionOptions;
518 if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo) {
519 result = null;
522 return result;
524 set {
525 // If a connection is connecting or is ever opened, AccessToken cannot be set
526 if (!InnerConnection.AllowSetConnectionString) {
527 throw ADP.OpenConnectionPropertySet("AccessToken", InnerConnection.State);
530 if (value != null) {
531 // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential
532 CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions);
535 _accessToken = value;
536 // Need to call ConnectionString_Set to do proper pool group check
537 ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: _accessToken));
542 DefaultValue(""),
543 #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
544 RecommendedAsConfigurable(true),
545 #pragma warning restore 618
546 SettingsBindableAttribute(true),
547 RefreshProperties(RefreshProperties.All),
548 ResCategoryAttribute(Res.DataCategory_Data),
549 Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
550 ResDescriptionAttribute(Res.SqlConnection_ConnectionString),
552 override public string ConnectionString {
553 get {
554 return ConnectionString_Get();
556 set {
557 if(_credential != null || _accessToken != null) {
558 SqlConnectionString connectionOptions = new SqlConnectionString(value);
559 if(_credential != null) {
560 // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
561 // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
562 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
563 if(UsesActiveDirectoryIntegrated(connectionOptions)) {
564 throw SQL.SettingIntegratedWithCredential();
567 CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
569 else if(_accessToken != null) {
570 CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions);
573 ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken));
574 _connectionString = value; // Change _connectionString value only after value is validated
575 CacheConnectionStringProperties();
580 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
581 ResDescriptionAttribute(Res.SqlConnection_ConnectionTimeout),
583 override public int ConnectionTimeout {
584 get {
585 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
586 return ((null != constr) ? constr.ConnectTimeout : SqlConnectionString.DEFAULT.Connect_Timeout);
591 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
592 ResDescriptionAttribute(Res.SqlConnection_Database),
594 override public string Database {
595 // if the connection is open, we need to ask the inner connection what it's
596 // current catalog is because it may have gotten changed, otherwise we can
597 // just return what the connection string had.
598 get {
599 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
600 string result;
602 if (null != innerConnection) {
603 result = innerConnection.CurrentDatabase;
605 else {
606 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
607 result = ((null != constr) ? constr.InitialCatalog : SqlConnectionString.DEFAULT.Initial_Catalog);
609 return result;
614 Browsable(true),
615 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
616 ResDescriptionAttribute(Res.SqlConnection_DataSource),
618 override public string DataSource {
619 get {
620 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
621 string result;
623 if (null != innerConnection) {
624 result = innerConnection.CurrentDataSource;
626 else {
627 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
628 result = ((null != constr) ? constr.DataSource : SqlConnectionString.DEFAULT.Data_Source);
630 return result;
635 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
636 ResCategoryAttribute(Res.DataCategory_Data),
637 ResDescriptionAttribute(Res.SqlConnection_PacketSize),
639 public int PacketSize {
640 // if the connection is open, we need to ask the inner connection what it's
641 // current packet size is because it may have gotten changed, otherwise we
642 // can just return what the connection string had.
643 get {
644 if (IsContextConnection) {
645 throw SQL.NotAvailableOnContextConnection();
648 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
649 int result;
651 if (null != innerConnection) {
652 result = innerConnection.PacketSize;
654 else {
655 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
656 result = ((null != constr) ? constr.PacketSize : SqlConnectionString.DEFAULT.Packet_Size);
658 return result;
663 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
664 ResCategoryAttribute(Res.DataCategory_Data),
665 ResDescriptionAttribute(Res.SqlConnection_ClientConnectionId),
667 public Guid ClientConnectionId {
668 get {
670 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
672 if (null != innerConnection) {
673 return innerConnection.ClientConnectionId;
675 else {
676 Task reconnectTask = _currentReconnectionTask;
677 if (reconnectTask != null && !reconnectTask.IsCompleted) {
678 return _originalConnectionId;
680 return Guid.Empty;
686 Browsable(false),
687 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
688 ResDescriptionAttribute(Res.SqlConnection_ServerVersion),
690 override public string ServerVersion {
691 get {
692 return GetOpenConnection().ServerVersion;
697 Browsable(false),
698 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
699 ResDescriptionAttribute(Res.DbConnection_State),
701 override public ConnectionState State {
702 get {
703 Task reconnectTask=_currentReconnectionTask;
704 if (reconnectTask != null && !reconnectTask.IsCompleted) {
705 return ConnectionState.Open;
707 return InnerConnection.State;
712 internal SqlStatistics Statistics {
713 get {
714 return _statistics;
719 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
720 ResCategoryAttribute(Res.DataCategory_Data),
721 ResDescriptionAttribute(Res.SqlConnection_WorkstationId),
723 public string WorkstationId {
724 get {
725 if (IsContextConnection) {
726 throw SQL.NotAvailableOnContextConnection();
729 // If not supplied by the user, the default value is the MachineName
730 // Note: In Longhorn you'll be able to rename a machine without
731 // rebooting. Therefore, don't cache this machine name.
732 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
733 string result = ((null != constr) ? constr.WorkstationId : null);
734 if (null == result) {
735 // getting machine name requires Environment.Permission
736 // user must have that permission in order to retrieve this
737 result = Environment.MachineName;
739 return result;
743 // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication
745 Browsable(false),
746 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
747 ResDescriptionAttribute(Res.SqlConnection_Credential),
749 public SqlCredential Credential
753 SqlCredential result = _credential;
755 // When a connection is connecting or is ever opened, make credential available only if "Persist Security Info" is set to true
756 // otherwise, return null
757 SqlConnectionString connectionOptions = (SqlConnectionString) UserConnectionOptions;
758 if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo)
760 result = null;
763 return result;
768 // If a connection is connecting or is ever opened, user id/password cannot be set
769 if (!InnerConnection.AllowSetConnectionString)
771 throw ADP.OpenConnectionPropertySet("Credential", InnerConnection.State);
774 // check if the usage of credential has any conflict with the keys used in connection string
775 if (value != null)
777 // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
778 // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
779 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
780 if (UsesActiveDirectoryIntegrated((SqlConnectionString) ConnectionOptions)) {
781 throw SQL.SettingCredentialWithIntegratedInvalid();
784 CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential((SqlConnectionString) ConnectionOptions);
785 if(_accessToken != null) {
786 throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
791 _credential = value;
793 // Need to call ConnectionString_Set to do proper pool group check
794 ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken));
798 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential: check if the usage of credential has any conflict
799 // with the keys used in connection string
800 // If there is any conflict, it throws InvalidOperationException
801 // This is to be used setter of ConnectionString and Credential properties
802 private void CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(SqlConnectionString connectionOptions)
804 if (UsesClearUserIdOrPassword(connectionOptions))
806 throw ADP.InvalidMixedUsageOfSecureAndClearCredential();
809 if (UsesIntegratedSecurity(connectionOptions))
811 throw ADP.InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity();
814 if (UsesContextConnection(connectionOptions))
816 throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
820 // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken: check if the usage of AccessToken has any conflict
821 // with the keys used in connection string and credential
822 // If there is any conflict, it throws InvalidOperationException
823 // This is to be used setter of ConnectionString and AccessToken properties
824 private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(SqlConnectionString connectionOptions) {
825 if (UsesClearUserIdOrPassword(connectionOptions)) {
826 throw ADP.InvalidMixedUsageOfAccessTokenAndUserIDPassword();
829 if (UsesIntegratedSecurity(connectionOptions)) {
830 throw ADP.InvalidMixedUsageOfAccessTokenAndIntegratedSecurity();
833 if (UsesContextConnection(connectionOptions)) {
834 throw ADP.InvalidMixedUsageOfAccessTokenAndContextConnection();
837 if (UsesAuthentication(connectionOptions)) {
838 throw ADP.InvalidMixedUsageOfAccessTokenAndAuthentication();
841 // Check if the usage of AccessToken has the conflict with credential
842 if (_credential != null) {
843 throw ADP.InvalidMixedUsageOfAccessTokenAndCredential();
848 // PUBLIC EVENTS
852 ResCategoryAttribute(Res.DataCategory_InfoMessage),
853 ResDescriptionAttribute(Res.DbConnection_InfoMessage),
855 public event SqlInfoMessageEventHandler InfoMessage {
856 add {
857 Events.AddHandler(EventInfoMessage, value);
859 remove {
860 Events.RemoveHandler(EventInfoMessage, value);
864 public bool FireInfoMessageEventOnUserErrors {
865 get {
866 return _fireInfoMessageEventOnUserErrors;
868 set {
869 _fireInfoMessageEventOnUserErrors = value;
873 // Approx. number of times that the internal connection has been reconnected
874 internal int ReconnectCount {
875 get {
876 return _reconnectCount;
881 // PUBLIC METHODS
884 new public SqlTransaction BeginTransaction() {
885 // this is just a delegate. The actual method tracks executiontime
886 return BeginTransaction(IsolationLevel.Unspecified, null);
889 new public SqlTransaction BeginTransaction(IsolationLevel iso) {
890 // this is just a delegate. The actual method tracks executiontime
891 return BeginTransaction(iso, null);
894 public SqlTransaction BeginTransaction(string transactionName) {
895 // Use transaction names only on the outermost pair of nested
896 // BEGIN...COMMIT or BEGIN...ROLLBACK statements. Transaction names
897 // are ignored for nested BEGIN's. The only way to rollback a nested
898 // transaction is to have a save point from a SAVE TRANSACTION call.
899 return BeginTransaction(IsolationLevel.Unspecified, transactionName);
902 // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
903 [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
904 override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
905 IntPtr hscp;
907 Bid.ScopeEnter(out hscp, "<prov.SqlConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
908 try {
910 DbTransaction transaction = BeginTransaction(isolationLevel);
912 // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and
913 // subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction
914 // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
915 // until the completion of BeginTransaction with KeepAlive
916 GC.KeepAlive(this);
918 return transaction;
920 finally {
921 Bid.ScopeLeave(ref hscp);
925 public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) {
926 WaitForPendingReconnection();
927 SqlStatistics statistics = null;
928 IntPtr hscp;
929 string xactName = ADP.IsEmpty(transactionName)? "None" : transactionName;
930 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", ObjectID, (int)iso,
931 xactName);
933 try {
934 statistics = SqlStatistics.StartTimer(Statistics);
936 // NOTE: we used to throw an exception if the transaction name was empty
937 // (see MDAC 50292) but that was incorrect because we have a BeginTransaction
938 // method that doesn't have a transactionName argument.
939 SqlTransaction transaction;
940 bool isFirstAttempt = true;
941 do {
942 transaction = GetOpenConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice
943 Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt");
944 isFirstAttempt = false;
945 } while (transaction.InternalTransaction.ConnectionHasBeenRestored);
948 // SQLBU 503873 The GetOpenConnection line above doesn't keep a ref on the outer connection (this),
949 // and it could be collected before the inner connection can hook it to the transaction, resulting in
950 // a transaction with a null connection property. Use GC.KeepAlive to ensure this doesn't happen.
951 GC.KeepAlive(this);
953 return transaction;
955 finally {
956 Bid.ScopeLeave(ref hscp);
957 SqlStatistics.StopTimer(statistics);
961 override public void ChangeDatabase(string database) {
962 SqlStatistics statistics = null;
963 RepairInnerConnection();
964 Bid.CorrelationTrace("<sc.SqlConnection.ChangeDatabase|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
965 TdsParser bestEffortCleanupTarget = null;
966 RuntimeHelpers.PrepareConstrainedRegions();
967 try {
968 #if DEBUG
969 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
971 RuntimeHelpers.PrepareConstrainedRegions();
972 try {
973 tdsReliabilitySection.Start();
974 #else
976 #endif //DEBUG
977 bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
978 statistics = SqlStatistics.StartTimer(Statistics);
979 InnerConnection.ChangeDatabase(database);
981 #if DEBUG
982 finally {
983 tdsReliabilitySection.Stop();
985 #endif //DEBUG
987 catch (System.OutOfMemoryException e) {
988 Abort(e);
989 throw;
991 catch (System.StackOverflowException e) {
992 Abort(e);
993 throw;
995 catch (System.Threading.ThreadAbortException e) {
996 Abort(e);
997 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
998 throw;
1000 finally {
1001 SqlStatistics.StopTimer(statistics);
1005 static public void ClearAllPools() {
1006 (new SqlClientPermission(PermissionState.Unrestricted)).Demand();
1007 SqlConnectionFactory.SingletonInstance.ClearAllPools();
1010 static public void ClearPool(SqlConnection connection) {
1011 ADP.CheckArgumentNull(connection, "connection");
1013 DbConnectionOptions connectionOptions = connection.UserConnectionOptions;
1014 if (null != connectionOptions) {
1015 connectionOptions.DemandPermission();
1016 if (connection.IsContextConnection) {
1017 throw SQL.NotAvailableOnContextConnection();
1019 SqlConnectionFactory.SingletonInstance.ClearPool(connection);
1023 object ICloneable.Clone() {
1024 SqlConnection clone = new SqlConnection(this);
1025 Bid.Trace("<sc.SqlConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
1026 return clone;
1029 void CloseInnerConnection() {
1030 // CloseConnection() now handles the lock
1032 // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and
1033 // the command will no longer be cancelable. It might be desirable to be able to cancel the close opperation, but this is
1034 // outside of the scope of Whidbey RTM. See (SqlCommand::Cancel) for other lock.
1035 InnerConnection.CloseConnection(this, ConnectionFactory);
1038 override public void Close() {
1039 IntPtr hscp;
1040 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
1041 Bid.CorrelationTrace("<sc.SqlConnection.Close|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1042 try {
1043 SqlStatistics statistics = null;
1045 TdsParser bestEffortCleanupTarget = null;
1046 RuntimeHelpers.PrepareConstrainedRegions();
1047 try {
1048 #if DEBUG
1049 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1051 RuntimeHelpers.PrepareConstrainedRegions();
1052 try {
1053 tdsReliabilitySection.Start();
1054 #else
1056 #endif //DEBUG
1057 bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
1058 statistics = SqlStatistics.StartTimer(Statistics);
1060 Task reconnectTask = _currentReconnectionTask;
1061 if (reconnectTask != null && !reconnectTask.IsCompleted) {
1062 CancellationTokenSource cts = _reconnectionCancellationSource;
1063 if (cts != null) {
1064 cts.Cancel();
1066 AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection
1067 if (State != ConnectionState.Open) {// if we cancelled before the connection was opened
1068 OnStateChange(DbConnectionInternal.StateChangeClosed);
1071 CancelOpenAndWait();
1072 CloseInnerConnection();
1073 GC.SuppressFinalize(this);
1075 if (null != Statistics) {
1076 ADP.TimerCurrent(out _statistics._closeTimestamp);
1079 #if DEBUG
1080 finally {
1081 tdsReliabilitySection.Stop();
1083 #endif //DEBUG
1085 catch (System.OutOfMemoryException e) {
1086 Abort(e);
1087 throw;
1089 catch (System.StackOverflowException e) {
1090 Abort(e);
1091 throw;
1093 catch (System.Threading.ThreadAbortException e) {
1094 Abort(e);
1095 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1096 throw;
1098 finally {
1099 SqlStatistics.StopTimer(statistics);
1100 //dispose windows identity once connection is closed.
1101 if (_lastIdentity != null) {
1102 _lastIdentity.Dispose();
1106 finally {
1107 SqlDebugContext sdc = _sdc;
1108 _sdc = null;
1109 Bid.ScopeLeave(ref hscp);
1110 if (sdc != null) {
1111 sdc.Dispose();
1116 new public SqlCommand CreateCommand() {
1117 return new SqlCommand(null, this);
1120 private void DisposeMe(bool disposing) { // MDAC 65459
1121 // clear credential and AccessToken here rather than in IDisposable.Dispose as these are specific to SqlConnection only
1122 // IDisposable.Dispose is generated code from a template and used by other providers as well
1123 _credential = null;
1124 _accessToken = null;
1126 if (!disposing) {
1127 // DevDiv2 Bug 457934:SQLConnection leaks when not disposed
1128 // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/457934
1129 // For non-pooled connections we need to make sure that if the SqlConnection was not closed, then we release the GCHandle on the stateObject to allow it to be GCed
1130 // For pooled connections, we will rely on the pool reclaiming the connection
1131 var innerConnection = (InnerConnection as SqlInternalConnectionTds);
1132 if ((innerConnection != null) && (!innerConnection.ConnectionOptions.Pooling)) {
1133 var parser = innerConnection.Parser;
1134 if ((parser != null) && (parser._physicalStateObj != null)) {
1135 parser._physicalStateObj.DecrementPendingCallbacks(release: false);
1141 #if !MOBILE
1142 public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) {
1143 if (IsContextConnection) {
1144 throw SQL.NotAvailableOnContextConnection();
1147 EnlistDistributedTransactionHelper(transaction);
1149 #endif
1151 override public void Open() {
1152 IntPtr hscp;
1153 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Open|API> %d#", ObjectID) ;
1154 Bid.CorrelationTrace("<sc.SqlConnection.Open|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1156 try {
1157 if (StatisticsEnabled) {
1158 if (null == _statistics) {
1159 _statistics = new SqlStatistics();
1161 else {
1162 _statistics.ContinueOnNewConnection();
1166 SqlStatistics statistics = null;
1167 RuntimeHelpers.PrepareConstrainedRegions();
1168 try {
1169 statistics = SqlStatistics.StartTimer(Statistics);
1171 if (!TryOpen(null)) {
1172 throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
1175 finally {
1176 SqlStatistics.StopTimer(statistics);
1179 finally {
1180 Bid.ScopeLeave(ref hscp);
1184 internal void RegisterWaitingForReconnect(Task waitingTask) {
1185 if (((SqlConnectionString)ConnectionOptions).MARS) {
1186 return;
1188 Interlocked.CompareExchange(ref _asyncWaitingForReconnection, waitingTask, null);
1189 if (_asyncWaitingForReconnection != waitingTask) { // somebody else managed to register
1190 throw SQL.MARSUnspportedOnConnection();
1194 private async Task ReconnectAsync(int timeout) {
1195 try {
1196 long commandTimeoutExpiration = 0;
1197 if (timeout > 0) {
1198 commandTimeoutExpiration = ADP.TimerCurrent() + ADP.TimerFromSeconds(timeout);
1200 CancellationTokenSource cts = new CancellationTokenSource();
1201 _reconnectionCancellationSource = cts;
1202 CancellationToken ctoken = cts.Token;
1203 int retryCount = _connectRetryCount; // take a snapshot: could be changed by modifying the connection string
1204 for (int attempt = 0; attempt < retryCount; attempt++) {
1205 if (ctoken.IsCancellationRequested) {
1206 Bid.Trace("<sc.SqlConnection.ReconnectAsync|INFO> Orginal ClientConnectionID %ls - reconnection cancelled\n", _originalConnectionId.ToString());
1207 return;
1209 try {
1210 _impersonateIdentity = _lastIdentity;
1211 try {
1212 ForceNewConnection = true;
1213 await OpenAsync(ctoken).ConfigureAwait(false);
1214 // On success, increment the reconnect count - we don't really care if it rolls over since it is approx.
1215 _reconnectCount = unchecked(_reconnectCount + 1);
1216 #if DEBUG
1217 Debug.Assert(_recoverySessionData._debugReconnectDataApplied, "Reconnect data was not applied !");
1218 #endif
1220 finally {
1221 _impersonateIdentity = null;
1222 ForceNewConnection = false;
1224 Bid.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Reconnection suceeded. ClientConnectionID %ls -> %ls \n", _originalConnectionId.ToString(), ClientConnectionId.ToString());
1225 return;
1227 catch (SqlException e) {
1228 Bid.Trace("<sc.SqlConnection.ReconnectAsyncINFO> Orginal ClientConnectionID %ls - reconnection attempt failed error %ls\n", _originalConnectionId.ToString(), e.Message);
1229 if (attempt == retryCount - 1) {
1230 Bid.Trace("<sc.SqlConnection.ReconnectAsync|INFO> Orginal ClientConnectionID %ls - give up reconnection\n", _originalConnectionId.ToString());
1231 throw SQL.CR_AllAttemptsFailed(e, _originalConnectionId);
1233 if (timeout > 0 && ADP.TimerRemaining(commandTimeoutExpiration) < ADP.TimerFromSeconds(ConnectRetryInterval)) {
1234 throw SQL.CR_NextAttemptWillExceedQueryTimeout(e, _originalConnectionId);
1237 await Task.Delay(1000 * ConnectRetryInterval, ctoken).ConfigureAwait(false);
1240 finally {
1241 _recoverySessionData = null;
1242 _supressStateChangeForReconnection = false;
1244 Debug.Assert(false, "Should not reach this point");
1247 internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) {
1248 Task runningReconnect = _currentReconnectionTask;
1249 // This loop in the end will return not completed reconnect task or null
1250 while (runningReconnect != null && runningReconnect.IsCompleted) {
1251 // clean current reconnect task (if it is the same one we checked
1252 Interlocked.CompareExchange<Task>(ref _currentReconnectionTask, null, runningReconnect);
1253 // make sure nobody started new task (if which case we did not clean it)
1254 runningReconnect = _currentReconnectionTask;
1256 if (runningReconnect == null) {
1257 if (_connectRetryCount > 0) {
1258 SqlInternalConnectionTds tdsConn = GetOpenTdsConnection();
1259 if (tdsConn._sessionRecoveryAcknowledged) {
1260 TdsParserStateObject stateObj = tdsConn.Parser._physicalStateObj;
1261 if (!stateObj.ValidateSNIConnection()) {
1262 if (tdsConn.Parser._sessionPool != null) {
1263 if (tdsConn.Parser._sessionPool.ActiveSessionsCount > 0) {
1264 // >1 MARS session
1265 if (beforeDisconnect != null) {
1266 beforeDisconnect();
1268 OnError(SQL.CR_UnrecoverableClient(ClientConnectionId), true, null);
1271 SessionData cData = tdsConn.CurrentSessionData;
1272 cData.AssertUnrecoverableStateCountIsCorrect();
1273 if (cData._unrecoverableStatesCount == 0) {
1274 bool callDisconnect = false;
1275 lock (_reconnectLock) {
1276 tdsConn.CheckEnlistedTransactionBinding();
1277 runningReconnect = _currentReconnectionTask; // double check after obtaining the lock
1278 if (runningReconnect == null) {
1279 if (cData._unrecoverableStatesCount == 0) { // could change since the first check, but now is stable since connection is know to be broken
1280 _originalConnectionId = ClientConnectionId;
1281 Bid.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Connection ClientConnectionID %ls is invalid, reconnecting\n", _originalConnectionId.ToString());
1282 _recoverySessionData = cData;
1283 if (beforeDisconnect != null) {
1284 beforeDisconnect();
1286 try {
1287 _supressStateChangeForReconnection = true;
1288 tdsConn.DoomThisConnection();
1290 catch (SqlException) {
1292 runningReconnect = Task.Run(() => ReconnectAsync(timeout));
1293 // if current reconnect is not null, somebody already started reconnection task - some kind of race condition
1294 Debug.Assert(_currentReconnectionTask == null, "Duplicate reconnection tasks detected");
1295 _currentReconnectionTask = runningReconnect;
1298 else {
1299 callDisconnect = true;
1302 if (callDisconnect && beforeDisconnect != null) {
1303 beforeDisconnect();
1306 else {
1307 if (beforeDisconnect != null) {
1308 beforeDisconnect();
1310 OnError(SQL.CR_UnrecoverableServer(ClientConnectionId), true, null);
1312 } // ValidateSNIConnection
1313 } // sessionRecoverySupported
1314 } // connectRetryCount>0
1316 else { // runningReconnect = null
1317 if (beforeDisconnect != null) {
1318 beforeDisconnect();
1321 return runningReconnect;
1324 // this is straightforward, but expensive method to do connection resiliency - it take locks and all prepartions as for TDS request
1325 partial void RepairInnerConnection() {
1326 WaitForPendingReconnection();
1327 if (_connectRetryCount == 0) {
1328 return;
1330 SqlInternalConnectionTds tdsConn = InnerConnection as SqlInternalConnectionTds;
1331 if (tdsConn != null) {
1332 tdsConn.ValidateConnectionForExecute(null);
1333 tdsConn.GetSessionAndReconnectIfNeeded((SqlConnection)this);
1337 private void WaitForPendingReconnection() {
1338 Task reconnectTask = _currentReconnectionTask;
1339 if (reconnectTask != null && !reconnectTask.IsCompleted) {
1340 AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false);
1344 void CancelOpenAndWait()
1346 // copy from member to avoid changes by background thread
1347 var completion = _currentCompletion;
1348 if (completion != null)
1350 completion.Item1.TrySetCanceled();
1351 ((IAsyncResult)completion.Item2).AsyncWaitHandle.WaitOne();
1353 Debug.Assert(_currentCompletion == null, "After waiting for an async call to complete, there should be no completion source");
1356 public override Task OpenAsync(CancellationToken cancellationToken) {
1357 IntPtr hscp;
1358 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.OpenAsync|API> %d#", ObjectID) ;
1359 Bid.CorrelationTrace("<sc.SqlConnection.OpenAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1360 try {
1362 if (StatisticsEnabled) {
1363 if (null == _statistics) {
1364 _statistics = new SqlStatistics();
1366 else {
1367 _statistics.ContinueOnNewConnection();
1371 SqlStatistics statistics = null;
1372 RuntimeHelpers.PrepareConstrainedRegions();
1373 try {
1374 statistics = SqlStatistics.StartTimer(Statistics);
1376 System.Transactions.Transaction transaction = ADP.GetCurrentTransaction();
1377 TaskCompletionSource<DbConnectionInternal> completion = new TaskCompletionSource<DbConnectionInternal>(transaction);
1378 TaskCompletionSource<object> result = new TaskCompletionSource<object>();
1380 if (cancellationToken.IsCancellationRequested) {
1381 result.SetCanceled();
1382 return result.Task;
1385 if (IsContextConnection) {
1386 // Async not supported on Context Connections
1387 result.SetException(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
1388 return result.Task;
1391 bool completed;
1393 try {
1394 completed = TryOpen(completion);
1396 catch (Exception e) {
1397 result.SetException(e);
1398 return result.Task;
1401 if (completed) {
1402 result.SetResult(null);
1404 else {
1405 CancellationTokenRegistration registration = new CancellationTokenRegistration();
1406 if (cancellationToken.CanBeCanceled) {
1407 registration = cancellationToken.Register(() => completion.TrySetCanceled());
1409 OpenAsyncRetry retry = new OpenAsyncRetry(this, completion, result, registration);
1410 _currentCompletion = new Tuple<TaskCompletionSource<DbConnectionInternal>, Task>(completion, result.Task);
1411 completion.Task.ContinueWith(retry.Retry, TaskScheduler.Default);
1412 return result.Task;
1415 return result.Task;
1417 finally {
1418 SqlStatistics.StopTimer(statistics);
1421 finally {
1422 Bid.ScopeLeave(ref hscp);
1426 private class OpenAsyncRetry {
1427 SqlConnection _parent;
1428 TaskCompletionSource<DbConnectionInternal> _retry;
1429 TaskCompletionSource<object> _result;
1430 CancellationTokenRegistration _registration;
1432 public OpenAsyncRetry(SqlConnection parent, TaskCompletionSource<DbConnectionInternal> retry, TaskCompletionSource<object> result, CancellationTokenRegistration registration) {
1433 _parent = parent;
1434 _retry = retry;
1435 _result = result;
1436 _registration = registration;
1439 internal void Retry(Task<DbConnectionInternal> retryTask) {
1440 Bid.Trace("<sc.SqlConnection.OpenAsyncRetry|Info> %d#\n", _parent.ObjectID);
1441 _registration.Dispose();
1442 try {
1443 SqlStatistics statistics = null;
1444 RuntimeHelpers.PrepareConstrainedRegions();
1445 try {
1446 statistics = SqlStatistics.StartTimer(_parent.Statistics);
1448 if (retryTask.IsFaulted) {
1449 Exception e = retryTask.Exception.InnerException;
1450 _parent.CloseInnerConnection();
1451 _parent._currentCompletion = null;
1452 _result.SetException(retryTask.Exception.InnerException);
1454 else if (retryTask.IsCanceled) {
1455 _parent.CloseInnerConnection();
1456 _parent._currentCompletion = null;
1457 _result.SetCanceled();
1459 else {
1460 bool result;
1461 // protect continuation from ----s with close and cancel
1462 lock (_parent.InnerConnection) {
1463 result = _parent.TryOpen(_retry);
1465 if (result)
1467 _parent._currentCompletion = null;
1468 _result.SetResult(null);
1470 else {
1471 _parent.CloseInnerConnection();
1472 _parent._currentCompletion = null;
1473 _result.SetException(ADP.ExceptionWithStackTrace(ADP.InternalError(ADP.InternalErrorCode.CompletedConnectReturnedPending)));
1477 finally {
1478 SqlStatistics.StopTimer(statistics);
1481 catch (Exception e) {
1482 _parent.CloseInnerConnection();
1483 _parent._currentCompletion = null;
1484 _result.SetException(e);
1489 private bool TryOpen(TaskCompletionSource<DbConnectionInternal> retry) {
1490 SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions;
1492 _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0 );
1494 if (connectionOptions != null &&
1495 (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword) &&
1496 (!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) &&
1497 _credential == null) {
1498 throw SQL.CredentialsNotProvided(connectionOptions.Authentication);
1501 if (_impersonateIdentity != null) {
1502 using (WindowsIdentity identity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity()) {
1503 if (_impersonateIdentity.User == identity.User) {
1504 return TryOpenInner(retry);
1506 else {
1507 using (WindowsImpersonationContext context = _impersonateIdentity.Impersonate()) {
1508 return TryOpenInner(retry);
1513 else {
1514 if (this.UsesIntegratedSecurity(connectionOptions) || this.UsesActiveDirectoryIntegrated(connectionOptions)) {
1515 _lastIdentity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity();
1517 else {
1518 _lastIdentity = null;
1520 return TryOpenInner(retry);
1524 private bool TryOpenInner(TaskCompletionSource<DbConnectionInternal> retry) {
1525 TdsParser bestEffortCleanupTarget = null;
1526 RuntimeHelpers.PrepareConstrainedRegions();
1527 try {
1528 #if DEBUG
1529 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1531 RuntimeHelpers.PrepareConstrainedRegions();
1532 try {
1533 tdsReliabilitySection.Start();
1534 #else
1536 #endif //DEBUG
1537 if (ForceNewConnection) {
1538 if (!InnerConnection.TryReplaceConnection(this, ConnectionFactory, retry, UserConnectionOptions)) {
1539 return false;
1542 else {
1543 if (!InnerConnection.TryOpenConnection(this, ConnectionFactory, retry, UserConnectionOptions)) {
1544 return false;
1547 // does not require GC.KeepAlive(this) because of OnStateChange
1549 // GetBestEffortCleanup must happen AFTER OpenConnection to get the correct target.
1550 bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
1552 var tdsInnerConnection = (InnerConnection as SqlInternalConnectionTds);
1553 if (tdsInnerConnection == null) {
1554 SqlInternalConnectionSmi innerConnection = (InnerConnection as SqlInternalConnectionSmi);
1555 innerConnection.AutomaticEnlistment();
1557 else {
1558 Debug.Assert(tdsInnerConnection.Parser != null, "Where's the parser?");
1560 if (!tdsInnerConnection.ConnectionOptions.Pooling) {
1561 // For non-pooled connections, we need to make sure that the finalizer does actually run to avoid leaking SNI handles
1562 GC.ReRegisterForFinalize(this);
1565 if (StatisticsEnabled) {
1566 ADP.TimerCurrent(out _statistics._openTimestamp);
1567 tdsInnerConnection.Parser.Statistics = _statistics;
1569 else {
1570 tdsInnerConnection.Parser.Statistics = null;
1571 _statistics = null; // in case of previous Open/Close/reset_CollectStats sequence
1573 CompleteOpen();
1576 #if DEBUG
1577 finally {
1578 tdsReliabilitySection.Stop();
1580 #endif //DEBUG
1582 catch (System.OutOfMemoryException e) {
1583 Abort(e);
1584 throw;
1586 catch (System.StackOverflowException e) {
1587 Abort(e);
1588 throw;
1590 catch (System.Threading.ThreadAbortException e) {
1591 Abort(e);
1592 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1593 throw;
1596 return true;
1601 // INTERNAL PROPERTIES
1604 internal bool HasLocalTransaction {
1605 get {
1606 return GetOpenConnection().HasLocalTransaction;
1610 internal bool HasLocalTransactionFromAPI {
1611 get {
1612 Task reconnectTask = _currentReconnectionTask;
1613 if (reconnectTask != null && !reconnectTask.IsCompleted) {
1614 return false; //we will not go into reconnection if we are inside the transaction
1616 return GetOpenConnection().HasLocalTransactionFromAPI;
1620 internal bool IsShiloh {
1621 get {
1622 if (_currentReconnectionTask != null) { // holds true even if task is completed
1623 return true; // if CR is enabled, connection, if established, will be Katmai+
1625 return GetOpenConnection().IsShiloh;
1629 internal bool IsYukonOrNewer {
1630 get {
1631 if (_currentReconnectionTask != null) { // holds true even if task is completed
1632 return true; // if CR is enabled, connection, if established, will be Katmai+
1634 return GetOpenConnection().IsYukonOrNewer;
1638 internal bool IsKatmaiOrNewer {
1639 get {
1640 if (_currentReconnectionTask != null) { // holds true even if task is completed
1641 return true; // if CR is enabled, connection, if established, will be Katmai+
1643 return GetOpenConnection().IsKatmaiOrNewer;
1647 internal TdsParser Parser {
1648 get {
1649 SqlInternalConnectionTds tdsConnection = (GetOpenConnection() as SqlInternalConnectionTds);
1650 if (null == tdsConnection) {
1651 throw SQL.NotAvailableOnContextConnection();
1653 return tdsConnection.Parser;
1657 internal bool Asynchronous {
1658 get {
1659 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
1660 return ((null != constr) ? constr.Asynchronous : SqlConnectionString.DEFAULT.Asynchronous);
1665 // INTERNAL METHODS
1668 internal void ValidateConnectionForExecute(string method, SqlCommand command) {
1669 Task asyncWaitingForReconnection=_asyncWaitingForReconnection;
1670 if (asyncWaitingForReconnection!=null) {
1671 if (!asyncWaitingForReconnection.IsCompleted) {
1672 throw SQL.MARSUnspportedOnConnection();
1674 else {
1675 Interlocked.CompareExchange(ref _asyncWaitingForReconnection, null, asyncWaitingForReconnection);
1678 if (_currentReconnectionTask != null) {
1679 Task currentReconnectionTask = _currentReconnectionTask;
1680 if (currentReconnectionTask != null && !currentReconnectionTask.IsCompleted) {
1681 return; // execution will wait for this task later
1684 SqlInternalConnection innerConnection = GetOpenConnection(method);
1685 innerConnection.ValidateConnectionForExecute(command);
1688 // Surround name in brackets and then escape any end bracket to protect against SQL Injection.
1689 // NOTE: if the user escapes it themselves it will not work, but this was the case in V1 as well
1690 // as native OleDb and Odbc.
1691 static internal string FixupDatabaseTransactionName(string name) {
1692 if (!ADP.IsEmpty(name)) {
1693 return SqlServerEscapeHelper.EscapeIdentifier(name);
1695 else {
1696 return name;
1700 // If wrapCloseInAction is defined, then the action it defines will be run with the connection close action passed in as a parameter
1701 // The close action also supports being run asynchronously
1702 internal void OnError(SqlException exception, bool breakConnection, Action<Action> wrapCloseInAction) {
1703 Debug.Assert(exception != null && exception.Errors.Count != 0, "SqlConnection: OnError called with null or empty exception!");
1705 // Bug fix - MDAC 49022 - connection open after failure... Problem was parser was passing
1706 // Open as a state - because the parser's connection to the netlib was open. We would
1707 // then set the connection state to the parser's state - which is not correct. The only
1708 // time the connection state should change to what is passed in to this function is if
1709 // the parser is broken, then we should be closed. Changed to passing in
1710 // TdsParserState, not ConnectionState.
1711 // fixed by Microsoft
1713 if (breakConnection && (ConnectionState.Open == State)) {
1715 if (wrapCloseInAction != null) {
1716 int capturedCloseCount = _closeCount;
1718 Action closeAction = () => {
1719 if (capturedCloseCount == _closeCount) {
1720 Bid.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID);
1721 Close();
1725 wrapCloseInAction(closeAction);
1727 else {
1728 Bid.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID);
1729 Close();
1733 if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) {
1734 // It is an error, and should be thrown. Class of TdsEnums.MIN_ERROR_CLASS or above is an error,
1735 // below TdsEnums.MIN_ERROR_CLASS denotes an info message.
1736 throw exception;
1738 else {
1739 // If it is a class < TdsEnums.MIN_ERROR_CLASS, it is a warning collection - so pass to handler
1740 this.OnInfoMessage(new SqlInfoMessageEventArgs(exception));
1745 // PRIVATE METHODS
1748 // SxS: using Debugger.IsAttached
1750 [ResourceExposure(ResourceScope.None)]
1751 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1752 private void CompleteOpen() {
1753 Debug.Assert(ConnectionState.Open == State, "CompleteOpen not open");
1754 // be sure to mark as open so SqlDebugCheck can issue Query
1756 // check to see if we need to hook up sql-debugging if a debugger is attached
1757 // We only need this check for Shiloh and earlier servers.
1758 if (!GetOpenConnection().IsYukonOrNewer &&
1759 System.Diagnostics.Debugger.IsAttached) {
1760 bool debugCheck = false;
1761 try {
1762 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); // MDAC 66682, 69017
1763 debugCheck = true;
1765 catch (SecurityException e) {
1766 ADP.TraceExceptionWithoutRethrow(e);
1769 if (debugCheck) {
1770 // if we don't have Unmanaged code permission, don't check for debugging
1771 // but let the connection be opened while under the debugger
1772 CheckSQLDebugOnConnect();
1777 internal SqlInternalConnection GetOpenConnection() {
1778 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
1779 if (null == innerConnection) {
1780 throw ADP.ClosedConnectionError();
1782 return innerConnection;
1785 internal SqlInternalConnection GetOpenConnection(string method) {
1786 DbConnectionInternal innerConnection = InnerConnection;
1787 SqlInternalConnection innerSqlConnection = (innerConnection as SqlInternalConnection);
1788 if (null == innerSqlConnection) {
1789 throw ADP.OpenConnectionRequired(method, innerConnection.State);
1791 return innerSqlConnection;
1794 internal SqlInternalConnectionTds GetOpenTdsConnection() {
1795 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
1796 if (null == innerConnection) {
1797 throw ADP.ClosedConnectionError();
1799 return innerConnection;
1802 internal SqlInternalConnectionTds GetOpenTdsConnection(string method) {
1803 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
1804 if (null == innerConnection) {
1805 throw ADP.OpenConnectionRequired(method, InnerConnection.State);
1807 return innerConnection;
1810 internal void OnInfoMessage(SqlInfoMessageEventArgs imevent) {
1811 bool notified;
1812 OnInfoMessage(imevent, out notified);
1815 internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) {
1816 if (Bid.TraceOn) {
1817 Debug.Assert(null != imevent, "null SqlInfoMessageEventArgs");
1818 Bid.Trace("<sc.SqlConnection.OnInfoMessage|API|INFO> %d#, Message='%ls'\n", ObjectID, ((null != imevent) ? imevent.Message : ""));
1820 SqlInfoMessageEventHandler handler = (SqlInfoMessageEventHandler)Events[EventInfoMessage];
1821 if (null != handler) {
1822 notified = true;
1823 try {
1824 handler(this, imevent);
1826 catch (Exception e) { // MDAC 53175
1827 if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
1828 throw;
1831 ADP.TraceExceptionWithoutRethrow(e);
1833 } else {
1834 notified = false;
1839 // SQL DEBUGGING SUPPORT
1842 // this only happens once per connection
1843 // SxS: using named file mapping APIs
1845 [ResourceExposure(ResourceScope.None)]
1846 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1847 private void CheckSQLDebugOnConnect() {
1848 IntPtr hFileMap;
1849 uint pid = (uint)SafeNativeMethods.GetCurrentProcessId();
1851 string mapFileName;
1853 // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
1854 if (ADP.IsPlatformNT5) {
1855 mapFileName = "Global\\" + TdsEnums.SDCI_MAPFILENAME;
1857 else {
1858 mapFileName = TdsEnums.SDCI_MAPFILENAME;
1861 mapFileName = mapFileName + pid.ToString(CultureInfo.InvariantCulture);
1863 hFileMap = NativeMethods.OpenFileMappingA(0x4/*FILE_MAP_READ*/, false, mapFileName);
1865 if (ADP.PtrZero != hFileMap) {
1866 IntPtr pMemMap = NativeMethods.MapViewOfFile(hFileMap, 0x4/*FILE_MAP_READ*/, 0, 0, IntPtr.Zero);
1867 if (ADP.PtrZero != pMemMap) {
1868 SqlDebugContext sdc = new SqlDebugContext();
1869 sdc.hMemMap = hFileMap;
1870 sdc.pMemMap = pMemMap;
1871 sdc.pid = pid;
1873 // optimization: if we only have to refresh memory-mapped data at connection open time
1874 // optimization: then call here instead of in CheckSQLDebug() which gets called
1875 // optimization: at command execution time
1876 // RefreshMemoryMappedData(sdc);
1878 // delaying setting out global state until after we issue this first SQLDebug command so that
1879 // we don't reentrantly call into CheckSQLDebug
1880 CheckSQLDebug(sdc);
1881 // now set our global state
1882 _sdc = sdc;
1887 // This overload is called by the Command object when executing stored procedures. Note that
1888 // if SQLDebug has never been called, it is a noop.
1889 internal void CheckSQLDebug() {
1890 if (null != _sdc)
1891 CheckSQLDebug(_sdc);
1894 // SxS: using GetCurrentThreadId
1895 [ResourceExposure(ResourceScope.None)]
1896 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1897 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] // MDAC 66682, 69017
1898 private void CheckSQLDebug(SqlDebugContext sdc) {
1899 // check to see if debugging has been activated
1900 Debug.Assert(null != sdc, "SQL Debug: invalid null debugging context!");
1902 #pragma warning disable 618
1903 uint tid = (uint)AppDomain.GetCurrentThreadId(); // Sql Debugging doesn't need fiber support;
1904 #pragma warning restore 618
1905 RefreshMemoryMappedData(sdc);
1911 // If we get here, the debugger must be hooked up.
1912 if (!sdc.active) {
1913 if (sdc.fOption/*TdsEnums.SQLDEBUG_ON*/) {
1914 // turn on
1915 sdc.active = true;
1916 sdc.tid = tid;
1917 try {
1918 IssueSQLDebug(TdsEnums.SQLDEBUG_ON, sdc.machineName, sdc.pid, sdc.dbgpid, sdc.sdiDllName, sdc.data);
1919 sdc.tid = 0; // reset so that the first successful time through, we notify the server of the context switch
1921 catch {
1922 sdc.active = false;
1923 throw;
1928 // be sure to pick up thread context switch, especially the first time through
1929 if (sdc.active) {
1930 if (!sdc.fOption/*TdsEnums.SQLDEBUG_OFF*/) {
1931 // turn off and free the memory
1932 sdc.Dispose();
1933 // okay if we throw out here, no state to clean up
1934 IssueSQLDebug(TdsEnums.SQLDEBUG_OFF, null, 0, 0, null, null);
1936 else {
1937 // notify server of context change
1938 if (sdc.tid != tid) {
1939 sdc.tid = tid;
1940 try {
1941 IssueSQLDebug(TdsEnums.SQLDEBUG_CONTEXT, null, sdc.pid, sdc.tid, null, null);
1943 catch {
1944 sdc.tid = 0;
1945 throw;
1952 private void IssueSQLDebug(uint option, string machineName, uint pid, uint id, string sdiDllName, byte[] data) {
1954 if (GetOpenConnection().IsYukonOrNewer) {
1956 return;
1961 SqlCommand c = new SqlCommand(TdsEnums.SP_SDIDEBUG, this);
1962 c.CommandType = CommandType.StoredProcedure;
1964 // context param
1965 SqlParameter p = new SqlParameter(null, SqlDbType.VarChar, TdsEnums.SQLDEBUG_MODE_NAMES[option].Length);
1966 p.Value = TdsEnums.SQLDEBUG_MODE_NAMES[option];
1967 c.Parameters.Add(p);
1969 if (option == TdsEnums.SQLDEBUG_ON) {
1970 // debug dll name
1971 p = new SqlParameter(null, SqlDbType.VarChar, sdiDllName.Length);
1972 p.Value = sdiDllName;
1973 c.Parameters.Add(p);
1974 // debug machine name
1975 p = new SqlParameter(null, SqlDbType.VarChar, machineName.Length);
1976 p.Value = machineName;
1977 c.Parameters.Add(p);
1980 if (option != TdsEnums.SQLDEBUG_OFF) {
1981 // client pid
1982 p = new SqlParameter(null, SqlDbType.Int);
1983 p.Value = pid;
1984 c.Parameters.Add(p);
1985 // dbgpid or tid
1986 p = new SqlParameter(null, SqlDbType.Int);
1987 p.Value = id;
1988 c.Parameters.Add(p);
1991 if (option == TdsEnums.SQLDEBUG_ON) {
1992 // debug data
1993 p = new SqlParameter(null, SqlDbType.VarBinary, (null != data) ? data.Length : 0);
1994 p.Value = data;
1995 c.Parameters.Add(p);
1998 c.ExecuteNonQuery();
2002 public static void ChangePassword(string connectionString, string newPassword) {
2003 IntPtr hscp;
2004 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.ChangePassword|API>") ;
2005 Bid.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
2006 try {
2007 if (ADP.IsEmpty(connectionString)) {
2008 throw SQL.ChangePasswordArgumentMissing("connectionString");
2010 if (ADP.IsEmpty(newPassword)) {
2011 throw SQL.ChangePasswordArgumentMissing("newPassword");
2013 if (TdsEnums.MAXLEN_NEWPASSWORD < newPassword.Length) {
2014 throw ADP.InvalidArgumentLength("newPassword", TdsEnums.MAXLEN_NEWPASSWORD);
2017 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null);
2019 SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
2020 if (connectionOptions.IntegratedSecurity || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
2022 throw SQL.ChangePasswordConflictsWithSSPI();
2024 if (! ADP.IsEmpty(connectionOptions.AttachDBFilename)) {
2025 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename);
2027 if (connectionOptions.ContextConnection) {
2028 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.Context_Connection);
2031 System.Security.PermissionSet permissionSet = connectionOptions.CreatePermissionSet();
2032 permissionSet.Demand();
2034 ChangePassword(connectionString, connectionOptions, null, newPassword, null);
2036 finally {
2037 Bid.ScopeLeave(ref hscp) ;
2041 public static void ChangePassword(string connectionString, SqlCredential credential, SecureString newSecurePassword) {
2042 IntPtr hscp;
2043 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.ChangePassword|API>") ;
2044 Bid.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
2045 try {
2046 if (ADP.IsEmpty(connectionString)) {
2047 throw SQL.ChangePasswordArgumentMissing("connectionString");
2050 // check credential; not necessary to check the length of password in credential as the check is done by SqlCredential class
2051 if (credential == null) {
2052 throw SQL.ChangePasswordArgumentMissing("credential");
2055 if (newSecurePassword == null || newSecurePassword.Length == 0) {
2056 throw SQL.ChangePasswordArgumentMissing("newSecurePassword");;
2059 if (!newSecurePassword.IsReadOnly()) {
2060 throw ADP.MustBeReadOnly("newSecurePassword");
2063 if (TdsEnums.MAXLEN_NEWPASSWORD < newSecurePassword.Length) {
2064 throw ADP.InvalidArgumentLength("newSecurePassword", TdsEnums.MAXLEN_NEWPASSWORD);
2067 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null);
2069 SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
2071 // Check for incompatible connection string value with SqlCredential
2072 if (!ADP.IsEmpty(connectionOptions.UserID) || !ADP.IsEmpty(connectionOptions.Password)) {
2073 throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
2076 if (connectionOptions.IntegratedSecurity || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
2077 throw SQL.ChangePasswordConflictsWithSSPI();
2080 if (! ADP.IsEmpty(connectionOptions.AttachDBFilename)) {
2081 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename);
2084 if (connectionOptions.ContextConnection) {
2085 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.Context_Connection);
2088 System.Security.PermissionSet permissionSet = connectionOptions.CreatePermissionSet();
2089 permissionSet.Demand();
2091 ChangePassword(connectionString, connectionOptions, credential, null, newSecurePassword);
2093 finally {
2094 Bid.ScopeLeave(ref hscp) ;
2098 private static void ChangePassword(string connectionString, SqlConnectionString connectionOptions, SqlCredential credential, string newPassword, SecureString newSecurePassword ) {
2099 // note: This is the only case where we directly construt the internal connection, passing in the new password.
2100 // Normally we would simply create a regular connectoin and open it but there is no other way to pass the
2101 // new password down to the constructor. Also it would have an unwanted impact on the connection pool
2103 using (SqlInternalConnectionTds con = new SqlInternalConnectionTds(null, connectionOptions, credential, null, newPassword, newSecurePassword, false)) {
2104 if (!con.IsYukonOrNewer) {
2105 throw SQL.ChangePasswordRequiresYukon();
2108 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null);
2110 SqlConnectionFactory.SingletonInstance.ClearPool(key);
2113 internal void RegisterForConnectionCloseNotification<T>(ref Task<T> outterTask, object value, int tag) {
2114 // Connection exists, schedule removal, will be added to ref collection after calling ValidateAndReconnect
2115 outterTask = outterTask.ContinueWith(task => {
2116 RemoveWeakReference(value);
2117 return task;
2118 }, TaskScheduler.Default).Unwrap();
2121 // updates our context with any changes made to the memory-mapped data by an external process
2122 static private void RefreshMemoryMappedData(SqlDebugContext sdc) {
2123 Debug.Assert(ADP.PtrZero != sdc.pMemMap, "SQL Debug: invalid null value for pMemMap!");
2124 // copy memory mapped file contents into managed types
2125 MEMMAP memMap = (MEMMAP)Marshal.PtrToStructure(sdc.pMemMap, typeof(MEMMAP));
2126 sdc.dbgpid = memMap.dbgpid;
2127 sdc.fOption = (memMap.fOption == 1) ? true : false;
2128 // xlate ansi byte[] -> managed strings
2129 Encoding cp = System.Text.Encoding.GetEncoding(TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE);
2130 sdc.machineName = cp.GetString(memMap.rgbMachineName, 0, memMap.rgbMachineName.Length);
2131 sdc.sdiDllName = cp.GetString(memMap.rgbDllName, 0, memMap.rgbDllName.Length);
2132 // just get data reference
2133 sdc.data = memMap.rgbData;
2136 public void ResetStatistics() {
2137 if (IsContextConnection) {
2138 throw SQL.NotAvailableOnContextConnection();
2141 if (null != Statistics) {
2142 Statistics.Reset();
2143 if (ConnectionState.Open == State) {
2144 // update timestamp;
2145 ADP.TimerCurrent(out _statistics._openTimestamp);
2150 public IDictionary RetrieveStatistics() {
2151 if (IsContextConnection) {
2152 throw SQL.NotAvailableOnContextConnection();
2155 if (null != Statistics) {
2156 UpdateStatistics();
2157 return Statistics.GetHashtable();
2159 else {
2160 return new SqlStatistics().GetHashtable();
2164 private void UpdateStatistics() {
2165 if (ConnectionState.Open == State) {
2166 // update timestamp
2167 ADP.TimerCurrent(out _statistics._closeTimestamp);
2169 // delegate the rest of the work to the SqlStatistics class
2170 Statistics.UpdateStatistics();
2174 // UDT SUPPORT
2177 private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) {
2178 Debug.Assert(TypeSystemAssemblyVersion != null, "TypeSystemAssembly should be set !");
2179 if (string.Compare(asmRef.Name, "Microsoft.SqlServer.Types", StringComparison.OrdinalIgnoreCase) == 0) {
2180 if (Bid.TraceOn) {
2181 if (asmRef.Version!=TypeSystemAssemblyVersion) {
2182 Bid.Trace("<sc.SqlConnection.ResolveTypeAssembly> SQL CLR type version change: Server sent %ls, client will instantiate %ls",
2183 asmRef.Version.ToString(), TypeSystemAssemblyVersion.ToString());
2186 asmRef.Version = TypeSystemAssemblyVersion;
2188 try {
2189 return Assembly.Load(asmRef);
2191 catch (Exception e) {
2192 if (throwOnError || !ADP.IsCatchableExceptionType(e)) {
2193 throw;
2195 else {
2196 return null;
2202 internal void CheckGetExtendedUDTInfo(SqlMetaDataPriv metaData, bool fThrow) {
2203 if (metaData.udtType == null) { // If null, we have not obtained extended info.
2204 Debug.Assert(!ADP.IsEmpty(metaData.udtAssemblyQualifiedName), "Unexpected state on GetUDTInfo");
2205 // Parameter throwOnError determines whether exception from Assembly.Load is thrown.
2206 metaData.udtType =
2207 Type.GetType(typeName:metaData.udtAssemblyQualifiedName, assemblyResolver:asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver:null, throwOnError: fThrow);
2209 if (fThrow && metaData.udtType == null) {
2211 throw SQL.UDTUnexpectedResult(metaData.udtAssemblyQualifiedName);
2216 internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnDBNull) {
2217 if (returnDBNull && ADP.IsNull(value)) {
2218 return DBNull.Value;
2221 object o = null;
2223 // Since the serializer doesn't handle nulls...
2224 if (ADP.IsNull(value)) {
2225 Type t = metaData.udtType;
2226 Debug.Assert(t != null, "Unexpected null of udtType on GetUdtValue!");
2227 o = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, new Object[]{}, CultureInfo.InvariantCulture);
2228 Debug.Assert(o != null);
2229 return o;
2231 else {
2233 MemoryStream stm = new MemoryStream((byte[]) value);
2235 o = SerializationHelperSql9.Deserialize(stm, metaData.udtType);
2237 Debug.Assert(o != null, "object could NOT be created");
2238 return o;
2242 internal byte[] GetBytes(object o) {
2243 Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;
2244 int maxSize = 0;
2245 return GetBytes(o, out format, out maxSize);
2248 internal byte[] GetBytes(object o, out Microsoft.SqlServer.Server.Format format, out int maxSize) {
2249 SqlUdtInfo attr = AssemblyCache.GetInfoFromType(o.GetType());
2250 maxSize = attr.MaxByteSize;
2251 format = attr.SerializationFormat;
2253 if (maxSize < -1 || maxSize >= UInt16.MaxValue) { // Do we need this? Is this the right place?
2254 throw new InvalidOperationException(o.GetType() + ": invalid Size");
2257 byte[] retval;
2259 using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) {
2260 SerializationHelperSql9.Serialize(stm, o);
2261 retval = stm.ToArray();
2263 return retval;
2265 } // SqlConnection
2274 ComVisible(true),
2275 ClassInterface(ClassInterfaceType.None),
2276 Guid("afef65ad-4577-447a-a148-83acadd3d4b9"),
2278 [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
2279 public sealed class SQLDebugging: ISQLDebug {
2281 // Security stuff
2282 const int STANDARD_RIGHTS_REQUIRED = (0x000F0000);
2283 const int DELETE = (0x00010000);
2284 const int READ_CONTROL = (0x00020000);
2285 const int WRITE_DAC = (0x00040000);
2286 const int WRITE_OWNER = (0x00080000);
2287 const int SYNCHRONIZE = (0x00100000);
2288 const int FILE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x000001FF);
2289 const uint GENERIC_READ = (0x80000000);
2290 const uint GENERIC_WRITE = (0x40000000);
2291 const uint GENERIC_EXECUTE = (0x20000000);
2292 const uint GENERIC_ALL = (0x10000000);
2294 const int SECURITY_DESCRIPTOR_REVISION = (1);
2295 const int ACL_REVISION = (2);
2297 const int SECURITY_AUTHENTICATED_USER_RID = (0x0000000B);
2298 const int SECURITY_LOCAL_SYSTEM_RID = (0x00000012);
2299 const int SECURITY_BUILTIN_DOMAIN_RID = (0x00000020);
2300 const int SECURITY_WORLD_RID = (0x00000000);
2301 const byte SECURITY_NT_AUTHORITY = 5;
2302 const int DOMAIN_GROUP_RID_ADMINS = (0x00000200);
2303 const int DOMAIN_ALIAS_RID_ADMINS = (0x00000220);
2305 const int sizeofSECURITY_ATTRIBUTES = 12; // sizeof(SECURITY_ATTRIBUTES);
2306 const int sizeofSECURITY_DESCRIPTOR = 20; // sizeof(SECURITY_DESCRIPTOR);
2307 const int sizeofACCESS_ALLOWED_ACE = 12; // sizeof(ACCESS_ALLOWED_ACE);
2308 const int sizeofACCESS_DENIED_ACE = 12; // sizeof(ACCESS_DENIED_ACE);
2309 const int sizeofSID_IDENTIFIER_AUTHORITY = 6; // sizeof(SID_IDENTIFIER_AUTHORITY)
2310 const int sizeofACL = 8; // sizeof(ACL);
2312 private IntPtr CreateSD(ref IntPtr pDacl) {
2313 IntPtr pSecurityDescriptor = IntPtr.Zero;
2314 IntPtr pUserSid = IntPtr.Zero;
2315 IntPtr pAdminSid = IntPtr.Zero;
2316 IntPtr pNtAuthority = IntPtr.Zero;
2317 int cbAcl = 0;
2318 bool status = false;
2320 pNtAuthority = Marshal.AllocHGlobal(sizeofSID_IDENTIFIER_AUTHORITY);
2321 if (pNtAuthority == IntPtr.Zero)
2322 goto cleanup;
2323 Marshal.WriteInt32(pNtAuthority, 0, 0);
2324 Marshal.WriteByte(pNtAuthority, 4, 0);
2325 Marshal.WriteByte(pNtAuthority, 5, SECURITY_NT_AUTHORITY);
2327 status =
2328 NativeMethods.AllocateAndInitializeSid(
2329 pNtAuthority,
2330 (byte)1,
2331 SECURITY_AUTHENTICATED_USER_RID,
2339 ref pUserSid);
2341 if (!status || pUserSid == IntPtr.Zero) {
2342 goto cleanup;
2344 status =
2345 NativeMethods.AllocateAndInitializeSid(
2346 pNtAuthority,
2347 (byte)2,
2348 SECURITY_BUILTIN_DOMAIN_RID,
2349 DOMAIN_ALIAS_RID_ADMINS,
2356 ref pAdminSid);
2358 if (!status || pAdminSid == IntPtr.Zero) {
2359 goto cleanup;
2361 status = false;
2362 pSecurityDescriptor = Marshal.AllocHGlobal(sizeofSECURITY_DESCRIPTOR);
2363 if (pSecurityDescriptor == IntPtr.Zero) {
2364 goto cleanup;
2366 for (int i = 0; i < sizeofSECURITY_DESCRIPTOR; i++)
2367 Marshal.WriteByte(pSecurityDescriptor, i, (byte)0);
2368 cbAcl = sizeofACL
2369 + (2 * (sizeofACCESS_ALLOWED_ACE))
2370 + sizeofACCESS_DENIED_ACE
2371 + NativeMethods.GetLengthSid(pUserSid)
2372 + NativeMethods.GetLengthSid(pAdminSid);
2374 pDacl = Marshal.AllocHGlobal(cbAcl);
2375 if (pDacl == IntPtr.Zero) {
2376 goto cleanup;
2378 // rights must be added in a certain order. Namely, deny access first, then add access
2379 if (NativeMethods.InitializeAcl(pDacl, cbAcl, ACL_REVISION))
2380 if (NativeMethods.AddAccessDeniedAce(pDacl, ACL_REVISION, WRITE_DAC, pUserSid))
2381 if (NativeMethods.AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_READ, pUserSid))
2382 if (NativeMethods.AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_ALL, pAdminSid))
2383 if (NativeMethods.InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
2384 if (NativeMethods.SetSecurityDescriptorDacl(pSecurityDescriptor, true, pDacl, false)) {
2385 status = true;
2388 cleanup :
2389 if (pNtAuthority != IntPtr.Zero) {
2390 Marshal.FreeHGlobal(pNtAuthority);
2392 if (pAdminSid != IntPtr.Zero)
2393 NativeMethods.FreeSid(pAdminSid);
2394 if (pUserSid != IntPtr.Zero)
2395 NativeMethods.FreeSid(pUserSid);
2396 if (status)
2397 return pSecurityDescriptor;
2398 else {
2399 if (pSecurityDescriptor != IntPtr.Zero) {
2400 Marshal.FreeHGlobal(pSecurityDescriptor);
2403 return IntPtr.Zero;
2406 // SxS: using file mapping API (CreateFileMapping)
2408 [ResourceExposure(ResourceScope.None)]
2409 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
2410 bool ISQLDebug.SQLDebug(int dwpidDebugger, int dwpidDebuggee, [MarshalAs(UnmanagedType.LPStr)] string pszMachineName,
2411 [MarshalAs(UnmanagedType.LPStr)] string pszSDIDLLName, int dwOption, int cbData, byte[] rgbData) {
2412 bool result = false;
2413 IntPtr hFileMap = IntPtr.Zero;
2414 IntPtr pMemMap = IntPtr.Zero;
2415 IntPtr pSecurityDescriptor = IntPtr.Zero;
2416 IntPtr pSecurityAttributes = IntPtr.Zero;
2417 IntPtr pDacl = IntPtr.Zero;
2419 // validate the structure
2420 if (null == pszMachineName || null == pszSDIDLLName)
2421 return false;
2423 if (pszMachineName.Length > TdsEnums.SDCI_MAX_MACHINENAME ||
2424 pszSDIDLLName.Length > TdsEnums.SDCI_MAX_DLLNAME)
2425 return false;
2427 // note that these are ansi strings
2428 Encoding cp = System.Text.Encoding.GetEncoding(TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE);
2429 byte[] rgbMachineName = cp.GetBytes(pszMachineName);
2430 byte[] rgbSDIDLLName = cp.GetBytes(pszSDIDLLName);
2432 if (null != rgbData && cbData > TdsEnums.SDCI_MAX_DATA)
2433 return false;
2435 string mapFileName;
2437 // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
2438 if (ADP.IsPlatformNT5) {
2439 mapFileName = "Global\\" + TdsEnums.SDCI_MAPFILENAME;
2441 else {
2442 mapFileName = TdsEnums.SDCI_MAPFILENAME;
2445 mapFileName = mapFileName + dwpidDebuggee.ToString(CultureInfo.InvariantCulture);
2447 // Create Security Descriptor
2448 pSecurityDescriptor = CreateSD(ref pDacl);
2449 pSecurityAttributes = Marshal.AllocHGlobal(sizeofSECURITY_ATTRIBUTES);
2450 if ((pSecurityDescriptor == IntPtr.Zero) || (pSecurityAttributes == IntPtr.Zero))
2451 return false;
2453 Marshal.WriteInt32(pSecurityAttributes, 0, sizeofSECURITY_ATTRIBUTES); // nLength = sizeof(SECURITY_ATTRIBUTES)
2454 Marshal.WriteIntPtr(pSecurityAttributes, 4, pSecurityDescriptor); // lpSecurityDescriptor = pSecurityDescriptor
2455 Marshal.WriteInt32(pSecurityAttributes, 8, 0); // bInheritHandle = FALSE
2456 hFileMap = NativeMethods.CreateFileMappingA(
2457 ADP.InvalidPtr/*INVALID_HANDLE_VALUE*/,
2458 pSecurityAttributes,
2459 0x4/*PAGE_READWRITE*/,
2461 Marshal.SizeOf(typeof(MEMMAP)),
2462 mapFileName);
2464 if (IntPtr.Zero == hFileMap) {
2465 goto cleanup;
2469 pMemMap = NativeMethods.MapViewOfFile(hFileMap, 0x6/*FILE_MAP_READ|FILE_MAP_WRITE*/, 0, 0, IntPtr.Zero);
2471 if (IntPtr.Zero == pMemMap) {
2472 goto cleanup;
2475 // copy data to memory-mapped file
2476 // layout of MEMMAP structure is:
2477 // uint dbgpid
2478 // uint fOption
2479 // byte[32] machineName
2480 // byte[16] sdiDllName
2481 // uint dbData
2482 // byte[255] vData
2483 int offset = 0;
2484 Marshal.WriteInt32(pMemMap, offset, (int)dwpidDebugger);
2485 offset += 4;
2486 Marshal.WriteInt32(pMemMap, offset, (int)dwOption);
2487 offset += 4;
2488 Marshal.Copy(rgbMachineName, 0, ADP.IntPtrOffset(pMemMap, offset), rgbMachineName.Length);
2489 offset += TdsEnums.SDCI_MAX_MACHINENAME;
2490 Marshal.Copy(rgbSDIDLLName, 0, ADP.IntPtrOffset(pMemMap, offset), rgbSDIDLLName.Length);
2491 offset += TdsEnums.SDCI_MAX_DLLNAME;
2492 Marshal.WriteInt32(pMemMap, offset, (int)cbData);
2493 offset += 4;
2494 if (null != rgbData) {
2495 Marshal.Copy(rgbData, 0, ADP.IntPtrOffset(pMemMap, offset), (int)cbData);
2497 NativeMethods.UnmapViewOfFile(pMemMap);
2498 result = true;
2499 cleanup :
2500 if (result == false) {
2501 if (hFileMap != IntPtr.Zero)
2502 NativeMethods.CloseHandle(hFileMap);
2504 if (pSecurityAttributes != IntPtr.Zero)
2505 Marshal.FreeHGlobal(pSecurityAttributes);
2506 if (pSecurityDescriptor != IntPtr.Zero)
2507 Marshal.FreeHGlobal(pSecurityDescriptor);
2508 if (pDacl != IntPtr.Zero)
2509 Marshal.FreeHGlobal(pDacl);
2510 return result;
2514 // this is a private interface to com+ users
2515 // do not change this guid
2517 ComImport,
2518 ComVisible(true),
2519 Guid("6cb925bf-c3c0-45b3-9f44-5dd67c7b7fe8"),
2520 InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
2521 BestFitMapping(false, ThrowOnUnmappableChar = true),
2523 interface ISQLDebug {
2525 [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
2526 bool SQLDebug(
2527 int dwpidDebugger,
2528 int dwpidDebuggee,
2529 [MarshalAs(UnmanagedType.LPStr)] string pszMachineName,
2530 [MarshalAs(UnmanagedType.LPStr)] string pszSDIDLLName,
2531 int dwOption,
2532 int cbData,
2533 byte[] rgbData);
2536 sealed class SqlDebugContext: IDisposable {
2537 // context data
2538 internal uint pid = 0;
2539 internal uint tid = 0;
2540 internal bool active = false;
2541 // memory-mapped data
2542 internal IntPtr pMemMap = ADP.PtrZero;
2543 internal IntPtr hMemMap = ADP.PtrZero;
2544 internal uint dbgpid = 0;
2545 internal bool fOption = false;
2546 internal string machineName = null;
2547 internal string sdiDllName = null;
2548 internal byte[] data = null;
2550 public void Dispose() {
2551 Dispose(true);
2552 GC.SuppressFinalize(this);
2555 // using CloseHandle and UnmapViewOfFile - no exposure
2556 [ResourceExposure(ResourceScope.None)]
2557 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
2558 private void Dispose(bool disposing) {
2559 if (disposing) {
2560 // Nothing to do here
2563 if (pMemMap != IntPtr.Zero) {
2564 NativeMethods.UnmapViewOfFile(pMemMap);
2565 pMemMap = IntPtr.Zero;
2567 if (hMemMap != IntPtr.Zero) {
2568 NativeMethods.CloseHandle(hMemMap);
2569 hMemMap = IntPtr.Zero;
2571 active = false;
2574 ~SqlDebugContext() {
2575 Dispose(false);
2580 // native interop memory mapped structure for sdi debugging
2581 [StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
2582 internal struct MEMMAP {
2583 [MarshalAs(UnmanagedType.U4)]
2584 internal uint dbgpid; // id of debugger
2585 [MarshalAs(UnmanagedType.U4)]
2586 internal uint fOption; // 1 - start debugging, 0 - stop debugging
2587 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
2588 internal byte[] rgbMachineName;
2589 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
2590 internal byte[] rgbDllName;
2591 [MarshalAs(UnmanagedType.U4)]
2592 internal uint cbData;
2593 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
2594 internal byte[] rgbData;
2596 } // System.Data.SqlClient namespace