1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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
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
;
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
;
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
;
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()}
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.
64 static private ReadOnlyDictionary
<string, SqlColumnEncryptionKeyStoreProvider
> _CustomColumnEncryptionKeyStoreProviders
;
66 // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders
67 static private readonly Object _CustomColumnEncryptionKeyProvidersLock
= new Object();
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.
74 static private readonly ConcurrentDictionary
<string, IList
<string>> _ColumnEncryptionTrustedMasterKeyPaths
75 = new ConcurrentDictionary
<string, IList
<string>>(concurrencyLevel
: 4 * Environment
.ProcessorCount
/* default value in ConcurrentDictionary*/,
77 comparer: StringComparer
.OrdinalIgnoreCase
);
81 ResCategoryAttribute(Res
.DataCategory_Data
),
82 ResDescriptionAttribute(Res
.TCE_SqlConnection_TrustedColumnMasterKeyPaths
),
84 static public IDictionary
<string, IList
<string>> ColumnEncryptionTrustedMasterKeyPaths
88 return _ColumnEncryptionTrustedMasterKeyPaths
;
93 /// Defines whether query metadata caching is enabled.
95 static private bool _ColumnEncryptionQueryMetadataCacheEnabled
= true;
99 ResCategoryAttribute(Res
.DataCategory_Data
),
100 ResDescriptionAttribute(Res
.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled
),
102 static public bool ColumnEncryptionQueryMetadataCacheEnabled
106 return _ColumnEncryptionQueryMetadataCacheEnabled
;
110 _ColumnEncryptionQueryMetadataCacheEnabled
= value;
115 /// Defines whether query metadata caching is enabled.
117 static private TimeSpan _ColumnEncryptionKeyCacheTtl
= TimeSpan
.FromHours(2);
121 ResCategoryAttribute(Res
.DataCategory_Data
),
122 ResDescriptionAttribute(Res
.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl
),
124 static public TimeSpan ColumnEncryptionKeyCacheTtl
128 return _ColumnEncryptionKeyCacheTtl
;
132 _ColumnEncryptionKeyCacheTtl
= value;
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.
142 /// Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
143 /// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
144 /// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
145 /// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
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
);
201 /// This function walks through both system and custom column encryption key store providers and returns an object if found.
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
))
218 lock (_CustomColumnEncryptionKeyProvidersLock
)
220 // If custom provider is not set, then return false
221 if (_CustomColumnEncryptionKeyStoreProviders
== null)
226 // Search in the custom provider list
227 return _CustomColumnEncryptionKeyStoreProviders
.TryGetValue(providerName
, out columnKeyStoreProvider
);
232 /// This function returns a list of system provider dictionary currently supported by this driver.
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();
241 /// This function returns a list of custom provider dictionary currently registered.
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
;
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
;
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
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.
367 ResCategoryAttribute(Res
.DataCategory_Data
),
368 ResDescriptionAttribute(Res
.SqlConnection_StatisticsEnabled
),
370 public bool StatisticsEnabled
{
372 return (_collectstats
);
375 if (IsContextConnection
) {
377 throw SQL
.NotAvailableOnContextConnection();
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
390 Debug
.Assert(Parser
!= null, "Where's the parser?");
391 Parser
.Statistics
= _statistics
;
396 if (null != _statistics
) {
397 if (ConnectionState
.Open
== State
) {
398 // remove statistics from parser
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
{
414 return (_AsyncCommandInProgress
);
416 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.Success
)]
418 _AsyncCommandInProgress
= value;
422 internal bool IsContextConnection
{
424 SqlConnectionString opt
= (SqlConnectionString
)ConnectionOptions
;
425 return UsesContextConnection(opt
);
430 /// Is this connection using column encryption ?
432 internal bool IsColumnEncryptionSettingEnabled
{
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
) {
463 result
= (!ADP
.IsEmpty(opt
.UserID
) || !ADP
.IsEmpty(opt
.Password
));
468 internal SqlConnectionString
.TransactionBindingEnum TransactionBinding
{
470 return ((SqlConnectionString
)ConnectionOptions
).TransactionBinding
;
474 internal SqlConnectionString
.TypeSystem TypeSystem
{
476 return ((SqlConnectionString
)ConnectionOptions
).TypeSystemVersion
;
480 internal Version TypeSystemAssemblyVersion
{
482 return ((SqlConnectionString
)ConnectionOptions
).TypeSystemAssemblyVersion
;
486 internal PoolBlockingPeriod PoolBlockingPeriod
490 return ((SqlConnectionString
)ConnectionOptions
).PoolBlockingPeriod
;
494 internal int ConnectRetryInterval
{
496 return ((SqlConnectionString
)ConnectionOptions
).ConnectRetryInterval
;
500 override protected DbProviderFactory DbProviderFactory
{
502 return SqlClientFactory
.Instance
;
506 // AccessToken: To be used for token based authentication
509 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
510 ResDescriptionAttribute(Res
.SqlConnection_AccessToken
),
512 public string AccessToken
{
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
) {
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
);
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
));
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
{
554 return ConnectionString_Get();
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
{
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.
599 SqlInternalConnection innerConnection
= (InnerConnection
as SqlInternalConnection
);
602 if (null != innerConnection
) {
603 result
= innerConnection
.CurrentDatabase
;
606 SqlConnectionString constr
= (SqlConnectionString
)ConnectionOptions
;
607 result
= ((null != constr
) ? constr
.InitialCatalog
: SqlConnectionString
.DEFAULT
.Initial_Catalog
);
615 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
616 ResDescriptionAttribute(Res
.SqlConnection_DataSource
),
618 override public string DataSource
{
620 SqlInternalConnection innerConnection
= (InnerConnection
as SqlInternalConnection
);
623 if (null != innerConnection
) {
624 result
= innerConnection
.CurrentDataSource
;
627 SqlConnectionString constr
= (SqlConnectionString
)ConnectionOptions
;
628 result
= ((null != constr
) ? constr
.DataSource
: SqlConnectionString
.DEFAULT
.Data_Source
);
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.
644 if (IsContextConnection
) {
645 throw SQL
.NotAvailableOnContextConnection();
648 SqlInternalConnectionTds innerConnection
= (InnerConnection
as SqlInternalConnectionTds
);
651 if (null != innerConnection
) {
652 result
= innerConnection
.PacketSize
;
655 SqlConnectionString constr
= (SqlConnectionString
)ConnectionOptions
;
656 result
= ((null != constr
) ? constr
.PacketSize
: SqlConnectionString
.DEFAULT
.Packet_Size
);
663 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
664 ResCategoryAttribute(Res
.DataCategory_Data
),
665 ResDescriptionAttribute(Res
.SqlConnection_ClientConnectionId
),
667 public Guid ClientConnectionId
{
670 SqlInternalConnectionTds innerConnection
= (InnerConnection
as SqlInternalConnectionTds
);
672 if (null != innerConnection
) {
673 return innerConnection
.ClientConnectionId
;
676 Task reconnectTask
= _currentReconnectionTask
;
677 if (reconnectTask
!= null && !reconnectTask
.IsCompleted
) {
678 return _originalConnectionId
;
687 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
688 ResDescriptionAttribute(Res
.SqlConnection_ServerVersion
),
690 override public string ServerVersion
{
692 return GetOpenConnection().ServerVersion
;
698 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
699 ResDescriptionAttribute(Res
.DbConnection_State
),
701 override public ConnectionState State
{
703 Task reconnectTask
=_currentReconnectionTask
;
704 if (reconnectTask
!= null && !reconnectTask
.IsCompleted
) {
705 return ConnectionState
.Open
;
707 return InnerConnection
.State
;
712 internal SqlStatistics Statistics
{
719 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
720 ResCategoryAttribute(Res
.DataCategory_Data
),
721 ResDescriptionAttribute(Res
.SqlConnection_WorkstationId
),
723 public string WorkstationId
{
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
;
743 // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication
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
)
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
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();
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();
852 ResCategoryAttribute(Res
.DataCategory_InfoMessage
),
853 ResDescriptionAttribute(Res
.DbConnection_InfoMessage
),
855 public event SqlInfoMessageEventHandler InfoMessage
{
857 Events
.AddHandler(EventInfoMessage
, value);
860 Events
.RemoveHandler(EventInfoMessage
, value);
864 public bool FireInfoMessageEventOnUserErrors
{
866 return _fireInfoMessageEventOnUserErrors
;
869 _fireInfoMessageEventOnUserErrors
= value;
873 // Approx. number of times that the internal connection has been reconnected
874 internal int ReconnectCount
{
876 return _reconnectCount
;
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
) {
907 Bid
.ScopeEnter(out hscp
, "<prov.SqlConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID
, (int)isolationLevel
);
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
921 Bid
.ScopeLeave(ref hscp
);
925 public SqlTransaction
BeginTransaction(IsolationLevel iso
, string transactionName
) {
926 WaitForPendingReconnection();
927 SqlStatistics statistics
= null;
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
,
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;
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.
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();
969 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
971 RuntimeHelpers
.PrepareConstrainedRegions();
973 tdsReliabilitySection
.Start();
977 bestEffortCleanupTarget
= SqlInternalConnection
.GetBestEffortCleanupTarget(this);
978 statistics
= SqlStatistics
.StartTimer(Statistics
);
979 InnerConnection
.ChangeDatabase(database
);
983 tdsReliabilitySection
.Stop();
987 catch (System
.OutOfMemoryException e
) {
991 catch (System
.StackOverflowException e
) {
995 catch (System
.Threading
.ThreadAbortException e
) {
997 SqlInternalConnection
.BestEffortCleanup(bestEffortCleanupTarget
);
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
);
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() {
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
);
1043 SqlStatistics statistics
= null;
1045 TdsParser bestEffortCleanupTarget
= null;
1046 RuntimeHelpers
.PrepareConstrainedRegions();
1049 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
1051 RuntimeHelpers
.PrepareConstrainedRegions();
1053 tdsReliabilitySection
.Start();
1057 bestEffortCleanupTarget
= SqlInternalConnection
.GetBestEffortCleanupTarget(this);
1058 statistics
= SqlStatistics
.StartTimer(Statistics
);
1060 Task reconnectTask
= _currentReconnectionTask
;
1061 if (reconnectTask
!= null && !reconnectTask
.IsCompleted
) {
1062 CancellationTokenSource cts
= _reconnectionCancellationSource
;
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
);
1081 tdsReliabilitySection
.Stop();
1085 catch (System
.OutOfMemoryException e
) {
1089 catch (System
.StackOverflowException e
) {
1093 catch (System
.Threading
.ThreadAbortException e
) {
1095 SqlInternalConnection
.BestEffortCleanup(bestEffortCleanupTarget
);
1099 SqlStatistics
.StopTimer(statistics
);
1100 //dispose windows identity once connection is closed.
1101 if (_lastIdentity
!= null) {
1102 _lastIdentity
.Dispose();
1107 SqlDebugContext sdc
= _sdc
;
1109 Bid
.ScopeLeave(ref hscp
);
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
1124 _accessToken
= null;
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);
1142 public void EnlistDistributedTransaction(System
.EnterpriseServices
.ITransaction transaction
) {
1143 if (IsContextConnection
) {
1144 throw SQL
.NotAvailableOnContextConnection();
1147 EnlistDistributedTransactionHelper(transaction
);
1151 override public void Open() {
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
);
1157 if (StatisticsEnabled
) {
1158 if (null == _statistics
) {
1159 _statistics
= new SqlStatistics();
1162 _statistics
.ContinueOnNewConnection();
1166 SqlStatistics statistics
= null;
1167 RuntimeHelpers
.PrepareConstrainedRegions();
1169 statistics
= SqlStatistics
.StartTimer(Statistics
);
1171 if (!TryOpen(null)) {
1172 throw ADP
.InternalError(ADP
.InternalErrorCode
.SynchronousConnectReturnedPending
);
1176 SqlStatistics
.StopTimer(statistics
);
1180 Bid
.ScopeLeave(ref hscp
);
1184 internal void RegisterWaitingForReconnect(Task waitingTask
) {
1185 if (((SqlConnectionString
)ConnectionOptions
).MARS
) {
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
) {
1196 long commandTimeoutExpiration
= 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());
1210 _impersonateIdentity
= _lastIdentity
;
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);
1217 Debug
.Assert(_recoverySessionData
._debugReconnectDataApplied
, "Reconnect data was not applied !");
1221 _impersonateIdentity
= null;
1222 ForceNewConnection
= false;
1224 Bid
.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Reconnection suceeded. ClientConnectionID %ls -> %ls \n", _originalConnectionId
.ToString(), ClientConnectionId
.ToString());
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);
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) {
1265 if (beforeDisconnect
!= null) {
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) {
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
;
1299 callDisconnect
= true;
1302 if (callDisconnect
&& beforeDisconnect
!= null) {
1307 if (beforeDisconnect
!= null) {
1310 OnError(SQL
.CR_UnrecoverableServer(ClientConnectionId
), true, null);
1312 } // ValidateSNIConnection
1313 } // sessionRecoverySupported
1314 } // connectRetryCount>0
1316 else { // runningReconnect = null
1317 if (beforeDisconnect
!= null) {
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) {
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
) {
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
);
1362 if (StatisticsEnabled
) {
1363 if (null == _statistics
) {
1364 _statistics
= new SqlStatistics();
1367 _statistics
.ContinueOnNewConnection();
1371 SqlStatistics statistics
= null;
1372 RuntimeHelpers
.PrepareConstrainedRegions();
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();
1385 if (IsContextConnection
) {
1386 // Async not supported on Context Connections
1387 result
.SetException(ADP
.ExceptionWithStackTrace(SQL
.NotAvailableOnContextConnection()));
1394 completed
= TryOpen(completion
);
1396 catch (Exception e
) {
1397 result
.SetException(e
);
1402 result
.SetResult(null);
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
);
1418 SqlStatistics
.StopTimer(statistics
);
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
) {
1436 _registration
= registration
;
1439 internal void Retry(Task
<DbConnectionInternal
> retryTask
) {
1440 Bid
.Trace("<sc.SqlConnection.OpenAsyncRetry|Info> %d#\n", _parent
.ObjectID
);
1441 _registration
.Dispose();
1443 SqlStatistics statistics
= null;
1444 RuntimeHelpers
.PrepareConstrainedRegions();
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();
1461 // protect continuation from ----s with close and cancel
1462 lock (_parent
.InnerConnection
) {
1463 result
= _parent
.TryOpen(_retry
);
1467 _parent
._currentCompletion
= null;
1468 _result
.SetResult(null);
1471 _parent
.CloseInnerConnection();
1472 _parent
._currentCompletion
= null;
1473 _result
.SetException(ADP
.ExceptionWithStackTrace(ADP
.InternalError(ADP
.InternalErrorCode
.CompletedConnectReturnedPending
)));
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
);
1507 using (WindowsImpersonationContext context
= _impersonateIdentity
.Impersonate()) {
1508 return TryOpenInner(retry
);
1514 if (this.UsesIntegratedSecurity(connectionOptions
) || this.UsesActiveDirectoryIntegrated(connectionOptions
)) {
1515 _lastIdentity
= DbConnectionPoolIdentity
.GetCurrentWindowsIdentity();
1518 _lastIdentity
= null;
1520 return TryOpenInner(retry
);
1524 private bool TryOpenInner(TaskCompletionSource
<DbConnectionInternal
> retry
) {
1525 TdsParser bestEffortCleanupTarget
= null;
1526 RuntimeHelpers
.PrepareConstrainedRegions();
1529 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
1531 RuntimeHelpers
.PrepareConstrainedRegions();
1533 tdsReliabilitySection
.Start();
1537 if (ForceNewConnection
) {
1538 if (!InnerConnection
.TryReplaceConnection(this, ConnectionFactory
, retry
, UserConnectionOptions
)) {
1543 if (!InnerConnection
.TryOpenConnection(this, ConnectionFactory
, retry
, UserConnectionOptions
)) {
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();
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
;
1570 tdsInnerConnection
.Parser
.Statistics
= null;
1571 _statistics
= null; // in case of previous Open/Close/reset_CollectStats sequence
1578 tdsReliabilitySection
.Stop();
1582 catch (System
.OutOfMemoryException e
) {
1586 catch (System
.StackOverflowException e
) {
1590 catch (System
.Threading
.ThreadAbortException e
) {
1592 SqlInternalConnection
.BestEffortCleanup(bestEffortCleanupTarget
);
1601 // INTERNAL PROPERTIES
1604 internal bool HasLocalTransaction
{
1606 return GetOpenConnection().HasLocalTransaction
;
1610 internal bool HasLocalTransactionFromAPI
{
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
{
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
{
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
{
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
{
1649 SqlInternalConnectionTds tdsConnection
= (GetOpenConnection() as SqlInternalConnectionTds
);
1650 if (null == tdsConnection
) {
1651 throw SQL
.NotAvailableOnContextConnection();
1653 return tdsConnection
.Parser
;
1657 internal bool Asynchronous
{
1659 SqlConnectionString constr
= (SqlConnectionString
)ConnectionOptions
;
1660 return ((null != constr
) ? constr
.Asynchronous
: SqlConnectionString
.DEFAULT
.Asynchronous
);
1668 internal void ValidateConnectionForExecute(string method
, SqlCommand command
) {
1669 Task asyncWaitingForReconnection
=_asyncWaitingForReconnection
;
1670 if (asyncWaitingForReconnection
!=null) {
1671 if (!asyncWaitingForReconnection
.IsCompleted
) {
1672 throw SQL
.MARSUnspportedOnConnection();
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
);
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
);
1725 wrapCloseInAction(closeAction
);
1728 Bid
.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID
);
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.
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
));
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;
1762 new SecurityPermission(SecurityPermissionFlag
.UnmanagedCode
).Demand(); // MDAC 66682, 69017
1765 catch (SecurityException e
) {
1766 ADP
.TraceExceptionWithoutRethrow(e
);
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
) {
1812 OnInfoMessage(imevent
, out notified
);
1815 internal void OnInfoMessage(SqlInfoMessageEventArgs imevent
, out bool notified
) {
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
) {
1824 handler(this, imevent
);
1826 catch (Exception e
) { // MDAC 53175
1827 if (!ADP
.IsCatchableOrSecurityExceptionType(e
)) {
1831 ADP
.TraceExceptionWithoutRethrow(e
);
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() {
1849 uint pid
= (uint)SafeNativeMethods
.GetCurrentProcessId();
1853 // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
1854 if (ADP
.IsPlatformNT5
) {
1855 mapFileName
= "Global\\" + TdsEnums
.SDCI_MAPFILENAME
;
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
;
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
1881 // now set our global state
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() {
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.
1913 if (sdc
.fOption
/*TdsEnums.SQLDEBUG_ON*/) {
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
1928 // be sure to pick up thread context switch, especially the first time through
1930 if (!sdc
.fOption
/*TdsEnums.SQLDEBUG_OFF*/) {
1931 // turn off and free the memory
1933 // okay if we throw out here, no state to clean up
1934 IssueSQLDebug(TdsEnums
.SQLDEBUG_OFF
, null, 0, 0, null, null);
1937 // notify server of context change
1938 if (sdc
.tid
!= tid
) {
1941 IssueSQLDebug(TdsEnums
.SQLDEBUG_CONTEXT
, null, sdc
.pid
, sdc
.tid
, null, null);
1952 private void IssueSQLDebug(uint option
, string machineName
, uint pid
, uint id
, string sdiDllName
, byte[] data
) {
1954 if (GetOpenConnection().IsYukonOrNewer
) {
1961 SqlCommand c
= new SqlCommand(TdsEnums
.SP_SDIDEBUG
, this);
1962 c
.CommandType
= CommandType
.StoredProcedure
;
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
) {
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
) {
1982 p
= new SqlParameter(null, SqlDbType
.Int
);
1984 c
.Parameters
.Add(p
);
1986 p
= new SqlParameter(null, SqlDbType
.Int
);
1988 c
.Parameters
.Add(p
);
1991 if (option
== TdsEnums
.SQLDEBUG_ON
) {
1993 p
= new SqlParameter(null, SqlDbType
.VarBinary
, (null != data
) ? data
.Length
: 0);
1995 c
.Parameters
.Add(p
);
1998 c
.ExecuteNonQuery();
2002 public static void ChangePassword(string connectionString
, string newPassword
) {
2004 Bid
.ScopeEnter(out hscp
, "<sc.SqlConnection.ChangePassword|API>") ;
2005 Bid
.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
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);
2037 Bid
.ScopeLeave(ref hscp
) ;
2041 public static void ChangePassword(string connectionString
, SqlCredential credential
, SecureString newSecurePassword
) {
2043 Bid
.ScopeEnter(out hscp
, "<sc.SqlConnection.ChangePassword|API>") ;
2044 Bid
.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
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
);
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);
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
) {
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
) {
2157 return Statistics
.GetHashtable();
2160 return new SqlStatistics().GetHashtable();
2164 private void UpdateStatistics() {
2165 if (ConnectionState
.Open
== State
) {
2167 ADP
.TimerCurrent(out _statistics
._closeTimestamp
);
2169 // delegate the rest of the work to the SqlStatistics class
2170 Statistics
.UpdateStatistics();
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) {
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
;
2189 return Assembly
.Load(asmRef
);
2191 catch (Exception e
) {
2192 if (throwOnError
|| !ADP
.IsCatchableExceptionType(e
)) {
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.
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
;
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);
2233 MemoryStream stm
= new MemoryStream((byte[]) value);
2235 o
= SerializationHelperSql9
.Deserialize(stm
, metaData
.udtType
);
2237 Debug
.Assert(o
!= null, "object could NOT be created");
2242 internal byte[] GetBytes(object o
) {
2243 Microsoft
.SqlServer
.Server
.Format format
= Microsoft
.SqlServer
.Server
.Format
.Native
;
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");
2259 using (MemoryStream stm
= new MemoryStream(maxSize
< 0 ? 0 : maxSize
)) {
2260 SerializationHelperSql9
.Serialize(stm
, o
);
2261 retval
= stm
.ToArray();
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
{
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
;
2318 bool status
= false;
2320 pNtAuthority
= Marshal
.AllocHGlobal(sizeofSID_IDENTIFIER_AUTHORITY
);
2321 if (pNtAuthority
== IntPtr
.Zero
)
2323 Marshal
.WriteInt32(pNtAuthority
, 0, 0);
2324 Marshal
.WriteByte(pNtAuthority
, 4, 0);
2325 Marshal
.WriteByte(pNtAuthority
, 5, SECURITY_NT_AUTHORITY
);
2328 NativeMethods
.AllocateAndInitializeSid(
2331 SECURITY_AUTHENTICATED_USER_RID
,
2341 if (!status
|| pUserSid
== IntPtr
.Zero
) {
2345 NativeMethods
.AllocateAndInitializeSid(
2348 SECURITY_BUILTIN_DOMAIN_RID
,
2349 DOMAIN_ALIAS_RID_ADMINS
,
2358 if (!status
|| pAdminSid
== IntPtr
.Zero
) {
2362 pSecurityDescriptor
= Marshal
.AllocHGlobal(sizeofSECURITY_DESCRIPTOR
);
2363 if (pSecurityDescriptor
== IntPtr
.Zero
) {
2366 for (int i
= 0; i
< sizeofSECURITY_DESCRIPTOR
; i
++)
2367 Marshal
.WriteByte(pSecurityDescriptor
, i
, (byte)0);
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
) {
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)) {
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
);
2397 return pSecurityDescriptor
;
2399 if (pSecurityDescriptor
!= IntPtr
.Zero
) {
2400 Marshal
.FreeHGlobal(pSecurityDescriptor
);
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
)
2423 if (pszMachineName
.Length
> TdsEnums
.SDCI_MAX_MACHINENAME
||
2424 pszSDIDLLName
.Length
> TdsEnums
.SDCI_MAX_DLLNAME
)
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
)
2437 // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
2438 if (ADP
.IsPlatformNT5
) {
2439 mapFileName
= "Global\\" + TdsEnums
.SDCI_MAPFILENAME
;
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
))
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
)),
2464 if (IntPtr
.Zero
== hFileMap
) {
2469 pMemMap
= NativeMethods
.MapViewOfFile(hFileMap
, 0x6/*FILE_MAP_READ|FILE_MAP_WRITE*/, 0, 0, IntPtr
.Zero
);
2471 if (IntPtr
.Zero
== pMemMap
) {
2475 // copy data to memory-mapped file
2476 // layout of MEMMAP structure is:
2479 // byte[32] machineName
2480 // byte[16] sdiDllName
2484 Marshal
.WriteInt32(pMemMap
, offset
, (int)dwpidDebugger
);
2486 Marshal
.WriteInt32(pMemMap
, offset
, (int)dwOption
);
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
);
2494 if (null != rgbData
) {
2495 Marshal
.Copy(rgbData
, 0, ADP
.IntPtrOffset(pMemMap
, offset
), (int)cbData
);
2497 NativeMethods
.UnmapViewOfFile(pMemMap
);
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
);
2514 // this is a private interface to com+ users
2515 // do not change this guid
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")]
2529 [MarshalAs(UnmanagedType
.LPStr
)] string pszMachineName
,
2530 [MarshalAs(UnmanagedType
.LPStr
)] string pszSDIDLLName
,
2536 sealed class SqlDebugContext
: IDisposable
{
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() {
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
) {
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
;
2574 ~
SqlDebugContext() {
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