/mcs/class/referencesource/System.Data/System/Data/SqlClient/SqlConnection.cs

http://github.com/mono/mono · C# · 2598 lines · 1981 code · 318 blank · 299 comment · 444 complexity · 6cebdf85c2005c2c7b8e5723a5995d48 MD5 · raw file

Large files are truncated click here to view the full file

  1. //------------------------------------------------------------------------------
  2. // <copyright file="SqlConnection.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. // <owner current="true" primary="false">Microsoft</owner>
  7. //------------------------------------------------------------------------------
  8. [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.Data.DataSetExtensions, PublicKey="+AssemblyRef.EcmaPublicKeyFull)] // DevDiv Bugs 92166
  9. namespace System.Data.SqlClient
  10. {
  11. using System;
  12. using System.Collections;
  13. using System.Collections.Concurrent;
  14. using System.Collections.Generic;
  15. using System.Collections.ObjectModel;
  16. using System.Configuration.Assemblies;
  17. using System.ComponentModel;
  18. using System.Data;
  19. using System.Data.Common;
  20. using System.Data.ProviderBase;
  21. using System.Data.Sql;
  22. using System.Data.SqlTypes;
  23. using System.Diagnostics;
  24. using System.Globalization;
  25. using System.IO;
  26. using System.Linq;
  27. using System.Runtime.CompilerServices;
  28. using System.Runtime.ConstrainedExecution;
  29. using System.Runtime.InteropServices;
  30. using System.Runtime.Remoting;
  31. using System.Runtime.Serialization.Formatters;
  32. using System.Text;
  33. using System.Threading;
  34. using System.Threading.Tasks;
  35. using System.Security;
  36. using System.Security.Permissions;
  37. using System.Reflection;
  38. using System.Runtime.Versioning;
  39. using Microsoft.SqlServer.Server;
  40. using System.Security.Principal;
  41. using System.Diagnostics.CodeAnalysis;
  42. [DefaultEvent("InfoMessage")]
  43. public sealed partial class SqlConnection: DbConnection, ICloneable {
  44. static private readonly object EventInfoMessage = new object();
  45. // System column encryption key store providers are added by default
  46. static private readonly Dictionary<string, SqlColumnEncryptionKeyStoreProvider> _SystemColumnEncryptionKeyStoreProviders
  47. = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
  48. {
  49. {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()},
  50. {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()},
  51. {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()}
  52. };
  53. /// <summary>
  54. /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary.
  55. /// Custom provider list can only supplied once per application.
  56. /// </summary>
  57. static private ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _CustomColumnEncryptionKeyStoreProviders;
  58. // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders
  59. static private readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object();
  60. /// <summary>
  61. /// Dictionary object holding trusted key paths for various SQL Servers.
  62. /// Key to the dictionary is a SQL Server Name
  63. /// IList contains a list of trusted key paths.
  64. /// </summary>
  65. static private readonly ConcurrentDictionary<string, IList<string>> _ColumnEncryptionTrustedMasterKeyPaths
  66. = new ConcurrentDictionary<string, IList<string>>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
  67. capacity: 1,
  68. comparer: StringComparer.OrdinalIgnoreCase);
  69. [
  70. DefaultValue(null),
  71. ResCategoryAttribute(Res.DataCategory_Data),
  72. ResDescriptionAttribute(Res.TCE_SqlConnection_TrustedColumnMasterKeyPaths),
  73. ]
  74. static public IDictionary<string, IList<string>> ColumnEncryptionTrustedMasterKeyPaths
  75. {
  76. get
  77. {
  78. return _ColumnEncryptionTrustedMasterKeyPaths;
  79. }
  80. }
  81. /// <summary>
  82. /// Defines whether query metadata caching is enabled.
  83. /// </summary>
  84. static private bool _ColumnEncryptionQueryMetadataCacheEnabled = true;
  85. [
  86. DefaultValue(null),
  87. ResCategoryAttribute(Res.DataCategory_Data),
  88. ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled),
  89. ]
  90. static public bool ColumnEncryptionQueryMetadataCacheEnabled
  91. {
  92. get
  93. {
  94. return _ColumnEncryptionQueryMetadataCacheEnabled;
  95. }
  96. set
  97. {
  98. _ColumnEncryptionQueryMetadataCacheEnabled = value;
  99. }
  100. }
  101. /// <summary>
  102. /// Defines whether query metadata caching is enabled.
  103. /// </summary>
  104. static private TimeSpan _ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2);
  105. [
  106. DefaultValue(null),
  107. ResCategoryAttribute(Res.DataCategory_Data),
  108. ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl),
  109. ]
  110. static public TimeSpan ColumnEncryptionKeyCacheTtl
  111. {
  112. get
  113. {
  114. return _ColumnEncryptionKeyCacheTtl;
  115. }
  116. set
  117. {
  118. _ColumnEncryptionKeyCacheTtl = value;
  119. }
  120. }
  121. /// <summary>
  122. /// This function should only be called once in an app. This does shallow copying of the dictionary so that
  123. /// the app cannot alter the custom provider list once it has been set.
  124. ///
  125. /// Example:
  126. ///
  127. /// Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
  128. /// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
  129. /// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
  130. /// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
  131. /// </summary>
  132. /// <param name="customProviders">Custom column encryption key provider dictionary</param>
  133. static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders)
  134. {
  135. // Return when the provided dictionary is null.
  136. if (customProviders == null)
  137. {
  138. throw SQL.NullCustomKeyStoreProviderDictionary();
  139. }
  140. // Validate that custom provider list doesn't contain any of system provider list
  141. foreach (string key in customProviders.Keys)
  142. {
  143. // Validate the provider name
  144. //
  145. // Check for null or empty
  146. if (string.IsNullOrWhiteSpace(key))
  147. {
  148. throw SQL.EmptyProviderName();
  149. }
  150. // Check if the name starts with MSSQL_, since this is reserved namespace for system providers.
  151. if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase))
  152. {
  153. throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix);
  154. }
  155. // Validate the provider value
  156. if (customProviders[key] == null)
  157. {
  158. throw SQL.NullProviderValue(key);
  159. }
  160. }
  161. lock (_CustomColumnEncryptionKeyProvidersLock)
  162. {
  163. // Provider list can only be set once
  164. if (_CustomColumnEncryptionKeyStoreProviders != null)
  165. {
  166. throw SQL.CanOnlyCallOnce();
  167. }
  168. // Create a temporary dictionary and then add items from the provided dictionary.
  169. // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs
  170. // in the provided customerProviders dictionary.
  171. Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customColumnEncryptionKeyStoreProviders =
  172. new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(customProviders, StringComparer.OrdinalIgnoreCase);
  173. // Set the dictionary to the ReadOnly dictionary.
  174. _CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider>(customColumnEncryptionKeyStoreProviders);
  175. }
  176. }
  177. /// <summary>
  178. /// This function walks through both system and custom column encryption key store providers and returns an object if found.
  179. /// </summary>
  180. /// <param name="providerName">Provider Name to be searched in System Provider diction and Custom provider dictionary.</param>
  181. /// <param name="columnKeyStoreProvider">If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance.</param>
  182. /// <returns>true if the provider is found, else returns false</returns>
  183. static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) {
  184. Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid");
  185. // Initialize the out parameter
  186. columnKeyStoreProvider = null;
  187. // Search in the sytem provider list.
  188. if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
  189. {
  190. return true;
  191. }
  192. lock (_CustomColumnEncryptionKeyProvidersLock)
  193. {
  194. // If custom provider is not set, then return false
  195. if (_CustomColumnEncryptionKeyStoreProviders == null)
  196. {
  197. return false;
  198. }
  199. // Search in the custom provider list
  200. return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
  201. }
  202. }
  203. /// <summary>
  204. /// This function returns a list of system provider dictionary currently supported by this driver.
  205. /// </summary>
  206. /// <returns>Combined list of provider names</returns>
  207. static internal List<string> GetColumnEncryptionSystemKeyStoreProviders() {
  208. HashSet<string> providerNames = new HashSet<string>(_SystemColumnEncryptionKeyStoreProviders.Keys);
  209. return providerNames.ToList();
  210. }
  211. /// <summary>
  212. /// This function returns a list of custom provider dictionary currently registered.
  213. /// </summary>
  214. /// <returns>Combined list of provider names</returns>
  215. static internal List<string> GetColumnEncryptionCustomKeyStoreProviders() {
  216. if(_CustomColumnEncryptionKeyStoreProviders != null)
  217. {
  218. HashSet<string> providerNames = new HashSet<string>(_CustomColumnEncryptionKeyStoreProviders.Keys);
  219. return providerNames.ToList();
  220. }
  221. return new List<string>();
  222. }
  223. private SqlDebugContext _sdc; // SQL Debugging support
  224. private bool _AsyncCommandInProgress;
  225. // SQLStatistics support
  226. internal SqlStatistics _statistics;
  227. private bool _collectstats;
  228. private bool _fireInfoMessageEventOnUserErrors; // False by default
  229. // root task associated with current async invocation
  230. Tuple<TaskCompletionSource<DbConnectionInternal>, Task> _currentCompletion;
  231. private SqlCredential _credential; // SQL authentication password stored in SecureString
  232. private string _connectionString;
  233. private int _connectRetryCount;
  234. private string _accessToken; // Access Token to be used for token based authententication
  235. // connection resiliency
  236. private object _reconnectLock = new object();
  237. internal Task _currentReconnectionTask;
  238. private Task _asyncWaitingForReconnection; // current async task waiting for reconnection in non-MARS connections
  239. private Guid _originalConnectionId = Guid.Empty;
  240. private CancellationTokenSource _reconnectionCancellationSource;
  241. internal SessionData _recoverySessionData;
  242. internal WindowsIdentity _lastIdentity;
  243. internal WindowsIdentity _impersonateIdentity;
  244. private int _reconnectCount;
  245. // 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
  246. // 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
  247. // using SqlConnection.Open() method.
  248. internal bool _applyTransientFaultHandling = false;
  249. public SqlConnection(string connectionString) : this(connectionString, null) {
  250. }
  251. public SqlConnection(string connectionString, SqlCredential credential) : this() {
  252. ConnectionString = connectionString; // setting connection string first so that ConnectionOption is available
  253. if (credential != null)
  254. {
  255. // The following checks are necessary as setting Credential property will call CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential
  256. // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential it will throw InvalidOperationException rather than Arguemtn exception
  257. // Need to call setter on Credential property rather than setting _credential directly as pool groups need to be checked
  258. SqlConnectionString connectionOptions = (SqlConnectionString) ConnectionOptions;
  259. if (UsesClearUserIdOrPassword(connectionOptions))
  260. {
  261. throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
  262. }
  263. if (UsesIntegratedSecurity(connectionOptions))
  264. {
  265. throw ADP.InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity();
  266. }
  267. if (UsesContextConnection(connectionOptions))
  268. {
  269. throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
  270. }
  271. if (UsesActiveDirectoryIntegrated(connectionOptions))
  272. {
  273. throw SQL.SettingCredentialWithIntegratedArgument();
  274. }
  275. Credential = credential;
  276. }
  277. // else
  278. // credential == null: we should not set "Credential" as this will do additional validation check and
  279. // checking pool groups which is not necessary. All necessary operation is already done by calling "ConnectionString = connectionString"
  280. CacheConnectionStringProperties();
  281. }
  282. private SqlConnection(SqlConnection connection) { // Clone
  283. GC.SuppressFinalize(this);
  284. CopyFrom(connection);
  285. _connectionString = connection._connectionString;
  286. if (connection._credential != null)
  287. {
  288. SecureString password = connection._credential.Password.Copy();
  289. password.MakeReadOnly();
  290. _credential = new SqlCredential(connection._credential.UserId, password);
  291. }
  292. _accessToken = connection._accessToken;
  293. CacheConnectionStringProperties();
  294. }
  295. // This method will be called once connection string is set or changed.
  296. private void CacheConnectionStringProperties() {
  297. SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
  298. if (connString != null) {
  299. _connectRetryCount = connString.ConnectRetryCount;
  300. }
  301. }
  302. //
  303. // PUBLIC PROPERTIES
  304. //
  305. // used to start/stop collection of statistics data and do verify the current state
  306. //
  307. // devnote: start/stop should not performed using a property since it requires execution of code
  308. //
  309. // start statistics
  310. // set the internal flag (_statisticsEnabled) to true.
  311. // Create a new SqlStatistics object if not already there.
  312. // connect the parser to the object.
  313. // if there is no parser at this time we need to connect it after creation.
  314. //
  315. [
  316. DefaultValue(false),
  317. ResCategoryAttribute(Res.DataCategory_Data),
  318. ResDescriptionAttribute(Res.SqlConnection_StatisticsEnabled),
  319. ]
  320. public bool StatisticsEnabled {
  321. get {
  322. return (_collectstats);
  323. }
  324. set {
  325. if (IsContextConnection) {
  326. if (value) {
  327. throw SQL.NotAvailableOnContextConnection();
  328. }
  329. }
  330. else {
  331. if (value) {
  332. // start
  333. if (ConnectionState.Open == State) {
  334. if (null == _statistics) {
  335. _statistics = new SqlStatistics();
  336. ADP.TimerCurrent(out _statistics._openTimestamp);
  337. }
  338. // set statistics on the parser
  339. // update timestamp;
  340. Debug.Assert(Parser != null, "Where's the parser?");
  341. Parser.Statistics = _statistics;
  342. }
  343. }
  344. else {
  345. // stop
  346. if (null != _statistics) {
  347. if (ConnectionState.Open == State) {
  348. // remove statistics from parser
  349. // update timestamp;
  350. TdsParser parser = Parser;
  351. Debug.Assert(parser != null, "Where's the parser?");
  352. parser.Statistics = null;
  353. ADP.TimerCurrent(out _statistics._closeTimestamp);
  354. }
  355. }
  356. }
  357. this._collectstats = value;
  358. }
  359. }
  360. }
  361. internal bool AsyncCommandInProgress {
  362. get {
  363. return (_AsyncCommandInProgress);
  364. }
  365. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  366. set {
  367. _AsyncCommandInProgress = value;
  368. }
  369. }
  370. internal bool IsContextConnection {
  371. get {
  372. SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
  373. return UsesContextConnection(opt);
  374. }
  375. }
  376. /// <summary>
  377. /// Is this connection using column encryption ?
  378. /// </summary>
  379. internal bool IsColumnEncryptionSettingEnabled {
  380. get {
  381. SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
  382. return opt != null ? opt.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled : false;
  383. }
  384. }
  385. // Is this connection is a Context Connection?
  386. private bool UsesContextConnection(SqlConnectionString opt)
  387. {
  388. return opt != null ? opt.ContextConnection : false;
  389. }
  390. private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt)
  391. {
  392. return opt != null ? opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated : false;
  393. }
  394. private bool UsesAuthentication(SqlConnectionString opt) {
  395. return opt != null ? opt.Authentication != SqlAuthenticationMethod.NotSpecified : false;
  396. }
  397. // Does this connection uses Integrated Security?
  398. private bool UsesIntegratedSecurity(SqlConnectionString opt) {
  399. return opt != null ? opt.IntegratedSecurity : false;
  400. }
  401. // Does this connection uses old style of clear userID or Password in connection string?
  402. private bool UsesClearUserIdOrPassword(SqlConnectionString opt) {
  403. bool result = false;
  404. if (null != opt) {
  405. result = (!ADP.IsEmpty(opt.UserID) || !ADP.IsEmpty(opt.Password));
  406. }
  407. return result;
  408. }
  409. internal SqlConnectionString.TransactionBindingEnum TransactionBinding {
  410. get {
  411. return ((SqlConnectionString)ConnectionOptions).TransactionBinding;
  412. }
  413. }
  414. internal SqlConnectionString.TypeSystem TypeSystem {
  415. get {
  416. return ((SqlConnectionString)ConnectionOptions).TypeSystemVersion;
  417. }
  418. }
  419. internal Version TypeSystemAssemblyVersion {
  420. get {
  421. return ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion;
  422. }
  423. }
  424. internal PoolBlockingPeriod PoolBlockingPeriod
  425. {
  426. get
  427. {
  428. return ((SqlConnectionString)ConnectionOptions).PoolBlockingPeriod;
  429. }
  430. }
  431. internal int ConnectRetryInterval {
  432. get {
  433. return ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval;
  434. }
  435. }
  436. override protected DbProviderFactory DbProviderFactory {
  437. get {
  438. return SqlClientFactory.Instance;
  439. }
  440. }
  441. // AccessToken: To be used for token based authentication
  442. [
  443. Browsable(false),
  444. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  445. ResDescriptionAttribute(Res.SqlConnection_AccessToken),
  446. ]
  447. public string AccessToken {
  448. get {
  449. string result = _accessToken;
  450. // When a connection is connecting or is ever opened, make AccessToken available only if "Persist Security Info" is set to true
  451. // otherwise, return null
  452. SqlConnectionString connectionOptions = (SqlConnectionString)UserConnectionOptions;
  453. if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo) {
  454. result = null;
  455. }
  456. return result;
  457. }
  458. set {
  459. // If a connection is connecting or is ever opened, AccessToken cannot be set
  460. if (!InnerConnection.AllowSetConnectionString) {
  461. throw ADP.OpenConnectionPropertySet("AccessToken", InnerConnection.State);
  462. }
  463. if (value != null) {
  464. // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential
  465. CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions);
  466. }
  467. _accessToken = value;
  468. // Need to call ConnectionString_Set to do proper pool group check
  469. ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: _accessToken));
  470. }
  471. }
  472. [
  473. DefaultValue(""),
  474. #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
  475. RecommendedAsConfigurable(true),
  476. #pragma warning restore 618
  477. SettingsBindableAttribute(true),
  478. RefreshProperties(RefreshProperties.All),
  479. ResCategoryAttribute(Res.DataCategory_Data),
  480. Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
  481. ResDescriptionAttribute(Res.SqlConnection_ConnectionString),
  482. ]
  483. override public string ConnectionString {
  484. get {
  485. return ConnectionString_Get();
  486. }
  487. set {
  488. if(_credential != null || _accessToken != null) {
  489. SqlConnectionString connectionOptions = new SqlConnectionString(value);
  490. if(_credential != null) {
  491. // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
  492. // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
  493. // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
  494. if(UsesActiveDirectoryIntegrated(connectionOptions)) {
  495. throw SQL.SettingIntegratedWithCredential();
  496. }
  497. CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
  498. }
  499. else if(_accessToken != null) {
  500. CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions);
  501. }
  502. }
  503. ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken));
  504. _connectionString = value; // Change _connectionString value only after value is validated
  505. CacheConnectionStringProperties();
  506. }
  507. }
  508. [
  509. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  510. ResDescriptionAttribute(Res.SqlConnection_ConnectionTimeout),
  511. ]
  512. override public int ConnectionTimeout {
  513. get {
  514. SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
  515. return ((null != constr) ? constr.ConnectTimeout : SqlConnectionString.DEFAULT.Connect_Timeout);
  516. }
  517. }
  518. [
  519. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  520. ResDescriptionAttribute(Res.SqlConnection_Database),
  521. ]
  522. override public string Database {
  523. // if the connection is open, we need to ask the inner connection what it's
  524. // current catalog is because it may have gotten changed, otherwise we can
  525. // just return what the connection string had.
  526. get {
  527. SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
  528. string result;
  529. if (null != innerConnection) {
  530. result = innerConnection.CurrentDatabase;
  531. }
  532. else {
  533. SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
  534. result = ((null != constr) ? constr.InitialCatalog : SqlConnectionString.DEFAULT.Initial_Catalog);
  535. }
  536. return result;
  537. }
  538. }
  539. [
  540. Browsable(true),
  541. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  542. ResDescriptionAttribute(Res.SqlConnection_DataSource),
  543. ]
  544. override public string DataSource {
  545. get {
  546. SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
  547. string result;
  548. if (null != innerConnection) {
  549. result = innerConnection.CurrentDataSource;
  550. }
  551. else {
  552. SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
  553. result = ((null != constr) ? constr.DataSource : SqlConnectionString.DEFAULT.Data_Source);
  554. }
  555. return result;
  556. }
  557. }
  558. [
  559. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  560. ResCategoryAttribute(Res.DataCategory_Data),
  561. ResDescriptionAttribute(Res.SqlConnection_PacketSize),
  562. ]
  563. public int PacketSize {
  564. // if the connection is open, we need to ask the inner connection what it's
  565. // current packet size is because it may have gotten changed, otherwise we
  566. // can just return what the connection string had.
  567. get {
  568. if (IsContextConnection) {
  569. throw SQL.NotAvailableOnContextConnection();
  570. }
  571. SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
  572. int result;
  573. if (null != innerConnection) {
  574. result = innerConnection.PacketSize;
  575. }
  576. else {
  577. SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
  578. result = ((null != constr) ? constr.PacketSize : SqlConnectionString.DEFAULT.Packet_Size);
  579. }
  580. return result;
  581. }
  582. }
  583. [
  584. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  585. ResCategoryAttribute(Res.DataCategory_Data),
  586. ResDescriptionAttribute(Res.SqlConnection_ClientConnectionId),
  587. ]
  588. public Guid ClientConnectionId {
  589. get {
  590. SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
  591. if (null != innerConnection) {
  592. return innerConnection.ClientConnectionId;
  593. }
  594. else {
  595. Task reconnectTask = _currentReconnectionTask;
  596. if (reconnectTask != null && !reconnectTask.IsCompleted) {
  597. return _originalConnectionId;
  598. }
  599. return Guid.Empty;
  600. }
  601. }
  602. }
  603. [
  604. Browsable(false),
  605. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  606. ResDescriptionAttribute(Res.SqlConnection_ServerVersion),
  607. ]
  608. override public string ServerVersion {
  609. get {
  610. return GetOpenConnection().ServerVersion;
  611. }
  612. }
  613. [
  614. Browsable(false),
  615. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  616. ResDescriptionAttribute(Res.DbConnection_State),
  617. ]
  618. override public ConnectionState State {
  619. get {
  620. Task reconnectTask=_currentReconnectionTask;
  621. if (reconnectTask != null && !reconnectTask.IsCompleted) {
  622. return ConnectionState.Open;
  623. }
  624. return InnerConnection.State;
  625. }
  626. }
  627. internal SqlStatistics Statistics {
  628. get {
  629. return _statistics;
  630. }
  631. }
  632. [
  633. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  634. ResCategoryAttribute(Res.DataCategory_Data),
  635. ResDescriptionAttribute(Res.SqlConnection_WorkstationId),
  636. ]
  637. public string WorkstationId {
  638. get {
  639. if (IsContextConnection) {
  640. throw SQL.NotAvailableOnContextConnection();
  641. }
  642. // If not supplied by the user, the default value is the MachineName
  643. // Note: In Longhorn you'll be able to rename a machine without
  644. // rebooting. Therefore, don't cache this machine name.
  645. SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
  646. string result = ((null != constr) ? constr.WorkstationId : null);
  647. if (null == result) {
  648. // getting machine name requires Environment.Permission
  649. // user must have that permission in order to retrieve this
  650. result = Environment.MachineName;
  651. }
  652. return result;
  653. }
  654. }
  655. // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication
  656. [
  657. Browsable(false),
  658. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  659. ResDescriptionAttribute(Res.SqlConnection_Credential),
  660. ]
  661. public SqlCredential Credential
  662. {
  663. get
  664. {
  665. SqlCredential result = _credential;
  666. // When a connection is connecting or is ever opened, make credential available only if "Persist Security Info" is set to true
  667. // otherwise, return null
  668. SqlConnectionString connectionOptions = (SqlConnectionString) UserConnectionOptions;
  669. if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo)
  670. {
  671. result = null;
  672. }
  673. return result;
  674. }
  675. set
  676. {
  677. // If a connection is connecting or is ever opened, user id/password cannot be set
  678. if (!InnerConnection.AllowSetConnectionString)
  679. {
  680. throw ADP.OpenConnectionPropertySet("Credential", InnerConnection.State);
  681. }
  682. // check if the usage of credential has any conflict with the keys used in connection string
  683. if (value != null)
  684. {
  685. // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
  686. // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
  687. // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
  688. if (UsesActiveDirectoryIntegrated((SqlConnectionString) ConnectionOptions)) {
  689. throw SQL.SettingCredentialWithIntegratedInvalid();
  690. }
  691. CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential((SqlConnectionString) ConnectionOptions);
  692. if(_accessToken != null) {
  693. throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
  694. }
  695. }
  696. _credential = value;
  697. // Need to call ConnectionString_Set to do proper pool group check
  698. ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken));
  699. }
  700. }
  701. // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential: check if the usage of credential has any conflict
  702. // with the keys used in connection string
  703. // If there is any conflict, it throws InvalidOperationException
  704. // This is to be used setter of ConnectionString and Credential properties
  705. private void CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(SqlConnectionString connectionOptions)
  706. {
  707. if (UsesClearUserIdOrPassword(connectionOptions))
  708. {
  709. throw ADP.InvalidMixedUsageOfSecureAndClearCredential();
  710. }
  711. if (UsesIntegratedSecurity(connectionOptions))
  712. {
  713. throw ADP.InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity();
  714. }
  715. if (UsesContextConnection(connectionOptions))
  716. {
  717. throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
  718. }
  719. }
  720. // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken: check if the usage of AccessToken has any conflict
  721. // with the keys used in connection string and credential
  722. // If there is any conflict, it throws InvalidOperationException
  723. // This is to be used setter of ConnectionString and AccessToken properties
  724. private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(SqlConnectionString connectionOptions) {
  725. if (UsesClearUserIdOrPassword(connectionOptions)) {
  726. throw ADP.InvalidMixedUsageOfAccessTokenAndUserIDPassword();
  727. }
  728. if (UsesIntegratedSecurity(connectionOptions)) {
  729. throw ADP.InvalidMixedUsageOfAccessTokenAndIntegratedSecurity();
  730. }
  731. if (UsesContextConnection(connectionOptions)) {
  732. throw ADP.InvalidMixedUsageOfAccessTokenAndContextConnection();
  733. }
  734. if (UsesAuthentication(connectionOptions)) {
  735. throw ADP.InvalidMixedUsageOfAccessTokenAndAuthentication();
  736. }
  737. // Check if the usage of AccessToken has the conflict with credential
  738. if (_credential != null) {
  739. throw ADP.InvalidMixedUsageOfAccessTokenAndCredential();
  740. }
  741. }
  742. //
  743. // PUBLIC EVENTS
  744. //
  745. [
  746. ResCategoryAttribute(Res.DataCategory_InfoMessage),
  747. ResDescriptionAttribute(Res.DbConnection_InfoMessage),
  748. ]
  749. public event SqlInfoMessageEventHandler InfoMessage {
  750. add {
  751. Events.AddHandler(EventInfoMessage, value);
  752. }
  753. remove {
  754. Events.RemoveHandler(EventInfoMessage, value);
  755. }
  756. }
  757. public bool FireInfoMessageEventOnUserErrors {
  758. get {
  759. return _fireInfoMessageEventOnUserErrors;
  760. }
  761. set {
  762. _fireInfoMessageEventOnUserErrors = value;
  763. }
  764. }
  765. // Approx. number of times that the internal connection has been reconnected
  766. internal int ReconnectCount {
  767. get {
  768. return _reconnectCount;
  769. }
  770. }
  771. //
  772. // PUBLIC METHODS
  773. //
  774. new public SqlTransaction BeginTransaction() {
  775. // this is just a delegate. The actual method tracks executiontime
  776. return BeginTransaction(IsolationLevel.Unspecified, null);
  777. }
  778. new public SqlTransaction BeginTransaction(IsolationLevel iso) {
  779. // this is just a delegate. The actual method tracks executiontime
  780. return BeginTransaction(iso, null);
  781. }
  782. public SqlTransaction BeginTransaction(string transactionName) {
  783. // Use transaction names only on the outermost pair of nested
  784. // BEGIN...COMMIT or BEGIN...ROLLBACK statements. Transaction names
  785. // are ignored for nested BEGIN's. The only way to rollback a nested
  786. // transaction is to have a save point from a SAVE TRANSACTION call.
  787. return BeginTransaction(IsolationLevel.Unspecified, transactionName);
  788. }
  789. // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
  790. [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
  791. override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
  792. IntPtr hscp;
  793. Bid.ScopeEnter(out hscp, "<prov.SqlConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
  794. try {
  795. DbTransaction transaction = BeginTransaction(isolationLevel);
  796. // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and
  797. // subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction
  798. // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
  799. // until the completion of BeginTransaction with KeepAlive
  800. GC.KeepAlive(this);
  801. return transaction;
  802. }
  803. finally {
  804. Bid.ScopeLeave(ref hscp);
  805. }
  806. }
  807. public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) {
  808. WaitForPendingReconnection();
  809. SqlStatistics statistics = null;
  810. IntPtr hscp;
  811. string xactName = ADP.IsEmpty(transactionName)? "None" : transactionName;
  812. Bid.ScopeEnter(out hscp, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", ObjectID, (int)iso,
  813. xactName);
  814. try {
  815. statistics = SqlStatistics.StartTimer(Statistics);
  816. // NOTE: we used to throw an exception if the transaction name was empty
  817. // (see MDAC 50292) but that was incorrect because we have a BeginTransaction
  818. // method that doesn't have a transactionName argument.
  819. SqlTransaction transaction;
  820. bool isFirstAttempt = true;
  821. do {
  822. transaction = GetOpenConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice
  823. Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt");
  824. isFirstAttempt = false;
  825. } while (transaction.InternalTransaction.ConnectionHasBeenRestored);
  826. // SQLBU 503873 The GetOpenConnection line above doesn't keep a ref on the outer connection (this),
  827. // and it could be collected before the inner connection can hook it to the transaction, resulting in
  828. // a transaction with a null connection property. Use GC.KeepAlive to ensure this doesn't happen.
  829. GC.KeepAlive(this);
  830. return transaction;
  831. }
  832. finally {
  833. Bid.ScopeLeave(ref hscp);
  834. SqlStatistics.StopTimer(statistics);
  835. }
  836. }
  837. override public void ChangeDatabase(string database) {
  838. SqlStatistics statistics = null;
  839. RepairInnerConnection();
  840. Bid.CorrelationTrace("<sc.SqlConnection.ChangeDatabase|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
  841. TdsParser bestEffortCleanupTarget = null;
  842. RuntimeHelpers.PrepareConstrainedRegions();
  843. try {
  844. #if DEBUG
  845. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  846. RuntimeHelpers.PrepareConstrainedRegions();
  847. try {
  848. tdsReliabilitySection.Start();
  849. #else
  850. {
  851. #endif //DEBUG
  852. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
  853. statistics = SqlStatistics.StartTimer(Statistics);
  854. InnerConnection.ChangeDatabase(database);
  855. }
  856. #if DEBUG
  857. finally {
  858. tdsReliabilitySection.Stop();
  859. }
  860. #endif //DEBUG
  861. }
  862. catch (System.OutOfMemoryException e) {
  863. Abort(e);
  864. throw;
  865. }
  866. catch (System.StackOverflowException e) {
  867. Abort(e);
  868. throw;
  869. }
  870. catch (System.Threading.ThreadAbortException e) {
  871. Abort(e);
  872. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  873. throw;
  874. }
  875. finally {
  876. SqlStatistics.StopTimer(statistics);
  877. }
  878. }
  879. static public void ClearAllPools() {
  880. (new SqlClientPermission(PermissionState.Unrestricted)).Demand();
  881. SqlConnectionFactory.SingletonInstance.ClearAllPools();
  882. }
  883. static public void ClearPool(SqlConnection connection) {
  884. ADP.CheckArgumentNull(connection, "connection");
  885. DbConnectionOptions connectionOptions = connection.UserConnectionOptions;
  886. if (null != connectionOptions) {
  887. connectionOptions.DemandPermission();
  888. if (connection.IsContextConnection) {
  889. throw SQL.NotAvailableOnContextConnection();
  890. }
  891. SqlConnectionFactory.SingletonInstance.ClearPool(connection);
  892. }
  893. }
  894. object ICloneable.Clone() {
  895. SqlConnection clone = new SqlConnection(this);
  896. Bid.Trace("<sc.SqlConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
  897. return clone;
  898. }
  899. void CloseInnerConnection() {
  900. // CloseConnection() now handles the lock
  901. // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and
  902. // the command will no longer be cancelable. It might be desirable to be able to cancel the close opperation, but this is
  903. // outside of the scope of Whidbey RTM. See (SqlCommand::Cancel) for other lock.
  904. InnerConnection.CloseConnection(this, ConnectionFactory);
  905. }
  906. override public void Close() {
  907. IntPtr hscp;
  908. Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
  909. Bid.CorrelationTrace("<sc.SqlConnection.Close|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
  910. try {
  911. SqlStatistics statistics = null;
  912. TdsParser bestEffortCleanupTarget = null;
  913. RuntimeHelpers.PrepareConstrainedRegions();
  914. try {
  915. #if DEBUG
  916. TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
  917. RuntimeHelpers.PrepareConstrainedRegions();
  918. try {
  919. tdsReliabilitySection.Start();
  920. #else
  921. {
  922. #endif //DEBUG
  923. bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
  924. statistics = SqlStatistics.StartTimer(Statistics);
  925. Task reconnectTask = _currentReconnectionTask;
  926. if (reconnectTask != null && !reconnectTask.IsCompleted) {
  927. CancellationTokenSource cts = _reconnectionCancellationSource;
  928. if (cts != null) {
  929. cts.Cancel();
  930. }
  931. AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection
  932. if (State != ConnectionState.Open) {// if we cancelled before the connection was opened
  933. OnStateChange(DbConnectionInternal.StateChangeClosed);
  934. }
  935. }
  936. CancelOpenAndWait();
  937. CloseInnerConnection();
  938. GC.SuppressFinalize(this);
  939. if (null != Statistics) {
  940. ADP.TimerCurrent(out _statistics._closeTimestamp);
  941. }
  942. }
  943. #if DEBUG
  944. finally {
  945. tdsReliabilitySection.Stop();
  946. }
  947. #endif //DEBUG
  948. }
  949. catch (System.OutOfMemoryException e) {
  950. Abort(e);
  951. throw;
  952. }
  953. catch (System.StackOverflowException e) {
  954. Abort(e);
  955. throw;
  956. }
  957. catch (System.Threading.ThreadAbortException e) {
  958. Abort(e);
  959. SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
  960. throw;
  961. }
  962. finally {
  963. SqlStatistics.StopTimer(statistics);
  964. //dispose windows identity once connection is closed.
  965. if (_lastIdentity != null) {
  966. _lastIdentity.Dispose();
  967. }
  968. }
  969. }
  970. finally {
  971. SqlDebugContext sdc = _sdc;
  972. _sdc = null;
  973. Bid.ScopeLeave(ref hscp);
  974. if (sdc != null) {
  975. sdc.Dispose();
  976. }
  977. }
  978. }
  979. new public SqlCommand CreateCommand() {
  980. return new SqlCommand(null, this);
  981. }
  982. private void DisposeMe(bool disposing) { // MDAC 65459
  983. // clear credential and AccessToken here rather than in IDisposable.Dispose as these are specific to SqlConnection only
  984. // IDisposable.Dispose is generated code from a template and used by other providers as well
  985. _credential = null;
  986. _accessToken = null;
  987. if (!disposing) {
  988. // DevDiv2 Bug 457934:SQLConnection leaks when not disposed
  989. // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/457934
  990. // 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
  991. // For pooled connections, we will rely on the pool reclaiming the connection
  992. var innerConnection = (InnerConnection as SqlInternalConnectionTds);
  993. if ((innerConnection != null) && (!innerConnection.ConnectionOptions.Pooling)) {
  994. var parser = innerConnection.Parser;
  995. if ((parser != null) && (parser.