1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</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 /// This function should only be called once in an app. This does shallow copying of the dictionary so that
94 /// the app cannot alter the custom provider list once it has been set.
98 /// Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
99 /// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
100 /// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
101 /// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
103 /// <param name="customProviders">Custom column encryption key provider dictionary</param>
104 static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders)
107 // Return when the provided dictionary is null.
108 if (customProviders == null)
110 throw SQL.NullCustomKeyStoreProviderDictionary();
113 // Validate that custom provider list doesn't contain any of system provider list
114 foreach (string key in customProviders.Keys)
116 // Validate the provider name
118 // Check for null or empty
119 if (string.IsNullOrWhiteSpace(key))
121 throw SQL.EmptyProviderName();
124 // Check if the name starts with MSSQL_, since this is reserved namespace for system providers.
125 if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase))
127 throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix);
130 // Validate the provider value
131 if (customProviders[key] == null)
133 throw SQL.NullProviderValue(key);
137 lock (_CustomColumnEncryptionKeyProvidersLock)
139 // Provider list can only be set once
140 if (_CustomColumnEncryptionKeyStoreProviders != null)
142 throw SQL.CanOnlyCallOnce();
145 // Create a temporary dictionary and then add items from the provided dictionary.
146 // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs
147 // in the provided customerProviders dictionary.
148 Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customColumnEncryptionKeyStoreProviders =
149 new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(customProviders, StringComparer.OrdinalIgnoreCase);
151 // Set the dictionary to the ReadOnly dictionary.
152 _CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider>(customColumnEncryptionKeyStoreProviders);
157 /// This function walks through both system and custom column encryption key store providers and returns an object if found.
159 /// <param name="providerName">Provider Name to be searched in System Provider diction and Custom provider dictionary.</param>
160 /// <param name="columnKeyStoreProvider">If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance.</param>
161 /// <returns>true if the provider is found, else returns false</returns>
162 static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) {
163 Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid");
165 // Initialize the out parameter
166 columnKeyStoreProvider = null;
168 // Search in the sytem provider list.
169 if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
174 lock (_CustomColumnEncryptionKeyProvidersLock)
176 // If custom provider is not set, then return false
177 if (_CustomColumnEncryptionKeyStoreProviders == null)
182 // Search in the custom provider list
183 return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
188 /// This function returns a list of system provider dictionary currently supported by this driver.
190 /// <returns>Combined list of provider names</returns>
191 static internal List<string> GetColumnEncryptionSystemKeyStoreProviders() {
192 HashSet<string> providerNames = new HashSet<string>(_SystemColumnEncryptionKeyStoreProviders.Keys);
193 return providerNames.ToList();
197 /// This function returns a list of custom provider dictionary currently registered.
199 /// <returns>Combined list of provider names</returns>
200 static internal List<string> GetColumnEncryptionCustomKeyStoreProviders() {
201 if(_CustomColumnEncryptionKeyStoreProviders != null)
203 HashSet<string> providerNames = new HashSet<string>(_CustomColumnEncryptionKeyStoreProviders.Keys);
204 return providerNames.ToList();
207 return new List<string>();
210 private SqlDebugContext _sdc; // SQL Debugging support
212 private bool _AsyncCommandInProgress;
214 // SQLStatistics support
215 internal SqlStatistics _statistics;
216 private bool _collectstats;
218 private bool _fireInfoMessageEventOnUserErrors; // False by default
220 // root task associated with current async invocation
221 Tuple<TaskCompletionSource<DbConnectionInternal>, Task> _currentCompletion;
223 private SqlCredential _credential; // SQL authentication password stored in SecureString
224 private string _connectionString;
225 private int _connectRetryCount;
227 private string _accessToken; // Access Token to be used for token based authententication
229 // connection resiliency
230 private object _reconnectLock = new object();
231 internal Task _currentReconnectionTask;
232 private Task _asyncWaitingForReconnection; // current async task waiting for reconnection in non-MARS connections
233 private Guid _originalConnectionId = Guid.Empty;
234 private CancellationTokenSource _reconnectionCancellationSource;
235 internal SessionData _recoverySessionData;
236 internal WindowsIdentity _lastIdentity;
237 internal WindowsIdentity _impersonateIdentity;
238 private int _reconnectCount;
240 // 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
241 // 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
242 // using SqlConnection.Open() method.
243 internal bool _applyTransientFaultHandling = false;
245 public SqlConnection(string connectionString) : this(connectionString, null) {
248 public SqlConnection(string connectionString, SqlCredential credential) : this() {
249 ConnectionString = connectionString; // setting connection string first so that ConnectionOption is available
250 if (credential != null)
252 // The following checks are necessary as setting Credential property will call CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential
253 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential it will throw InvalidOperationException rather than Arguemtn exception
254 // Need to call setter on Credential property rather than setting _credential directly as pool groups need to be checked
255 SqlConnectionString connectionOptions = (SqlConnectionString) ConnectionOptions;
256 if (UsesClearUserIdOrPassword(connectionOptions))
258 throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
261 if (UsesIntegratedSecurity(connectionOptions))
263 throw ADP.InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity();
266 if (UsesContextConnection(connectionOptions))
268 throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
271 if (UsesActiveDirectoryIntegrated(connectionOptions))
273 throw SQL.SettingCredentialWithIntegratedArgument();
276 Credential = credential;
279 // credential == null: we should not set "Credential" as this will do additional validation check and
280 // checking pool groups which is not necessary. All necessary operation is already done by calling "ConnectionString = connectionString"
281 CacheConnectionStringProperties();
284 private SqlConnection(SqlConnection connection) { // Clone
285 GC.SuppressFinalize(this);
286 CopyFrom(connection);
287 _connectionString = connection._connectionString;
288 if (connection._credential != null)
290 SecureString password = connection._credential.Password.Copy();
291 password.MakeReadOnly();
292 _credential = new SqlCredential(connection._credential.UserId, password);
294 _accessToken = connection._accessToken;
295 CacheConnectionStringProperties();
298 // This method will be called once connection string is set or changed.
299 private void CacheConnectionStringProperties() {
300 SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
301 if (connString != null) {
302 _connectRetryCount = connString.ConnectRetryCount;
310 // used to start/stop collection of statistics data and do verify the current state
312 // devnote: start/stop should not performed using a property since it requires execution of code
315 // set the internal flag (_statisticsEnabled) to true.
316 // Create a new SqlStatistics object if not already there.
317 // connect the parser to the object.
318 // if there is no parser at this time we need to connect it after creation.
323 ResCategoryAttribute(Res.DataCategory_Data),
324 ResDescriptionAttribute(Res.SqlConnection_StatisticsEnabled),
326 public bool StatisticsEnabled {
328 return (_collectstats);
331 if (IsContextConnection) {
333 throw SQL.NotAvailableOnContextConnection();
339 if (ConnectionState.Open == State) {
340 if (null == _statistics) {
341 _statistics = new SqlStatistics();
342 ADP.TimerCurrent(out _statistics._openTimestamp);
344 // set statistics on the parser
346 Debug.Assert(Parser != null, "Where's the parser?");
347 Parser.Statistics = _statistics;
352 if (null != _statistics) {
353 if (ConnectionState.Open == State) {
354 // remove statistics from parser
356 TdsParser parser = Parser;
357 Debug.Assert(parser != null, "Where's the parser?");
358 parser.Statistics = null;
359 ADP.TimerCurrent(out _statistics._closeTimestamp);
363 this._collectstats = value;
368 internal bool AsyncCommandInProgress {
370 return (_AsyncCommandInProgress);
372 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
374 _AsyncCommandInProgress = value;
378 internal bool IsContextConnection {
380 SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
381 return UsesContextConnection(opt);
386 /// Is this connection using column encryption ?
388 internal bool IsColumnEncryptionSettingEnabled {
390 SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
391 return opt != null ? opt.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled : false;
395 // Is this connection is a Context Connection?
396 private bool UsesContextConnection(SqlConnectionString opt)
398 return opt != null ? opt.ContextConnection : false;
401 private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt)
403 return opt != null ? opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated : false;
406 private bool UsesAuthentication(SqlConnectionString opt) {
407 return opt != null ? opt.Authentication != SqlAuthenticationMethod.NotSpecified : false;
410 // Does this connection uses Integrated Security?
411 private bool UsesIntegratedSecurity(SqlConnectionString opt) {
412 return opt != null ? opt.IntegratedSecurity : false;
415 // Does this connection uses old style of clear userID or Password in connection string?
416 private bool UsesClearUserIdOrPassword(SqlConnectionString opt) {
419 result = (!ADP.IsEmpty(opt.UserID) || !ADP.IsEmpty(opt.Password));
424 internal SqlConnectionString.TransactionBindingEnum TransactionBinding {
426 return ((SqlConnectionString)ConnectionOptions).TransactionBinding;
430 internal SqlConnectionString.TypeSystem TypeSystem {
432 return ((SqlConnectionString)ConnectionOptions).TypeSystemVersion;
436 internal Version TypeSystemAssemblyVersion {
438 return ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion;
442 internal int ConnectRetryInterval {
444 return ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval;
448 override protected DbProviderFactory DbProviderFactory {
450 return SqlClientFactory.Instance;
454 // AccessToken: To be used for token based authentication
457 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
458 ResDescriptionAttribute(Res.SqlConnection_AccessToken),
460 public string AccessToken {
462 string result = _accessToken;
463 // When a connection is connecting or is ever opened, make AccessToken available only if "Persist Security Info" is set to true
464 // otherwise, return null
465 SqlConnectionString connectionOptions = (SqlConnectionString)UserConnectionOptions;
466 if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo) {
473 // If a connection is connecting or is ever opened, AccessToken cannot be set
474 if (!InnerConnection.AllowSetConnectionString) {
475 throw ADP.OpenConnectionPropertySet("AccessToken", InnerConnection.State);
479 // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential
480 CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions);
483 _accessToken = value;
484 // Need to call ConnectionString_Set to do proper pool group check
485 ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: _accessToken));
491 #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
492 RecommendedAsConfigurable(true),
493 #pragma warning restore 618
494 SettingsBindableAttribute(true),
495 RefreshProperties(RefreshProperties.All),
496 ResCategoryAttribute(Res.DataCategory_Data),
497 Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
498 ResDescriptionAttribute(Res.SqlConnection_ConnectionString),
500 override public string ConnectionString {
502 return ConnectionString_Get();
505 if(_credential != null || _accessToken != null) {
506 SqlConnectionString connectionOptions = new SqlConnectionString(value);
507 if(_credential != null) {
508 // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
509 // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
510 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
511 if(UsesActiveDirectoryIntegrated(connectionOptions)) {
512 throw SQL.SettingIntegratedWithCredential();
515 CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
517 else if(_accessToken != null) {
518 CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions);
521 ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken));
522 _connectionString = value; // Change _connectionString value only after value is validated
523 CacheConnectionStringProperties();
528 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
529 ResDescriptionAttribute(Res.SqlConnection_ConnectionTimeout),
531 override public int ConnectionTimeout {
533 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
534 return ((null != constr) ? constr.ConnectTimeout : SqlConnectionString.DEFAULT.Connect_Timeout);
539 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
540 ResDescriptionAttribute(Res.SqlConnection_Database),
542 override public string Database {
543 // if the connection is open, we need to ask the inner connection what it's
544 // current catalog is because it may have gotten changed, otherwise we can
545 // just return what the connection string had.
547 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
550 if (null != innerConnection) {
551 result = innerConnection.CurrentDatabase;
554 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
555 result = ((null != constr) ? constr.InitialCatalog : SqlConnectionString.DEFAULT.Initial_Catalog);
563 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
564 ResDescriptionAttribute(Res.SqlConnection_DataSource),
566 override public string DataSource {
568 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
571 if (null != innerConnection) {
572 result = innerConnection.CurrentDataSource;
575 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
576 result = ((null != constr) ? constr.DataSource : SqlConnectionString.DEFAULT.Data_Source);
583 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
584 ResCategoryAttribute(Res.DataCategory_Data),
585 ResDescriptionAttribute(Res.SqlConnection_PacketSize),
587 public int PacketSize {
588 // if the connection is open, we need to ask the inner connection what it's
589 // current packet size is because it may have gotten changed, otherwise we
590 // can just return what the connection string had.
592 if (IsContextConnection) {
593 throw SQL.NotAvailableOnContextConnection();
596 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
599 if (null != innerConnection) {
600 result = innerConnection.PacketSize;
603 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
604 result = ((null != constr) ? constr.PacketSize : SqlConnectionString.DEFAULT.Packet_Size);
611 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
612 ResCategoryAttribute(Res.DataCategory_Data),
613 ResDescriptionAttribute(Res.SqlConnection_ClientConnectionId),
615 public Guid ClientConnectionId {
618 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
620 if (null != innerConnection) {
621 return innerConnection.ClientConnectionId;
624 Task reconnectTask = _currentReconnectionTask;
625 if (reconnectTask != null && !reconnectTask.IsCompleted) {
626 return _originalConnectionId;
635 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
636 ResDescriptionAttribute(Res.SqlConnection_ServerVersion),
638 override public string ServerVersion {
640 return GetOpenConnection().ServerVersion;
646 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
647 ResDescriptionAttribute(Res.DbConnection_State),
649 override public ConnectionState State {
651 Task reconnectTask=_currentReconnectionTask;
652 if (reconnectTask != null && !reconnectTask.IsCompleted) {
653 return ConnectionState.Open;
655 return InnerConnection.State;
660 internal SqlStatistics Statistics {
667 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
668 ResCategoryAttribute(Res.DataCategory_Data),
669 ResDescriptionAttribute(Res.SqlConnection_WorkstationId),
671 public string WorkstationId {
673 if (IsContextConnection) {
674 throw SQL.NotAvailableOnContextConnection();
677 // If not supplied by the user, the default value is the MachineName
678 // Note: In Longhorn you'll be able to rename a machine without
679 // rebooting. Therefore, don't cache this machine name.
680 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
681 string result = ((null != constr) ? constr.WorkstationId : null);
682 if (null == result) {
683 // getting machine name requires Environment.Permission
684 // user must have that permission in order to retrieve this
685 result = Environment.MachineName;
691 // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication
694 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
695 ResDescriptionAttribute(Res.SqlConnection_Credential),
697 public SqlCredential Credential
701 SqlCredential result = _credential;
703 // When a connection is connecting or is ever opened, make credential available only if "Persist Security Info" is set to true
704 // otherwise, return null
705 SqlConnectionString connectionOptions = (SqlConnectionString) UserConnectionOptions;
706 if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo)
716 // If a connection is connecting or is ever opened, user id/password cannot be set
717 if (!InnerConnection.AllowSetConnectionString)
719 throw ADP.OpenConnectionPropertySet("Credential", InnerConnection.State);
722 // check if the usage of credential has any conflict with the keys used in connection string
725 // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
726 // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
727 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
728 if (UsesActiveDirectoryIntegrated((SqlConnectionString) ConnectionOptions)) {
729 throw SQL.SettingCredentialWithIntegratedInvalid();
732 CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential((SqlConnectionString) ConnectionOptions);
733 if(_accessToken != null) {
734 throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
741 // Need to call ConnectionString_Set to do proper pool group check
742 ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken));
746 // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential: check if the usage of credential has any conflict
747 // with the keys used in connection string
748 // If there is any conflict, it throws InvalidOperationException
749 // This is to be used setter of ConnectionString and Credential properties
750 private void CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(SqlConnectionString connectionOptions)
752 if (UsesClearUserIdOrPassword(connectionOptions))
754 throw ADP.InvalidMixedUsageOfSecureAndClearCredential();
757 if (UsesIntegratedSecurity(connectionOptions))
759 throw ADP.InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity();
762 if (UsesContextConnection(connectionOptions))
764 throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
768 // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken: check if the usage of AccessToken has any conflict
769 // with the keys used in connection string and credential
770 // If there is any conflict, it throws InvalidOperationException
771 // This is to be used setter of ConnectionString and AccessToken properties
772 private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(SqlConnectionString connectionOptions) {
773 if (UsesClearUserIdOrPassword(connectionOptions)) {
774 throw ADP.InvalidMixedUsageOfAccessTokenAndUserIDPassword();
777 if (UsesIntegratedSecurity(connectionOptions)) {
778 throw ADP.InvalidMixedUsageOfAccessTokenAndIntegratedSecurity();
781 if (UsesContextConnection(connectionOptions)) {
782 throw ADP.InvalidMixedUsageOfAccessTokenAndContextConnection();
785 if (UsesAuthentication(connectionOptions)) {
786 throw ADP.InvalidMixedUsageOfAccessTokenAndAuthentication();
789 // Check if the usage of AccessToken has the conflict with credential
790 if (_credential != null) {
791 throw ADP.InvalidMixedUsageOfAccessTokenAndCredential();
800 ResCategoryAttribute(Res.DataCategory_InfoMessage),
801 ResDescriptionAttribute(Res.DbConnection_InfoMessage),
803 public event SqlInfoMessageEventHandler InfoMessage {
805 Events.AddHandler(EventInfoMessage, value);
808 Events.RemoveHandler(EventInfoMessage, value);
812 public bool FireInfoMessageEventOnUserErrors {
814 return _fireInfoMessageEventOnUserErrors;
817 _fireInfoMessageEventOnUserErrors = value;
821 // Approx. number of times that the internal connection has been reconnected
822 internal int ReconnectCount {
824 return _reconnectCount;
832 new public SqlTransaction BeginTransaction() {
833 // this is just a delegate. The actual method tracks executiontime
834 return BeginTransaction(IsolationLevel.Unspecified, null);
837 new public SqlTransaction BeginTransaction(IsolationLevel iso) {
838 // this is just a delegate. The actual method tracks executiontime
839 return BeginTransaction(iso, null);
842 public SqlTransaction BeginTransaction(string transactionName) {
843 // Use transaction names only on the outermost pair of nested
844 // BEGIN...COMMIT or BEGIN...ROLLBACK statements. Transaction names
845 // are ignored for nested BEGIN's. The only way to rollback a nested
846 // transaction is to have a save point from a SAVE TRANSACTION call.
847 return BeginTransaction(IsolationLevel.Unspecified, transactionName);
850 // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
851 [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
852 override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
855 Bid.ScopeEnter(out hscp, "<prov.SqlConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
858 DbTransaction transaction = BeginTransaction(isolationLevel);
860 // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and
861 // subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction
862 // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
863 // until the completion of BeginTransaction with KeepAlive
869 Bid.ScopeLeave(ref hscp);
873 public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) {
874 WaitForPendingReconnection();
875 SqlStatistics statistics = null;
877 string xactName = ADP.IsEmpty(transactionName)? "None" : transactionName;
878 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", ObjectID, (int)iso,
882 statistics = SqlStatistics.StartTimer(Statistics);
884 // NOTE: we used to throw an exception if the transaction name was empty
885 // (see MDAC 50292) but that was incorrect because we have a BeginTransaction
886 // method that doesn't have a transactionName argument.
887 SqlTransaction transaction;
888 bool isFirstAttempt = true;
890 transaction = GetOpenConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice
891 Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt");
892 isFirstAttempt = false;
893 } while (transaction.InternalTransaction.ConnectionHasBeenRestored);
896 // SQLBU 503873 The GetOpenConnection line above doesn't keep a ref on the outer connection (this),
897 // and it could be collected before the inner connection can hook it to the transaction, resulting in
898 // a transaction with a null connection property. Use GC.KeepAlive to ensure this doesn't happen.
904 Bid.ScopeLeave(ref hscp);
905 SqlStatistics.StopTimer(statistics);
909 override public void ChangeDatabase(string database) {
910 SqlStatistics statistics = null;
911 RepairInnerConnection();
912 Bid.CorrelationTrace("<sc.SqlConnection.ChangeDatabase|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
913 TdsParser bestEffortCleanupTarget = null;
914 RuntimeHelpers.PrepareConstrainedRegions();
917 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
919 RuntimeHelpers.PrepareConstrainedRegions();
921 tdsReliabilitySection.Start();
925 bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
926 statistics = SqlStatistics.StartTimer(Statistics);
927 InnerConnection.ChangeDatabase(database);
931 tdsReliabilitySection.Stop();
935 catch (System.OutOfMemoryException e) {
939 catch (System.StackOverflowException e) {
943 catch (System.Threading.ThreadAbortException e) {
945 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
949 SqlStatistics.StopTimer(statistics);
953 static public void ClearAllPools() {
954 (new SqlClientPermission(PermissionState.Unrestricted)).Demand();
955 SqlConnectionFactory.SingletonInstance.ClearAllPools();
958 static public void ClearPool(SqlConnection connection) {
959 ADP.CheckArgumentNull(connection, "connection");
961 DbConnectionOptions connectionOptions = connection.UserConnectionOptions;
962 if (null != connectionOptions) {
963 connectionOptions.DemandPermission();
964 if (connection.IsContextConnection) {
965 throw SQL.NotAvailableOnContextConnection();
967 SqlConnectionFactory.SingletonInstance.ClearPool(connection);
971 object ICloneable.Clone() {
972 SqlConnection clone = new SqlConnection(this);
973 Bid.Trace("<sc.SqlConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
977 void CloseInnerConnection() {
978 // CloseConnection() now handles the lock
980 // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and
981 // the command will no longer be cancelable. It might be desirable to be able to cancel the close opperation, but this is
982 // outside of the scope of Whidbey RTM. See (SqlCommand::Cancel) for other lock.
983 InnerConnection.CloseConnection(this, ConnectionFactory);
986 override public void Close() {
988 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
989 Bid.CorrelationTrace("<sc.SqlConnection.Close|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
991 SqlStatistics statistics = null;
993 TdsParser bestEffortCleanupTarget = null;
994 RuntimeHelpers.PrepareConstrainedRegions();
997 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
999 RuntimeHelpers.PrepareConstrainedRegions();
1001 tdsReliabilitySection.Start();
1005 bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
1006 statistics = SqlStatistics.StartTimer(Statistics);
1008 Task reconnectTask = _currentReconnectionTask;
1009 if (reconnectTask != null && !reconnectTask.IsCompleted) {
1010 CancellationTokenSource cts = _reconnectionCancellationSource;
1014 AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection
1015 if (State != ConnectionState.Open) {// if we cancelled before the connection was opened
1016 OnStateChange(DbConnectionInternal.StateChangeClosed);
1019 CancelOpenAndWait();
1020 CloseInnerConnection();
1021 GC.SuppressFinalize(this);
1023 if (null != Statistics) {
1024 ADP.TimerCurrent(out _statistics._closeTimestamp);
1029 tdsReliabilitySection.Stop();
1033 catch (System.OutOfMemoryException e) {
1037 catch (System.StackOverflowException e) {
1041 catch (System.Threading.ThreadAbortException e) {
1043 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1047 SqlStatistics.StopTimer(statistics);
1051 SqlDebugContext sdc = _sdc;
1053 Bid.ScopeLeave(ref hscp);
1060 new public SqlCommand CreateCommand() {
1061 return new SqlCommand(null, this);
1064 private void DisposeMe(bool disposing) { // MDAC 65459
1065 // clear credential and AccessToken here rather than in IDisposable.Dispose as these are specific to SqlConnection only
1066 // IDisposable.Dispose is generated code from a template and used by other providers as well
1068 _accessToken = null;
1071 // DevDiv2 Bug 457934:SQLConnection leaks when not disposed
1072 // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/457934
1073 // 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
1074 // For pooled connections, we will rely on the pool reclaiming the connection
1075 var innerConnection = (InnerConnection as SqlInternalConnectionTds);
1076 if ((innerConnection != null) && (!innerConnection.ConnectionOptions.Pooling)) {
1077 var parser = innerConnection.Parser;
1078 if ((parser != null) && (parser._physicalStateObj != null)) {
1079 parser._physicalStateObj.DecrementPendingCallbacks(release: false);
1086 public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) {
1087 if (IsContextConnection) {
1088 throw SQL.NotAvailableOnContextConnection();
1091 EnlistDistributedTransactionHelper(transaction);
1095 override public void Open() {
1097 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Open|API> %d#", ObjectID) ;
1098 Bid.CorrelationTrace("<sc.SqlConnection.Open|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1101 if (StatisticsEnabled) {
1102 if (null == _statistics) {
1103 _statistics = new SqlStatistics();
1106 _statistics.ContinueOnNewConnection();
1110 SqlStatistics statistics = null;
1111 RuntimeHelpers.PrepareConstrainedRegions();
1113 statistics = SqlStatistics.StartTimer(Statistics);
1115 if (!TryOpen(null)) {
1116 throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
1120 SqlStatistics.StopTimer(statistics);
1124 Bid.ScopeLeave(ref hscp);
1128 internal void RegisterWaitingForReconnect(Task waitingTask) {
1129 if (((SqlConnectionString)ConnectionOptions).MARS) {
1132 Interlocked.CompareExchange(ref _asyncWaitingForReconnection, waitingTask, null);
1133 if (_asyncWaitingForReconnection != waitingTask) { // somebody else managed to register
1134 throw SQL.MARSUnspportedOnConnection();
1138 private async Task ReconnectAsync(int timeout) {
1140 long commandTimeoutExpiration = 0;
1142 commandTimeoutExpiration = ADP.TimerCurrent() + ADP.TimerFromSeconds(timeout);
1144 CancellationTokenSource cts = new CancellationTokenSource();
1145 _reconnectionCancellationSource = cts;
1146 CancellationToken ctoken = cts.Token;
1147 int retryCount = _connectRetryCount; // take a snapshot: could be changed by modifying the connection string
1148 for (int attempt = 0; attempt < retryCount; attempt++) {
1149 if (ctoken.IsCancellationRequested) {
1150 Bid.Trace("<sc.SqlConnection.ReconnectAsync|INFO> Orginal ClientConnectionID %ls - reconnection cancelled\n", _originalConnectionId.ToString());
1154 _impersonateIdentity = _lastIdentity;
1156 ForceNewConnection = true;
1157 await OpenAsync(ctoken).ConfigureAwait(false);
1158 // On success, increment the reconnect count - we don't really care if it rolls over since it is approx.
1159 _reconnectCount = unchecked(_reconnectCount + 1);
1161 Debug.Assert(_recoverySessionData._debugReconnectDataApplied, "Reconnect data was not applied !");
1165 _impersonateIdentity = null;
1166 ForceNewConnection = false;
1168 Bid.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Reconnection suceeded. ClientConnectionID %ls -> %ls \n", _originalConnectionId.ToString(), ClientConnectionId.ToString());
1171 catch (SqlException e) {
1172 Bid.Trace("<sc.SqlConnection.ReconnectAsyncINFO> Orginal ClientConnectionID %ls - reconnection attempt failed error %ls\n", _originalConnectionId.ToString(), e.Message);
1173 if (attempt == retryCount - 1) {
1174 Bid.Trace("<sc.SqlConnection.ReconnectAsync|INFO> Orginal ClientConnectionID %ls - give up reconnection\n", _originalConnectionId.ToString());
1175 throw SQL.CR_AllAttemptsFailed(e, _originalConnectionId);
1177 if (timeout > 0 && ADP.TimerRemaining(commandTimeoutExpiration) < ADP.TimerFromSeconds(ConnectRetryInterval)) {
1178 throw SQL.CR_NextAttemptWillExceedQueryTimeout(e, _originalConnectionId);
1181 await Task.Delay(1000 * ConnectRetryInterval, ctoken).ConfigureAwait(false);
1185 _recoverySessionData = null;
1186 _supressStateChangeForReconnection = false;
1188 Debug.Assert(false, "Should not reach this point");
1191 internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) {
1192 Task runningReconnect = _currentReconnectionTask;
1193 // This loop in the end will return not completed reconnect task or null
1194 while (runningReconnect != null && runningReconnect.IsCompleted) {
1195 // clean current reconnect task (if it is the same one we checked
1196 Interlocked.CompareExchange<Task>(ref _currentReconnectionTask, null, runningReconnect);
1197 // make sure nobody started new task (if which case we did not clean it)
1198 runningReconnect = _currentReconnectionTask;
1200 if (runningReconnect == null) {
1201 if (_connectRetryCount > 0) {
1202 SqlInternalConnectionTds tdsConn = GetOpenTdsConnection();
1203 if (tdsConn._sessionRecoveryAcknowledged) {
1204 TdsParserStateObject stateObj = tdsConn.Parser._physicalStateObj;
1205 if (!stateObj.ValidateSNIConnection()) {
1206 if (tdsConn.Parser._sessionPool != null) {
1207 if (tdsConn.Parser._sessionPool.ActiveSessionsCount > 0) {
1209 if (beforeDisconnect != null) {
1212 OnError(SQL.CR_UnrecoverableClient(ClientConnectionId), true, null);
1215 SessionData cData = tdsConn.CurrentSessionData;
1216 cData.AssertUnrecoverableStateCountIsCorrect();
1217 if (cData._unrecoverableStatesCount == 0) {
1218 bool callDisconnect = false;
1219 lock (_reconnectLock) {
1220 tdsConn.CheckEnlistedTransactionBinding();
1221 runningReconnect = _currentReconnectionTask; // double check after obtaining the lock
1222 if (runningReconnect == null) {
1223 if (cData._unrecoverableStatesCount == 0) { // could change since the first check, but now is stable since connection is know to be broken
1224 _originalConnectionId = ClientConnectionId;
1225 Bid.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Connection ClientConnectionID %ls is invalid, reconnecting\n", _originalConnectionId.ToString());
1226 _recoverySessionData = cData;
1227 if (beforeDisconnect != null) {
1231 _supressStateChangeForReconnection = true;
1232 tdsConn.DoomThisConnection();
1234 catch (SqlException) {
1236 runningReconnect = Task.Run(() => ReconnectAsync(timeout));
1237 // if current reconnect is not null, somebody already started reconnection task - some kind of race condition
1238 Debug.Assert(_currentReconnectionTask == null, "Duplicate reconnection tasks detected");
1239 _currentReconnectionTask = runningReconnect;
1243 callDisconnect = true;
1246 if (callDisconnect && beforeDisconnect != null) {
1251 if (beforeDisconnect != null) {
1254 OnError(SQL.CR_UnrecoverableServer(ClientConnectionId), true, null);
1256 } // ValidateSNIConnection
1257 } // sessionRecoverySupported
1258 } // connectRetryCount>0
1260 else { // runningReconnect = null
1261 if (beforeDisconnect != null) {
1265 return runningReconnect;
1268 // this is straightforward, but expensive method to do connection resiliency - it take locks and all prepartions as for TDS request
1269 partial void RepairInnerConnection() {
1270 WaitForPendingReconnection();
1271 if (_connectRetryCount == 0) {
1274 SqlInternalConnectionTds tdsConn = InnerConnection as SqlInternalConnectionTds;
1275 if (tdsConn != null) {
1276 tdsConn.ValidateConnectionForExecute(null);
1277 tdsConn.GetSessionAndReconnectIfNeeded((SqlConnection)this);
1281 private void WaitForPendingReconnection() {
1282 Task reconnectTask = _currentReconnectionTask;
1283 if (reconnectTask != null && !reconnectTask.IsCompleted) {
1284 AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false);
1288 void CancelOpenAndWait()
1290 // copy from member to avoid changes by background thread
1291 var completion = _currentCompletion;
1292 if (completion != null)
1294 completion.Item1.TrySetCanceled();
1295 ((IAsyncResult)completion.Item2).AsyncWaitHandle.WaitOne();
1297 Debug.Assert(_currentCompletion == null, "After waiting for an async call to complete, there should be no completion source");
1300 public override Task OpenAsync(CancellationToken cancellationToken) {
1302 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.OpenAsync|API> %d#", ObjectID) ;
1303 Bid.CorrelationTrace("<sc.SqlConnection.OpenAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1306 if (StatisticsEnabled) {
1307 if (null == _statistics) {
1308 _statistics = new SqlStatistics();
1311 _statistics.ContinueOnNewConnection();
1315 SqlStatistics statistics = null;
1316 RuntimeHelpers.PrepareConstrainedRegions();
1318 statistics = SqlStatistics.StartTimer(Statistics);
1320 System.Transactions.Transaction transaction = ADP.GetCurrentTransaction();
1321 TaskCompletionSource<DbConnectionInternal> completion = new TaskCompletionSource<DbConnectionInternal>(transaction);
1322 TaskCompletionSource<object> result = new TaskCompletionSource<object>();
1324 if (cancellationToken.IsCancellationRequested) {
1325 result.SetCanceled();
1329 if (IsContextConnection) {
1330 // Async not supported on Context Connections
1331 result.SetException(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
1338 completed = TryOpen(completion);
1340 catch (Exception e) {
1341 result.SetException(e);
1346 result.SetResult(null);
1349 CancellationTokenRegistration registration = new CancellationTokenRegistration();
1350 if (cancellationToken.CanBeCanceled) {
1351 registration = cancellationToken.Register(() => completion.TrySetCanceled());
1353 OpenAsyncRetry retry = new OpenAsyncRetry(this, completion, result, registration);
1354 _currentCompletion = new Tuple<TaskCompletionSource<DbConnectionInternal>, Task>(completion, result.Task);
1355 completion.Task.ContinueWith(retry.Retry, TaskScheduler.Default);
1362 SqlStatistics.StopTimer(statistics);
1366 Bid.ScopeLeave(ref hscp);
1370 private class OpenAsyncRetry {
1371 SqlConnection _parent;
1372 TaskCompletionSource<DbConnectionInternal> _retry;
1373 TaskCompletionSource<object> _result;
1374 CancellationTokenRegistration _registration;
1376 public OpenAsyncRetry(SqlConnection parent, TaskCompletionSource<DbConnectionInternal> retry, TaskCompletionSource<object> result, CancellationTokenRegistration registration) {
1380 _registration = registration;
1383 internal void Retry(Task<DbConnectionInternal> retryTask) {
1384 Bid.Trace("<sc.SqlConnection.OpenAsyncRetry|Info> %d#\n", _parent.ObjectID);
1385 _registration.Dispose();
1387 SqlStatistics statistics = null;
1388 RuntimeHelpers.PrepareConstrainedRegions();
1390 statistics = SqlStatistics.StartTimer(_parent.Statistics);
1392 if (retryTask.IsFaulted) {
1393 Exception e = retryTask.Exception.InnerException;
1394 _parent.CloseInnerConnection();
1395 _parent._currentCompletion = null;
1396 _result.SetException(retryTask.Exception.InnerException);
1398 else if (retryTask.IsCanceled) {
1399 _parent.CloseInnerConnection();
1400 _parent._currentCompletion = null;
1401 _result.SetCanceled();
1405 // protect continuation from ----s with close and cancel
1406 lock (_parent.InnerConnection) {
1407 result = _parent.TryOpen(_retry);
1411 _parent._currentCompletion = null;
1412 _result.SetResult(null);
1415 _parent.CloseInnerConnection();
1416 _parent._currentCompletion = null;
1417 _result.SetException(ADP.ExceptionWithStackTrace(ADP.InternalError(ADP.InternalErrorCode.CompletedConnectReturnedPending)));
1422 SqlStatistics.StopTimer(statistics);
1425 catch (Exception e) {
1426 _parent.CloseInnerConnection();
1427 _parent._currentCompletion = null;
1428 _result.SetException(e);
1433 private bool TryOpen(TaskCompletionSource<DbConnectionInternal> retry) {
1434 SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions;
1436 _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0 );
1438 if (connectionOptions != null &&
1439 (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword) &&
1440 (!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) &&
1441 _credential == null) {
1442 throw SQL.CredentialsNotProvided(connectionOptions.Authentication);
1445 if (_impersonateIdentity != null) {
1446 if (_impersonateIdentity.User == DbConnectionPoolIdentity.GetCurrentWindowsIdentity().User) {
1447 return TryOpenInner(retry);
1450 using (WindowsImpersonationContext context = _impersonateIdentity.Impersonate()) {
1451 return TryOpenInner(retry);
1456 if (this.UsesIntegratedSecurity(connectionOptions) || this.UsesActiveDirectoryIntegrated(connectionOptions)) {
1457 _lastIdentity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity();
1460 _lastIdentity = null;
1462 return TryOpenInner(retry);
1466 private bool TryOpenInner(TaskCompletionSource<DbConnectionInternal> retry) {
1467 TdsParser bestEffortCleanupTarget = null;
1468 RuntimeHelpers.PrepareConstrainedRegions();
1471 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1473 RuntimeHelpers.PrepareConstrainedRegions();
1475 tdsReliabilitySection.Start();
1479 if (ForceNewConnection) {
1480 if (!InnerConnection.TryReplaceConnection(this, ConnectionFactory, retry, UserConnectionOptions)) {
1485 if (!InnerConnection.TryOpenConnection(this, ConnectionFactory, retry, UserConnectionOptions)) {
1489 // does not require GC.KeepAlive(this) because of OnStateChange
1491 // GetBestEffortCleanup must happen AFTER OpenConnection to get the correct target.
1492 bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
1494 var tdsInnerConnection = (InnerConnection as SqlInternalConnectionTds);
1495 if (tdsInnerConnection == null) {
1496 SqlInternalConnectionSmi innerConnection = (InnerConnection as SqlInternalConnectionSmi);
1497 innerConnection.AutomaticEnlistment();
1500 Debug.Assert(tdsInnerConnection.Parser != null, "Where's the parser?");
1502 if (!tdsInnerConnection.ConnectionOptions.Pooling) {
1503 // For non-pooled connections, we need to make sure that the finalizer does actually run to avoid leaking SNI handles
1504 GC.ReRegisterForFinalize(this);
1507 if (StatisticsEnabled) {
1508 ADP.TimerCurrent(out _statistics._openTimestamp);
1509 tdsInnerConnection.Parser.Statistics = _statistics;
1512 tdsInnerConnection.Parser.Statistics = null;
1513 _statistics = null; // in case of previous Open/Close/reset_CollectStats sequence
1520 tdsReliabilitySection.Stop();
1524 catch (System.OutOfMemoryException e) {
1528 catch (System.StackOverflowException e) {
1532 catch (System.Threading.ThreadAbortException e) {
1534 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1543 // INTERNAL PROPERTIES
1546 internal bool HasLocalTransaction {
1548 return GetOpenConnection().HasLocalTransaction;
1552 internal bool HasLocalTransactionFromAPI {
1554 Task reconnectTask = _currentReconnectionTask;
1555 if (reconnectTask != null && !reconnectTask.IsCompleted) {
1556 return false; //we will not go into reconnection if we are inside the transaction
1558 return GetOpenConnection().HasLocalTransactionFromAPI;
1562 internal bool IsShiloh {
1564 if (_currentReconnectionTask != null) { // holds true even if task is completed
1565 return true; // if CR is enabled, connection, if established, will be Katmai+
1567 return GetOpenConnection().IsShiloh;
1571 internal bool IsYukonOrNewer {
1573 if (_currentReconnectionTask != null) { // holds true even if task is completed
1574 return true; // if CR is enabled, connection, if established, will be Katmai+
1576 return GetOpenConnection().IsYukonOrNewer;
1580 internal bool IsKatmaiOrNewer {
1582 if (_currentReconnectionTask != null) { // holds true even if task is completed
1583 return true; // if CR is enabled, connection, if established, will be Katmai+
1585 return GetOpenConnection().IsKatmaiOrNewer;
1589 internal TdsParser Parser {
1591 SqlInternalConnectionTds tdsConnection = (GetOpenConnection() as SqlInternalConnectionTds);
1592 if (null == tdsConnection) {
1593 throw SQL.NotAvailableOnContextConnection();
1595 return tdsConnection.Parser;
1599 internal bool Asynchronous {
1601 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
1602 return ((null != constr) ? constr.Asynchronous : SqlConnectionString.DEFAULT.Asynchronous);
1610 internal void ValidateConnectionForExecute(string method, SqlCommand command) {
1611 Task asyncWaitingForReconnection=_asyncWaitingForReconnection;
1612 if (asyncWaitingForReconnection!=null) {
1613 if (!asyncWaitingForReconnection.IsCompleted) {
1614 throw SQL.MARSUnspportedOnConnection();
1617 Interlocked.CompareExchange(ref _asyncWaitingForReconnection, null, asyncWaitingForReconnection);
1620 if (_currentReconnectionTask != null) {
1621 Task currentReconnectionTask = _currentReconnectionTask;
1622 if (currentReconnectionTask != null && !currentReconnectionTask.IsCompleted) {
1623 return; // execution will wait for this task later
1626 SqlInternalConnection innerConnection = GetOpenConnection(method);
1627 innerConnection.ValidateConnectionForExecute(command);
1630 // Surround name in brackets and then escape any end bracket to protect against SQL Injection.
1631 // NOTE: if the user escapes it themselves it will not work, but this was the case in V1 as well
1632 // as native OleDb and Odbc.
1633 static internal string FixupDatabaseTransactionName(string name) {
1634 if (!ADP.IsEmpty(name)) {
1635 return SqlServerEscapeHelper.EscapeIdentifier(name);
1642 // If wrapCloseInAction is defined, then the action it defines will be run with the connection close action passed in as a parameter
1643 // The close action also supports being run asynchronously
1644 internal void OnError(SqlException exception, bool breakConnection, Action<Action> wrapCloseInAction) {
1645 Debug.Assert(exception != null && exception.Errors.Count != 0, "SqlConnection: OnError called with null or empty exception!");
1647 // Bug fix - MDAC 49022 - connection open after failure... Problem was parser was passing
1648 // Open as a state - because the parser's connection to the netlib was open. We would
1649 // then set the connection state to the parser's state - which is not correct. The only
1650 // time the connection state should change to what is passed in to this function is if
1651 // the parser is broken, then we should be closed. Changed to passing in
1652 // TdsParserState, not ConnectionState.
1655 if (breakConnection && (ConnectionState.Open == State)) {
1657 if (wrapCloseInAction != null) {
1658 int capturedCloseCount = _closeCount;
1660 Action closeAction = () => {
1661 if (capturedCloseCount == _closeCount) {
1662 Bid.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID);
1667 wrapCloseInAction(closeAction);
1670 Bid.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID);
1675 if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) {
1676 // It is an error, and should be thrown. Class of TdsEnums.MIN_ERROR_CLASS or above is an error,
1677 // below TdsEnums.MIN_ERROR_CLASS denotes an info message.
1681 // If it is a class < TdsEnums.MIN_ERROR_CLASS, it is a warning collection - so pass to handler
1682 this.OnInfoMessage(new SqlInfoMessageEventArgs(exception));
1690 // SxS: using Debugger.IsAttached
1692 [ResourceExposure(ResourceScope.None)]
1693 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1694 private void CompleteOpen() {
1695 Debug.Assert(ConnectionState.Open == State, "CompleteOpen not open");
1696 // be sure to mark as open so SqlDebugCheck can issue Query
1698 // check to see if we need to hook up sql-debugging if a debugger is attached
1699 // We only need this check for Shiloh and earlier servers.
1700 if (!GetOpenConnection().IsYukonOrNewer &&
1701 System.Diagnostics.Debugger.IsAttached) {
1702 bool debugCheck = false;
1704 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); // MDAC 66682, 69017
1707 catch (SecurityException e) {
1708 ADP.TraceExceptionWithoutRethrow(e);
1712 // if we don't have Unmanaged code permission, don't check for debugging
1713 // but let the connection be opened while under the debugger
1714 CheckSQLDebugOnConnect();
1719 internal SqlInternalConnection GetOpenConnection() {
1720 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
1721 if (null == innerConnection) {
1722 throw ADP.ClosedConnectionError();
1724 return innerConnection;
1727 internal SqlInternalConnection GetOpenConnection(string method) {
1728 DbConnectionInternal innerConnection = InnerConnection;
1729 SqlInternalConnection innerSqlConnection = (innerConnection as SqlInternalConnection);
1730 if (null == innerSqlConnection) {
1731 throw ADP.OpenConnectionRequired(method, innerConnection.State);
1733 return innerSqlConnection;
1736 internal SqlInternalConnectionTds GetOpenTdsConnection() {
1737 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
1738 if (null == innerConnection) {
1739 throw ADP.ClosedConnectionError();
1741 return innerConnection;
1744 internal SqlInternalConnectionTds GetOpenTdsConnection(string method) {
1745 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
1746 if (null == innerConnection) {
1747 throw ADP.OpenConnectionRequired(method, InnerConnection.State);
1749 return innerConnection;
1752 internal void OnInfoMessage(SqlInfoMessageEventArgs imevent) {
1754 OnInfoMessage(imevent, out notified);
1757 internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) {
1759 Debug.Assert(null != imevent, "null SqlInfoMessageEventArgs");
1760 Bid.Trace("<sc.SqlConnection.OnInfoMessage|API|INFO> %d#, Message='%ls'\n", ObjectID, ((null != imevent) ? imevent.Message : ""));
1762 SqlInfoMessageEventHandler handler = (SqlInfoMessageEventHandler)Events[EventInfoMessage];
1763 if (null != handler) {
1766 handler(this, imevent);
1768 catch (Exception e) { // MDAC 53175
1769 if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
1773 ADP.TraceExceptionWithoutRethrow(e);
1781 // SQL DEBUGGING SUPPORT
1784 // this only happens once per connection
1785 // SxS: using named file mapping APIs
1787 [ResourceExposure(ResourceScope.None)]
1788 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1789 private void CheckSQLDebugOnConnect() {
1791 uint pid = (uint)SafeNativeMethods.GetCurrentProcessId();
1795 // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
1796 if (ADP.IsPlatformNT5) {
1797 mapFileName = "Global\\" + TdsEnums.SDCI_MAPFILENAME;
1800 mapFileName = TdsEnums.SDCI_MAPFILENAME;
1803 mapFileName = mapFileName + pid.ToString(CultureInfo.InvariantCulture);
1805 hFileMap = NativeMethods.OpenFileMappingA(0x4/*FILE_MAP_READ*/, false, mapFileName);
1807 if (ADP.PtrZero != hFileMap) {
1808 IntPtr pMemMap = NativeMethods.MapViewOfFile(hFileMap, 0x4/*FILE_MAP_READ*/, 0, 0, IntPtr.Zero);
1809 if (ADP.PtrZero != pMemMap) {
1810 SqlDebugContext sdc = new SqlDebugContext();
1811 sdc.hMemMap = hFileMap;
1812 sdc.pMemMap = pMemMap;
1815 // optimization: if we only have to refresh memory-mapped data at connection open time
1816 // optimization: then call here instead of in CheckSQLDebug() which gets called
1817 // optimization: at command execution time
1818 // RefreshMemoryMappedData(sdc);
1820 // delaying setting out global state until after we issue this first SQLDebug command so that
1821 // we don't reentrantly call into CheckSQLDebug
1823 // now set our global state
1829 // This overload is called by the Command object when executing stored procedures. Note that
1830 // if SQLDebug has never been called, it is a noop.
1831 internal void CheckSQLDebug() {
1833 CheckSQLDebug(_sdc);
1836 // SxS: using GetCurrentThreadId
1837 [ResourceExposure(ResourceScope.None)]
1838 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1839 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] // MDAC 66682, 69017
1840 private void CheckSQLDebug(SqlDebugContext sdc) {
1841 // check to see if debugging has been activated
1842 Debug.Assert(null != sdc, "SQL Debug: invalid null debugging context!");
1844 #pragma warning disable 618
1845 uint tid = (uint)AppDomain.GetCurrentThreadId(); // Sql Debugging doesn't need fiber support;
1846 #pragma warning restore 618
1847 RefreshMemoryMappedData(sdc);
1853 // If we get here, the debugger must be hooked up.
1855 if (sdc.fOption/*TdsEnums.SQLDEBUG_ON*/) {
1860 IssueSQLDebug(TdsEnums.SQLDEBUG_ON, sdc.machineName, sdc.pid, sdc.dbgpid, sdc.sdiDllName, sdc.data);
1861 sdc.tid = 0; // reset so that the first successful time through, we notify the server of the context switch
1870 // be sure to pick up thread context switch, especially the first time through
1872 if (!sdc.fOption/*TdsEnums.SQLDEBUG_OFF*/) {
1873 // turn off and free the memory
1875 // okay if we throw out here, no state to clean up
1876 IssueSQLDebug(TdsEnums.SQLDEBUG_OFF, null, 0, 0, null, null);
1879 // notify server of context change
1880 if (sdc.tid != tid) {
1883 IssueSQLDebug(TdsEnums.SQLDEBUG_CONTEXT, null, sdc.pid, sdc.tid, null, null);
1894 private void IssueSQLDebug(uint option, string machineName, uint pid, uint id, string sdiDllName, byte[] data) {
1896 if (GetOpenConnection().IsYukonOrNewer) {
1903 SqlCommand c = new SqlCommand(TdsEnums.SP_SDIDEBUG, this);
1904 c.CommandType = CommandType.StoredProcedure;
1907 SqlParameter p = new SqlParameter(null, SqlDbType.VarChar, TdsEnums.SQLDEBUG_MODE_NAMES[option].Length);
1908 p.Value = TdsEnums.SQLDEBUG_MODE_NAMES[option];
1909 c.Parameters.Add(p);
1911 if (option == TdsEnums.SQLDEBUG_ON) {
1913 p = new SqlParameter(null, SqlDbType.VarChar, sdiDllName.Length);
1914 p.Value = sdiDllName;
1915 c.Parameters.Add(p);
1916 // debug machine name
1917 p = new SqlParameter(null, SqlDbType.VarChar, machineName.Length);
1918 p.Value = machineName;
1919 c.Parameters.Add(p);
1922 if (option != TdsEnums.SQLDEBUG_OFF) {
1924 p = new SqlParameter(null, SqlDbType.Int);
1926 c.Parameters.Add(p);
1928 p = new SqlParameter(null, SqlDbType.Int);
1930 c.Parameters.Add(p);
1933 if (option == TdsEnums.SQLDEBUG_ON) {
1935 p = new SqlParameter(null, SqlDbType.VarBinary, (null != data) ? data.Length : 0);
1937 c.Parameters.Add(p);
1940 c.ExecuteNonQuery();
1944 public static void ChangePassword(string connectionString, string newPassword) {
1946 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.ChangePassword|API>") ;
1947 Bid.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
1949 if (ADP.IsEmpty(connectionString)) {
1950 throw SQL.ChangePasswordArgumentMissing("connectionString");
1952 if (ADP.IsEmpty(newPassword)) {
1953 throw SQL.ChangePasswordArgumentMissing("newPassword");
1955 if (TdsEnums.MAXLEN_NEWPASSWORD < newPassword.Length) {
1956 throw ADP.InvalidArgumentLength("newPassword", TdsEnums.MAXLEN_NEWPASSWORD);
1959 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null);
1961 SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
1962 if (connectionOptions.IntegratedSecurity || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
1964 throw SQL.ChangePasswordConflictsWithSSPI();
1966 if (! ADP.IsEmpty(connectionOptions.AttachDBFilename)) {
1967 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename);
1969 if (connectionOptions.ContextConnection) {
1970 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.Context_Connection);
1973 System.Security.PermissionSet permissionSet = connectionOptions.CreatePermissionSet();
1974 permissionSet.Demand();
1976 ChangePassword(connectionString, connectionOptions, null, newPassword, null);
1979 Bid.ScopeLeave(ref hscp) ;
1983 public static void ChangePassword(string connectionString, SqlCredential credential, SecureString newSecurePassword) {
1985 Bid.ScopeEnter(out hscp, "<sc.SqlConnection.ChangePassword|API>") ;
1986 Bid.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
1988 if (ADP.IsEmpty(connectionString)) {
1989 throw SQL.ChangePasswordArgumentMissing("connectionString");
1992 // check credential; not necessary to check the length of password in credential as the check is done by SqlCredential class
1993 if (credential == null) {
1994 throw SQL.ChangePasswordArgumentMissing("credential");
1997 if (newSecurePassword == null || newSecurePassword.Length == 0) {
1998 throw SQL.ChangePasswordArgumentMissing("newSecurePassword");;
2001 if (!newSecurePassword.IsReadOnly()) {
2002 throw ADP.MustBeReadOnly("newSecurePassword");
2005 if (TdsEnums.MAXLEN_NEWPASSWORD < newSecurePassword.Length) {
2006 throw ADP.InvalidArgumentLength("newSecurePassword", TdsEnums.MAXLEN_NEWPASSWORD);
2009 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null);
2011 SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
2013 // Check for incompatible connection string value with SqlCredential
2014 if (!ADP.IsEmpty(connectionOptions.UserID) || !ADP.IsEmpty(connectionOptions.Password)) {
2015 throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
2018 if (connectionOptions.IntegratedSecurity || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
2019 throw SQL.ChangePasswordConflictsWithSSPI();
2022 if (! ADP.IsEmpty(connectionOptions.AttachDBFilename)) {
2023 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename);
2026 if (connectionOptions.ContextConnection) {
2027 throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.Context_Connection);
2030 System.Security.PermissionSet permissionSet = connectionOptions.CreatePermissionSet();
2031 permissionSet.Demand();
2033 ChangePassword(connectionString, connectionOptions, credential, null, newSecurePassword);
2036 Bid.ScopeLeave(ref hscp) ;
2040 private static void ChangePassword(string connectionString, SqlConnectionString connectionOptions, SqlCredential credential, string newPassword, SecureString newSecurePassword ) {
2041 // note: This is the only case where we directly construt the internal connection, passing in the new password.
2042 // Normally we would simply create a regular connectoin and open it but there is no other way to pass the
2043 // new password down to the constructor. Also it would have an unwanted impact on the connection pool
2045 using (SqlInternalConnectionTds con = new SqlInternalConnectionTds(null, connectionOptions, credential, null, newPassword, newSecurePassword, false)) {
2046 if (!con.IsYukonOrNewer) {
2047 throw SQL.ChangePasswordRequiresYukon();
2050 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null);
2052 SqlConnectionFactory.SingletonInstance.ClearPool(key);
2055 internal void RegisterForConnectionCloseNotification<T>(ref Task<T> outterTask, object value, int tag) {
2056 // Connection exists, schedule removal, will be added to ref collection after calling ValidateAndReconnect
2057 outterTask = outterTask.ContinueWith(task => {
2058 RemoveWeakReference(value);
2060 }, TaskScheduler.Default).Unwrap();
2063 // updates our context with any changes made to the memory-mapped data by an external process
2064 static private void RefreshMemoryMappedData(SqlDebugContext sdc) {
2065 Debug.Assert(ADP.PtrZero != sdc.pMemMap, "SQL Debug: invalid null value for pMemMap!");
2066 // copy memory mapped file contents into managed types
2067 MEMMAP memMap = (MEMMAP)Marshal.PtrToStructure(sdc.pMemMap, typeof(MEMMAP));
2068 sdc.dbgpid = memMap.dbgpid;
2069 sdc.fOption = (memMap.fOption == 1) ? true : false;
2070 // xlate ansi byte[] -> managed strings
2071 Encoding cp = System.Text.Encoding.GetEncoding(TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE);
2072 sdc.machineName = cp.GetString(memMap.rgbMachineName, 0, memMap.rgbMachineName.Length);
2073 sdc.sdiDllName = cp.GetString(memMap.rgbDllName, 0, memMap.rgbDllName.Length);
2074 // just get data reference
2075 sdc.data = memMap.rgbData;
2078 public void ResetStatistics() {
2079 if (IsContextConnection) {
2080 throw SQL.NotAvailableOnContextConnection();
2083 if (null != Statistics) {
2085 if (ConnectionState.Open == State) {
2086 // update timestamp;
2087 ADP.TimerCurrent(out _statistics._openTimestamp);
2092 public IDictionary RetrieveStatistics() {
2093 if (IsContextConnection) {
2094 throw SQL.NotAvailableOnContextConnection();
2097 if (null != Statistics) {
2099 return Statistics.GetHashtable();
2102 return new SqlStatistics().GetHashtable();
2106 private void UpdateStatistics() {
2107 if (ConnectionState.Open == State) {
2109 ADP.TimerCurrent(out _statistics._closeTimestamp);
2111 // delegate the rest of the work to the SqlStatistics class
2112 Statistics.UpdateStatistics();
2119 private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) {
2120 Debug.Assert(TypeSystemAssemblyVersion != null, "TypeSystemAssembly should be set !");
2121 if (string.Compare(asmRef.Name, "Microsoft.SqlServer.Types", StringComparison.OrdinalIgnoreCase) == 0) {
2123 if (asmRef.Version!=TypeSystemAssemblyVersion) {
2124 Bid.Trace("<sc.SqlConnection.ResolveTypeAssembly> SQL CLR type version change: Server sent %ls, client will instantiate %ls",
2125 asmRef.Version.ToString(), TypeSystemAssemblyVersion.ToString());
2128 asmRef.Version = TypeSystemAssemblyVersion;
2131 return Assembly.Load(asmRef);
2133 catch (Exception e) {
2134 if (throwOnError || !ADP.IsCatchableExceptionType(e)) {
2144 internal void CheckGetExtendedUDTInfo(SqlMetaDataPriv metaData, bool fThrow) {
2145 if (metaData.udtType == null) { // If null, we have not obtained extended info.
2146 Debug.Assert(!ADP.IsEmpty(metaData.udtAssemblyQualifiedName), "Unexpected state on GetUDTInfo");
2147 // Parameter throwOnError determines whether exception from Assembly.Load is thrown.
2149 Type.GetType(typeName:metaData.udtAssemblyQualifiedName, assemblyResolver:asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver:null, throwOnError: fThrow);
2151 if (fThrow && metaData.udtType == null) {
2153 throw SQL.UDTUnexpectedResult(metaData.udtAssemblyQualifiedName);
2158 internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnDBNull) {
2159 if (returnDBNull && ADP.IsNull(value)) {
2160 return DBNull.Value;
2165 // Since the serializer doesn't handle nulls...
2166 if (ADP.IsNull(value)) {
2167 Type t = metaData.udtType;
2168 Debug.Assert(t != null, "Unexpected null of udtType on GetUdtValue!");
2169 o = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, new Object[]{}, CultureInfo.InvariantCulture);
2170 Debug.Assert(o != null);
2175 MemoryStream stm = new MemoryStream((byte[]) value);
2177 o = SerializationHelperSql9.Deserialize(stm, metaData.udtType);
2179 Debug.Assert(o != null, "object could NOT be created");
2184 internal byte[] GetBytes(object o) {
2185 Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;
2187 return GetBytes(o, out format, out maxSize);
2190 internal byte[] GetBytes(object o, out Microsoft.SqlServer.Server.Format format, out int maxSize) {
2191 SqlUdtInfo attr = AssemblyCache.GetInfoFromType(o.GetType());
2192 maxSize = attr.MaxByteSize;
2193 format = attr.SerializationFormat;
2195 if (maxSize < -1 || maxSize >= UInt16.MaxValue) { // Do we need this? Is this the right place?
2196 throw new InvalidOperationException(o.GetType() + ": invalid Size");
2201 using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) {
2202 SerializationHelperSql9.Serialize(stm, o);
2203 retval = stm.ToArray();
2217 ClassInterface(ClassInterfaceType.None),
2218 Guid("afef65ad-4577-447a-a148-83acadd3d4b9"),
2220 [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
2221 public sealed class SQLDebugging: ISQLDebug {
2224 const int STANDARD_RIGHTS_REQUIRED = (0x000F0000);
2225 const int DELETE = (0x00010000);
2226 const int READ_CONTROL = (0x00020000);
2227 const int WRITE_DAC = (0x00040000);
2228 const int WRITE_OWNER = (0x00080000);
2229 const int SYNCHRONIZE = (0x00100000);
2230 const int FILE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x000001FF);
2231 const uint GENERIC_READ = (0x80000000);
2232 const uint GENERIC_WRITE = (0x40000000);
2233 const uint GENERIC_EXECUTE = (0x20000000);
2234 const uint GENERIC_ALL = (0x10000000);
2236 const int SECURITY_DESCRIPTOR_REVISION = (1);
2237 const int ACL_REVISION = (2);
2239 const int SECURITY_AUTHENTICATED_USER_RID = (0x0000000B);
2240 const int SECURITY_LOCAL_SYSTEM_RID = (0x00000012);
2241 const int SECURITY_BUILTIN_DOMAIN_RID = (0x00000020);
2242 const int SECURITY_WORLD_RID = (0x00000000);
2243 const byte SECURITY_NT_AUTHORITY = 5;
2244 const int DOMAIN_GROUP_RID_ADMINS = (0x00000200);
2245 const int DOMAIN_ALIAS_RID_ADMINS = (0x00000220);
2247 const int sizeofSECURITY_ATTRIBUTES = 12; // sizeof(SECURITY_ATTRIBUTES);
2248 const int sizeofSECURITY_DESCRIPTOR = 20; // sizeof(SECURITY_DESCRIPTOR);
2249 const int sizeofACCESS_ALLOWED_ACE = 12; // sizeof(ACCESS_ALLOWED_ACE);
2250 const int sizeofACCESS_DENIED_ACE = 12; // sizeof(ACCESS_DENIED_ACE);
2251 const int sizeofSID_IDENTIFIER_AUTHORITY = 6; // sizeof(SID_IDENTIFIER_AUTHORITY)
2252 const int sizeofACL = 8; // sizeof(ACL);
2254 private IntPtr CreateSD(ref IntPtr pDacl) {
2255 IntPtr pSecurityDescriptor = IntPtr.Zero;
2256 IntPtr pUserSid = IntPtr.Zero;
2257 IntPtr pAdminSid = IntPtr.Zero;
2258 IntPtr pNtAuthority = IntPtr.Zero;
2260 bool status = false;
2262 pNtAuthority = Marshal.AllocHGlobal(sizeofSID_IDENTIFIER_AUTHORITY);
2263 if (pNtAuthority == IntPtr.Zero)
2265 Marshal.WriteInt32(pNtAuthority, 0, 0);
2266 Marshal.WriteByte(pNtAuthority, 4, 0);
2267 Marshal.WriteByte(pNtAuthority, 5, SECURITY_NT_AUTHORITY);
2270 NativeMethods.AllocateAndInitializeSid(
2273 SECURITY_AUTHENTICATED_USER_RID,
2283 if (!status || pUserSid == IntPtr.Zero) {
2287 NativeMethods.AllocateAndInitializeSid(
2290 SECURITY_BUILTIN_DOMAIN_RID,
2291 DOMAIN_ALIAS_RID_ADMINS,
2300 if (!status || pAdminSid == IntPtr.Zero) {
2304 pSecurityDescriptor = Marshal.AllocHGlobal(sizeofSECURITY_DESCRIPTOR);
2305 if (pSecurityDescriptor == IntPtr.Zero) {
2308 for (int i = 0; i < sizeofSECURITY_DESCRIPTOR; i++)
2309 Marshal.WriteByte(pSecurityDescriptor, i, (byte)0);
2311 + (2 * (sizeofACCESS_ALLOWED_ACE))
2312 + sizeofACCESS_DENIED_ACE
2313 + NativeMethods.GetLengthSid(pUserSid)
2314 + NativeMethods.GetLengthSid(pAdminSid);
2316 pDacl = Marshal.AllocHGlobal(cbAcl);
2317 if (pDacl == IntPtr.Zero) {
2320 // rights must be added in a certain order. Namely, deny access first, then add access
2321 if (NativeMethods.InitializeAcl(pDacl, cbAcl, ACL_REVISION))
2322 if (NativeMethods.AddAccessDeniedAce(pDacl, ACL_REVISION, WRITE_DAC, pUserSid))
2323 if (NativeMethods.AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_READ, pUserSid))
2324 if (NativeMethods.AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_ALL, pAdminSid))
2325 if (NativeMethods.InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
2326 if (NativeMethods.SetSecurityDescriptorDacl(pSecurityDescriptor, true, pDacl, false)) {
2331 if (pNtAuthority != IntPtr.Zero) {
2332 Marshal.FreeHGlobal(pNtAuthority);
2334 if (pAdminSid != IntPtr.Zero)
2335 NativeMethods.FreeSid(pAdminSid);
2336 if (pUserSid != IntPtr.Zero)
2337 NativeMethods.FreeSid(pUserSid);
2339 return pSecurityDescriptor;
2341 if (pSecurityDescriptor != IntPtr.Zero) {
2342 Marshal.FreeHGlobal(pSecurityDescriptor);
2348 // SxS: using file mapping API (CreateFileMapping)
2350 [ResourceExposure(ResourceScope.None)]
2351 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
2352 bool ISQLDebug.SQLDebug(int dwpidDebugger, int dwpidDebuggee, [MarshalAs(UnmanagedType.LPStr)] string pszMachineName,
2353 [MarshalAs(UnmanagedType.LPStr)] string pszSDIDLLName, int dwOption, int cbData, byte[] rgbData) {
2354 bool result = false;
2355 IntPtr hFileMap = IntPtr.Zero;
2356 IntPtr pMemMap = IntPtr.Zero;
2357 IntPtr pSecurityDescriptor = IntPtr.Zero;
2358 IntPtr pSecurityAttributes = IntPtr.Zero;
2359 IntPtr pDacl = IntPtr.Zero;
2361 // validate the structure
2362 if (null == pszMachineName || null == pszSDIDLLName)
2365 if (pszMachineName.Length > TdsEnums.SDCI_MAX_MACHINENAME ||
2366 pszSDIDLLName.Length > TdsEnums.SDCI_MAX_DLLNAME)
2369 // note that these are ansi strings
2370 Encoding cp = System.Text.Encoding.GetEncoding(TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE);
2371 byte[] rgbMachineName = cp.GetBytes(pszMachineName);
2372 byte[] rgbSDIDLLName = cp.GetBytes(pszSDIDLLName);
2374 if (null != rgbData && cbData > TdsEnums.SDCI_MAX_DATA)
2379 // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
2380 if (ADP.IsPlatformNT5) {
2381 mapFileName = "Global\\" + TdsEnums.SDCI_MAPFILENAME;
2384 mapFileName = TdsEnums.SDCI_MAPFILENAME;
2387 mapFileName = mapFileName + dwpidDebuggee.ToString(CultureInfo.InvariantCulture);
2389 // Create Security Descriptor
2390 pSecurityDescriptor = CreateSD(ref pDacl);
2391 pSecurityAttributes = Marshal.AllocHGlobal(sizeofSECURITY_ATTRIBUTES);
2392 if ((pSecurityDescriptor == IntPtr.Zero) || (pSecurityAttributes == IntPtr.Zero))
2395 Marshal.WriteInt32(pSecurityAttributes, 0, sizeofSECURITY_ATTRIBUTES); // nLength = sizeof(SECURITY_ATTRIBUTES)
2396 Marshal.WriteIntPtr(pSecurityAttributes, 4, pSecurityDescriptor); // lpSecurityDescriptor = pSecurityDescriptor
2397 Marshal.WriteInt32(pSecurityAttributes, 8, 0); // bInheritHandle = FALSE
2398 hFileMap = NativeMethods.CreateFileMappingA(
2399 ADP.InvalidPtr/*INVALID_HANDLE_VALUE*/,
2400 pSecurityAttributes,
2401 0x4/*PAGE_READWRITE*/,
2403 Marshal.SizeOf(typeof(MEMMAP)),
2406 if (IntPtr.Zero == hFileMap) {
2411 pMemMap = NativeMethods.MapViewOfFile(hFileMap, 0x6/*FILE_MAP_READ|FILE_MAP_WRITE*/, 0, 0, IntPtr.Zero);
2413 if (IntPtr.Zero == pMemMap) {
2417 // copy data to memory-mapped file
2418 // layout of MEMMAP structure is:
2421 // byte[32] machineName
2422 // byte[16] sdiDllName
2426 Marshal.WriteInt32(pMemMap, offset, (int)dwpidDebugger);
2428 Marshal.WriteInt32(pMemMap, offset, (int)dwOption);
2430 Marshal.Copy(rgbMachineName, 0, ADP.IntPtrOffset(pMemMap, offset), rgbMachineName.Length);
2431 offset += TdsEnums.SDCI_MAX_MACHINENAME;
2432 Marshal.Copy(rgbSDIDLLName, 0, ADP.IntPtrOffset(pMemMap, offset), rgbSDIDLLName.Length);
2433 offset += TdsEnums.SDCI_MAX_DLLNAME;
2434 Marshal.WriteInt32(pMemMap, offset, (int)cbData);
2436 if (null != rgbData) {
2437 Marshal.Copy(rgbData, 0, ADP.IntPtrOffset(pMemMap, offset), (int)cbData);
2439 NativeMethods.UnmapViewOfFile(pMemMap);
2442 if (result == false) {
2443 if (hFileMap != IntPtr.Zero)
2444 NativeMethods.CloseHandle(hFileMap);
2446 if (pSecurityAttributes != IntPtr.Zero)
2447 Marshal.FreeHGlobal(pSecurityAttributes);
2448 if (pSecurityDescriptor != IntPtr.Zero)
2449 Marshal.FreeHGlobal(pSecurityDescriptor);
2450 if (pDacl != IntPtr.Zero)
2451 Marshal.FreeHGlobal(pDacl);
2456 // this is a private interface to com+ users
2457 // do not change this guid
2461 Guid("6cb925bf-c3c0-45b3-9f44-5dd67c7b7fe8"),
2462 InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
2463 BestFitMapping(false, ThrowOnUnmappableChar = true),
2465 interface ISQLDebug {
2467 [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
2471 [MarshalAs(UnmanagedType.LPStr)] string pszMachineName,
2472 [MarshalAs(UnmanagedType.LPStr)] string pszSDIDLLName,
2478 sealed class SqlDebugContext: IDisposable {
2480 internal uint pid = 0;
2481 internal uint tid = 0;
2482 internal bool active = false;
2483 // memory-mapped data
2484 internal IntPtr pMemMap = ADP.PtrZero;
2485 internal IntPtr hMemMap = ADP.PtrZero;
2486 internal uint dbgpid = 0;
2487 internal bool fOption = false;
2488 internal string machineName = null;
2489 internal string sdiDllName = null;
2490 internal byte[] data = null;
2492 public void Dispose() {
2494 GC.SuppressFinalize(this);
2497 // using CloseHandle and UnmapViewOfFile - no exposure
2498 [ResourceExposure(ResourceScope.None)]
2499 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
2500 private void Dispose(bool disposing) {
2502 // Nothing to do here
2505 if (pMemMap != IntPtr.Zero) {
2506 NativeMethods.UnmapViewOfFile(pMemMap);
2507 pMemMap = IntPtr.Zero;
2509 if (hMemMap != IntPtr.Zero) {
2510 NativeMethods.CloseHandle(hMemMap);
2511 hMemMap = IntPtr.Zero;
2516 ~SqlDebugContext() {
2522 // native interop memory mapped structure for sdi debugging
2523 [StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
2524 internal struct MEMMAP {
2525 [MarshalAs(UnmanagedType.U4)]
2526 internal uint dbgpid; // id of debugger
2527 [MarshalAs(UnmanagedType.U4)]
2528 internal uint fOption; // 1 - start debugging, 0 - stop debugging
2529 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
2530 internal byte[] rgbMachineName;
2531 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
2532 internal byte[] rgbDllName;
2533 [MarshalAs(UnmanagedType.U4)]
2534 internal uint cbData;
2535 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
2536 internal byte[] rgbData;
2538 } // System.Data.SqlClient namespace