private:System::Void chiqishToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
private:System::Void dasturHaqidaToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
private:System::Void saqlashToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
private: System::Void panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
private:System::Void saqlashToolStripMenuItem_Click_1(System::Object^ sender, System::EventArgs^ e) {
|
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Configuration;
|
|
using System.Configuration.Assemblies;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Data.ProviderBase;
|
|
using System.Data.Sql;
|
|
using System.Data.SqlTypes;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.ConstrainedExecution;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Remoting;
|
|
using System.Runtime.Serialization.Formatters;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.Reflection;
|
|
using System.Runtime.Versioning;
|
|
|
|
using Microsoft.SqlServer.Server;
|
|
using System.Security.Principal;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
[DefaultEvent("InfoMessage")]
|
|
public sealed partial class SqlConnection: DbConnection, ICloneable {
|
|
|
|
static SqlConnection() {
|
|
SqlColumnEncryptionEnclaveProviderConfigurationSection sqlColumnEncryptionEnclaveProviderConfigurationSection = null;
|
|
try {
|
|
sqlColumnEncryptionEnclaveProviderConfigurationSection = (SqlColumnEncryptionEnclaveProviderConfigurationSection)ConfigurationManager.GetSection("SqlColumnEncryptionEnclaveProviders");
|
|
} catch (ConfigurationErrorsException e) {
|
|
throw SQL.CannotGetSqlColumnEncryptionEnclaveProviderConfig(e);
|
|
}
|
|
|
|
sqlColumnEncryptionEnclaveProviderConfigurationManager = new SqlColumnEncryptionEnclaveProviderConfigurationManager(sqlColumnEncryptionEnclaveProviderConfigurationSection);
|
|
}
|
|
|
|
static private readonly object EventInfoMessage = new object();
|
|
static internal readonly SqlColumnEncryptionEnclaveProviderConfigurationManager sqlColumnEncryptionEnclaveProviderConfigurationManager;
|
|
|
|
// System column encryption key store providers are added by default
|
|
static private readonly Dictionary _SystemColumnEncryptionKeyStoreProviders
|
|
= new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()},
|
|
{SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()},
|
|
{SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()}
|
|
};
|
|
|
|
///
|
|
/// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary.
|
|
/// Custom provider list can only supplied once per application.
|
|
///
|
|
static private ReadOnlyDictionary _CustomColumnEncryptionKeyStoreProviders;
|
|
|
|
// Lock to control setting of _CustomColumnEncryptionKeyStoreProviders
|
|
static private readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object();
|
|
|
|
///
|
|
/// Dictionary object holding trusted key paths for various SQL Servers.
|
|
/// Key to the dictionary is a SQL Server Name
|
|
/// IList contains a list of trusted key paths.
|
|
///
|
|
static private readonly ConcurrentDictionary> _ColumnEncryptionTrustedMasterKeyPaths
|
|
= new ConcurrentDictionary>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
|
|
capacity: 1,
|
|
comparer: StringComparer.OrdinalIgnoreCase);
|
|
|
|
[
|
|
DefaultValue(null),
|
|
ResCategoryAttribute(Res.DataCategory_Data),
|
|
ResDescriptionAttribute(Res.TCE_SqlConnection_TrustedColumnMasterKeyPaths),
|
|
]
|
|
static public IDictionary> ColumnEncryptionTrustedMasterKeyPaths
|
|
{
|
|
get
|
|
{
|
|
return _ColumnEncryptionTrustedMasterKeyPaths;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Defines whether query metadata caching is enabled.
|
|
///
|
|
static private bool _ColumnEncryptionQueryMetadataCacheEnabled = true;
|
|
|
|
[
|
|
DefaultValue(null),
|
|
ResCategoryAttribute(Res.DataCategory_Data),
|
|
ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled),
|
|
]
|
|
static public bool ColumnEncryptionQueryMetadataCacheEnabled
|
|
{
|
|
get
|
|
{
|
|
return _ColumnEncryptionQueryMetadataCacheEnabled;
|
|
}
|
|
set
|
|
{
|
|
_ColumnEncryptionQueryMetadataCacheEnabled = value;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Defines whether query metadata caching is enabled.
|
|
///
|
|
static private TimeSpan _ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2);
|
|
|
|
[
|
|
DefaultValue(null),
|
|
ResCategoryAttribute(Res.DataCategory_Data),
|
|
ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl),
|
|
]
|
|
static public TimeSpan ColumnEncryptionKeyCacheTtl
|
|
{
|
|
get
|
|
{
|
|
return _ColumnEncryptionKeyCacheTtl;
|
|
}
|
|
set
|
|
{
|
|
_ColumnEncryptionKeyCacheTtl = value;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// This function should only be called once in an app. This does shallow copying of the dictionary so that
|
|
/// the app cannot alter the custom provider list once it has been set.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// Dictionary customKeyStoreProviders = new Dictionary();
|
|
/// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
|
|
/// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
|
|
/// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
|
|
///
|
|
/// Custom column encryption key provider dictionary
|
|
static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary customProviders)
|
|
{
|
|
|
|
// Return when the provided dictionary is null.
|
|
if (customProviders == null)
|
|
{
|
|
throw SQL.NullCustomKeyStoreProviderDictionary();
|
|
}
|
|
|
|
// Validate that custom provider list doesn't contain any of system provider list
|
|
foreach (string key in customProviders.Keys)
|
|
{
|
|
// Validate the provider name
|
|
//
|
|
// Check for null or empty
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
{
|
|
throw SQL.EmptyProviderName();
|
|
}
|
|
|
|
// Check if the name starts with MSSQL_, since this is reserved namespace for system providers.
|
|
if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix);
|
|
}
|
|
|
|
// Validate the provider value
|
|
if (customProviders[key] == null)
|
|
{
|
|
throw SQL.NullProviderValue(key);
|
|
}
|
|
}
|
|
|
|
lock (_CustomColumnEncryptionKeyProvidersLock)
|
|
{
|
|
// Provider list can only be set once
|
|
if (_CustomColumnEncryptionKeyStoreProviders != null)
|
|
{
|
|
throw SQL.CanOnlyCallOnce();
|
|
}
|
|
|
|
// Create a temporary dictionary and then add items from the provided dictionary.
|
|
// Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs
|
|
// in the provided customerProviders dictionary.
|
|
Dictionary customColumnEncryptionKeyStoreProviders =
|
|
new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase);
|
|
|
|
// Set the dictionary to the ReadOnly dictionary.
|
|
_CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary(customColumnEncryptionKeyStoreProviders);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// This function walks through both system and custom column encryption key store providers and returns an object if found.
|
|
///
|
|
/// Provider Name to be searched in System Provider diction and Custom provider dictionary.
|
|
/// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance.
|
|
/// true if the provider is found, else returns false
|
|
static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) {
|
|
Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid");
|
|
|
|
// Initialize the out parameter
|
|
columnKeyStoreProvider = null;
|
|
|
|
// Search in the sytem provider list.
|
|
if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
lock (_CustomColumnEncryptionKeyProvidersLock)
|
|
{
|
|
// If custom provider is not set, then return false
|
|
if (_CustomColumnEncryptionKeyStoreProviders == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Search in the custom provider list
|
|
return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// This function returns a list of system provider dictionary currently supported by this driver.
|
|
///
|
|
/// Combined list of provider names
|
|
static internal List GetColumnEncryptionSystemKeyStoreProviders() {
|
|
HashSet providerNames = new HashSet(_SystemColumnEncryptionKeyStoreProviders.Keys);
|
|
return providerNames.ToList();
|
|
}
|
|
|
|
///
|
|
/// This function returns a list of custom provider dictionary currently registered.
|
|
///
|
|
/// Combined list of provider names
|
|
static internal List GetColumnEncryptionCustomKeyStoreProviders() {
|
|
if(_CustomColumnEncryptionKeyStoreProviders != null)
|
|
{
|
|
HashSet providerNames = new HashSet(_CustomColumnEncryptionKeyStoreProviders.Keys);
|
|
return providerNames.ToList();
|
|
}
|
|
|
|
return new List();
|
|
}
|
|
|
|
private SqlDebugContext _sdc; // SQL Debugging support
|
|
|
|
private bool _AsyncCommandInProgress;
|
|
|
|
// SQLStatistics support
|
|
internal SqlStatistics _statistics;
|
|
private bool _collectstats;
|
|
|
|
private bool _fireInfoMessageEventOnUserErrors; // False by default
|
|
|
|
// root task associated with current async invocation
|
|
Tuple, Task> _currentCompletion;
|
|
|
|
private SqlCredential _credential; // SQL authentication password stored in SecureString
|
|
private string _connectionString;
|
|
private int _connectRetryCount;
|
|
|
|
private string _accessToken; // Access Token to be used for token based authententication
|
|
|
|
// connection resiliency
|
|
private object _reconnectLock = new object();
|
|
internal Task _currentReconnectionTask;
|
|
private Task _asyncWaitingForReconnection; // current async task waiting for reconnection in non-MARS connections
|
|
private Guid _originalConnectionId = Guid.Empty;
|
|
private CancellationTokenSource _reconnectionCancellationSource;
|
|
internal SessionData _recoverySessionData;
|
|
internal WindowsIdentity _lastIdentity;
|
|
internal WindowsIdentity _impersonateIdentity;
|
|
private int _reconnectCount;
|
|
|
|
// 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
|
|
// 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
|
|
// using SqlConnection.Open() method.
|
|
internal bool _applyTransientFaultHandling = false;
|
|
|
|
public SqlConnection(string connectionString) : this(connectionString, null) {
|
|
}
|
|
|
|
public SqlConnection(string connectionString, SqlCredential credential) : this() {
|
|
ConnectionString = connectionString; // setting connection string first so that ConnectionOption is available
|
|
if (credential != null)
|
|
{
|
|
// The following checks are necessary as setting Credential property will call CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential
|
|
// CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential it will throw InvalidOperationException rather than Arguemtn exception
|
|
// Need to call setter on Credential property rather than setting _credential directly as pool groups need to be checked
|
|
SqlConnectionString connectionOptions = (SqlConnectionString) ConnectionOptions;
|
|
if (UsesClearUserIdOrPassword(connectionOptions))
|
|
{
|
|
throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
|
|
}
|
|
|
|
if (UsesIntegratedSecurity(connectionOptions))
|
|
{
|
|
throw ADP.InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity();
|
|
}
|
|
|
|
if (UsesContextConnection(connectionOptions))
|
|
{
|
|
throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
|
|
}
|
|
|
|
if (UsesActiveDirectoryIntegrated(connectionOptions))
|
|
{
|
|
throw SQL.SettingCredentialWithIntegratedArgument();
|
|
}
|
|
|
|
Credential = credential;
|
|
}
|
|
// else
|
|
// credential == null: we should not set "Credential" as this will do additional validation check and
|
|
// checking pool groups which is not necessary. All necessary operation is already done by calling "ConnectionString = connectionString"
|
|
CacheConnectionStringProperties();
|
|
}
|
|
|
|
private SqlConnection(SqlConnection connection) { // Clone
|
|
GC.SuppressFinalize(this);
|
|
CopyFrom(connection);
|
|
_connectionString = connection._connectionString;
|
|
if (connection._credential != null)
|
|
{
|
|
SecureString password = connection._credential.Password.Copy();
|
|
password.MakeReadOnly();
|
|
_credential = new SqlCredential(connection._credential.UserId, password);
|
|
}
|
|
_accessToken = connection._accessToken;
|
|
CacheConnectionStringProperties();
|
|
}
|
|
|
|
// This method will be called once connection string is set or changed.
|
|
private void CacheConnectionStringProperties() {
|
|
SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
|
|
if (connString != null) {
|
|
_connectRetryCount = connString.ConnectRetryCount;
|
|
// For Azure SQL connection, set _connectRetryCount to 2 instead of 1 will greatly improve recovery
|
|
// success rate
|
|
if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource))
|
|
{
|
|
_connectRetryCount = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// PUBLIC PROPERTIES
|
|
//
|
|
|
|
// used to start/stop collection of statistics data and do verify the current state
|
|
//
|
|
// devnote: start/stop should not performed using a property since it requires execution of code
|
|
//
|
|
// start statistics
|
|
// set the internal flag (_statisticsEnabled) to true.
|
|
// Create a new SqlStatistics object if not already there.
|
|
// connect the parser to the object.
|
|
// if there is no parser at this time we need to connect it after creation.
|
|
//
|
|
|
|
[
|
|
DefaultValue(false),
|
|
ResCategoryAttribute(Res.DataCategory_Data),
|
|
ResDescriptionAttribute(Res.SqlConnection_StatisticsEnabled),
|
|
]
|
|
public bool StatisticsEnabled {
|
|
get {
|
|
return (_collectstats);
|
|
}
|
|
set {
|
|
if (IsContextConnection) {
|
|
if (value) {
|
|
throw SQL.NotAvailableOnContextConnection();
|
|
}
|
|
}
|
|
else {
|
|
if (value) {
|
|
// start
|
|
if (ConnectionState.Open == State) {
|
|
if (null == _statistics) {
|
|
_statistics = new SqlStatistics();
|
|
ADP.TimerCurrent(out _statistics._openTimestamp);
|
|
}
|
|
// set statistics on the parser
|
|
// update timestamp;
|
|
Debug.Assert(Parser != null, "Where's the parser?");
|
|
Parser.Statistics = _statistics;
|
|
}
|
|
}
|
|
else {
|
|
// stop
|
|
if (null != _statistics) {
|
|
if (ConnectionState.Open == State) {
|
|
// remove statistics from parser
|
|
// update timestamp;
|
|
TdsParser parser = Parser;
|
|
Debug.Assert(parser != null, "Where's the parser?");
|
|
parser.Statistics = null;
|
|
ADP.TimerCurrent(out _statistics._closeTimestamp);
|
|
}
|
|
}
|
|
}
|
|
this._collectstats = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool AsyncCommandInProgress {
|
|
get {
|
|
return (_AsyncCommandInProgress);
|
|
}
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
set {
|
|
_AsyncCommandInProgress = value;
|
|
}
|
|
}
|
|
|
|
internal bool IsContextConnection {
|
|
get {
|
|
SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
|
|
return UsesContextConnection(opt);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Is this connection using column encryption ?
|
|
///
|
|
internal bool IsColumnEncryptionSettingEnabled {
|
|
get {
|
|
SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
|
|
return opt != null ? opt.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled : false;
|
|
}
|
|
}
|
|
|
|
///
|
|
|
|
/// Get enclave attestation url to be used with enclave based Always Encrypted
|
|
|
///
|
|
|
internal string EnclaveAttestationUrl {
|
|
|
get {
|
|
|
SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
|
|
|
return opt.EnclaveAttestationUrl;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Is this connection is a Context Connection?
|
|
|
private bool UsesContextConnection(SqlConnectionString opt)
|
|
|
{
|
|
|
return opt != null ? opt.ContextConnection : false;
|
|
|
}
|
|
|
|
|
|
private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt)
|
|
|
{
|
|
|
return opt != null ? opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated : false;
|
|
|
}
|
|
|
|
|
|
private bool UsesAuthentication(SqlConnectionString opt) {
|
|
|
return opt != null ? opt.Authentication != SqlAuthenticationMethod.NotSpecified : false;
|
|
|
}
|
|
|
|
|
|
// Does this connection uses Integrated Security?
|
|
|
private bool UsesIntegratedSecurity(SqlConnectionString opt) {
|
|
|
return opt != null ? opt.IntegratedSecurity : false;
|
|
|
}
|
|
|
|
|
|
// Does this connection uses old style of clear userID or Password in connection string?
|
|
|
private bool UsesClearUserIdOrPassword(SqlConnectionString opt) {
|
|
|
bool result = false;
|
|
|
if (null != opt) {
|
|
|
result = (!ADP.IsEmpty(opt.UserID) || !ADP.IsEmpty(opt.Password));
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
internal SqlConnectionString.TransactionBindingEnum TransactionBinding {
|
|
|
get {
|
|
|
return ((SqlConnectionString)ConnectionOptions).TransactionBinding;
|
|
|
}
|
|
|
}
|