From 0177b19bc15ecc1c9f4a4cc2fef97e7802ebd552 Mon Sep 17 00:00:00 2001 From: SqlClient DevOps Date: Thu, 16 Apr 2020 09:37:48 -0700 Subject: [PATCH 01/13] [Scheduled Run] Localized resource files from OneLocBuild --- .../netfx/src/Resources/Strings.de.resx | 2 +- .../netfx/src/Resources/Strings.es.resx | 2 +- .../netfx/src/Resources/Strings.fr.resx | 2 +- .../netfx/src/Resources/Strings.ja.resx | 2 +- .../netfx/src/Resources/Strings.ko.resx | 2 +- .../netfx/src/Resources/Strings.pt-BR.resx | 2 +- .../netfx/src/Resources/Strings.ru.resx | 2 +- .../netfx/src/Resources/Strings.zh-Hans.resx | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 4b6a1b696e..58a66f1782 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -4528,6 +4528,6 @@ Fehler beim Generieren des Enclave-Pakets. In der Verbindungszeichenfolge wurde kein Nachweisprotokoll angegeben, aber für die Abfrage sind Enclave-Berechnungen erforderlich. - UDT size must be less than {1}, size: {0} + Die UDT-Größe muss kleiner als {1} sein. Größe: {0} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 25ec202a40..4ad3c8946e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -4528,6 +4528,6 @@ Se ha producido un error durante la generación del paquete enclave. No se ha especificado el protocolo de atestación en la cadena de conexión, pero la consulta requiere cálculos de enclave. - UDT size must be less than {1}, size: {0} + El tamaño de UDT debe ser menor que {1}; tamaño: {0} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index d6a8474971..a8ccbee9b1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -4528,6 +4528,6 @@ Une erreur s'est produite lors de la génération du package d'enclave. Le protocole d'attestation n'a pas été spécifié dans la chaîne de connexion, mais la requête nécessite des calculs d'enclave. - UDT size must be less than {1}, size: {0} + La taille UDT doit être inférieure à {1}, taille : {0} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 366ee2b609..daaed69a9f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -4528,6 +4528,6 @@ エンクレーブ パッケージの生成中にエラーが発生しました。接続文字列の中では構成証明プロトコルが指定されていませんが、クエリではエンクレーブ計算が必要です。 - UDT size must be less than {1}, size: {0} + UDT のサイズは {1} より小さくなければなりません。サイズ: {0} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index e2c24be5d5..b943f50402 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -4528,6 +4528,6 @@ enclave 패키지를 생성하는 동안 오류가 발생했습니다. 연결 문자열에 증명 프로토콜이 지정되지 않았지만 쿼리에 enclave 계산이 필요합니다. - UDT size must be less than {1}, size: {0} + UDT 크기는 {1}보다 작아야 합니다. 크기: {0} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index 2fc34c4b63..328c0665ff 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -4528,6 +4528,6 @@ Erro ao gerar o pacote de enclave. O Protocolo de Atestado não foi especificado na cadeia de conexão, mas a consulta exige computações de enclave. - UDT size must be less than {1}, size: {0} + O tamanho do UDT precisa ser menor que {1}. Tamanho: {0} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index a99a120e69..d76623d14d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -4528,6 +4528,6 @@ Возникла ошибка при создании пакета анклава. Запрос требует вычислений на основе анклава, но в строке подключения не указан протокол аттестации. - UDT size must be less than {1}, size: {0} + Размер пользовательского типа должен быть меньше {1}, текущий размер: {0}. \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 577c9d198a..a28628a017 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -4528,6 +4528,6 @@ 生成 enclave 包时出错。尚未在连接字符串中指定证明协议,但查询需要 enclave 计算。 - UDT size must be less than {1}, size: {0} + UDT 大小必须小于 {1},大小为 {0} \ No newline at end of file From 46ab2ec9a3d230723006357dcccde599d14f3c87 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 16 Apr 2020 12:29:48 -0700 Subject: [PATCH 02/13] Add "Transaction Id" and "Client Version" in Diagnostic Source traces (#515) --- .../SqlClientDiagnosticListenerExtensions.cs | 30 +++++++--- .../Microsoft/Data/SqlClient/SqlCommand.cs | 58 +++++++++---------- .../Data/SqlClient/SqlTransaction.cs | 18 +++--- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs index 288edb4055..d8b8b073c3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs @@ -38,7 +38,7 @@ internal static class SqlClientDiagnosticListenerExtensions public const string SqlAfterRollbackTransaction = SqlClientPrefix + nameof(WriteTransactionRollbackAfter); public const string SqlErrorRollbackTransaction = SqlClientPrefix + nameof(WriteTransactionRollbackError); - public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand sqlCommand, [CallerMemberName] string operation = "") + public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand sqlCommand, SqlTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeExecuteCommand)) { @@ -52,6 +52,7 @@ public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand Operation = operation, ConnectionId = sqlCommand.Connection?.ClientConnectionId, Command = sqlCommand, + transaction?.InternalTransaction?.TransactionId, Timestamp = Stopwatch.GetTimestamp() }); @@ -61,7 +62,7 @@ public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand return Guid.Empty; } - public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, [CallerMemberName] string operation = "") + public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, SqlTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterExecuteCommand)) { @@ -73,13 +74,14 @@ public static void WriteCommandAfter(this DiagnosticListener @this, Guid operati Operation = operation, ConnectionId = sqlCommand.Connection?.ClientConnectionId, Command = sqlCommand, + transaction?.InternalTransaction?.TransactionId, Statistics = sqlCommand.Statistics?.GetDictionary(), Timestamp = Stopwatch.GetTimestamp() }); } } - public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, Exception ex, [CallerMemberName] string operation = "") + public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, SqlTransaction transaction, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorExecuteCommand)) { @@ -91,6 +93,7 @@ public static void WriteCommandError(this DiagnosticListener @this, Guid operati Operation = operation, ConnectionId = sqlCommand.Connection?.ClientConnectionId, Command = sqlCommand, + transaction?.InternalTransaction?.TransactionId, Exception = ex, Timestamp = Stopwatch.GetTimestamp() }); @@ -110,6 +113,7 @@ public static Guid WriteConnectionOpenBefore(this DiagnosticListener @this, SqlC OperationId = operationId, Operation = operation, Connection = sqlConnection, + ClientVersion = ThisAssembly.InformationalVersion, Timestamp = Stopwatch.GetTimestamp() }); @@ -131,6 +135,7 @@ public static void WriteConnectionOpenAfter(this DiagnosticListener @this, Guid Operation = operation, ConnectionId = sqlConnection.ClientConnectionId, Connection = sqlConnection, + ClientVersion = ThisAssembly.InformationalVersion, Statistics = sqlConnection.Statistics?.GetDictionary(), Timestamp = Stopwatch.GetTimestamp() }); @@ -149,6 +154,7 @@ public static void WriteConnectionOpenError(this DiagnosticListener @this, Guid Operation = operation, ConnectionId = sqlConnection.ClientConnectionId, Connection = sqlConnection, + ClientVersion = ThisAssembly.InformationalVersion, Exception = ex, Timestamp = Stopwatch.GetTimestamp() }); @@ -216,7 +222,7 @@ public static void WriteConnectionCloseError(this DiagnosticListener @this, Guid } } - public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, [CallerMemberName] string operation = "") + public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeCommitTransaction)) { @@ -230,6 +236,7 @@ public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, I Operation = operation, IsolationLevel = isolationLevel, Connection = connection, + transaction?.TransactionId, Timestamp = Stopwatch.GetTimestamp() }); @@ -239,7 +246,7 @@ public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, I return Guid.Empty; } - public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, [CallerMemberName] string operation = "") + public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterCommitTransaction)) { @@ -251,12 +258,13 @@ public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Gu Operation = operation, IsolationLevel = isolationLevel, Connection = connection, + transaction?.TransactionId, Timestamp = Stopwatch.GetTimestamp() }); } } - public static void WriteTransactionCommitError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, Exception ex, [CallerMemberName] string operation = "") + public static void WriteTransactionCommitError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorCommitTransaction)) { @@ -268,13 +276,14 @@ public static void WriteTransactionCommitError(this DiagnosticListener @this, Gu Operation = operation, IsolationLevel = isolationLevel, Connection = connection, + transaction?.TransactionId, Exception = ex, Timestamp = Stopwatch.GetTimestamp() }); } } - public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, string transactionName, [CallerMemberName] string operation = "") + public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName = null, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeRollbackTransaction)) { @@ -288,6 +297,7 @@ public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, Operation = operation, IsolationLevel = isolationLevel, Connection = connection, + transaction?.TransactionId, TransactionName = transactionName, Timestamp = Stopwatch.GetTimestamp() }); @@ -298,7 +308,7 @@ public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, return Guid.Empty; } - public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, string transactionName, [CallerMemberName] string operation = "") + public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName = null, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterRollbackTransaction)) { @@ -310,13 +320,14 @@ public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, Operation = operation, IsolationLevel = isolationLevel, Connection = connection, + transaction?.TransactionId, TransactionName = transactionName, Timestamp = Stopwatch.GetTimestamp() }); } } - public static void WriteTransactionRollbackError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, string transactionName, Exception ex, [CallerMemberName] string operation = "") + public static void WriteTransactionRollbackError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, Exception ex, string transactionName = null, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorRollbackTransaction)) { @@ -328,6 +339,7 @@ public static void WriteTransactionRollbackError(this DiagnosticListener @this, Operation = operation, IsolationLevel = isolationLevel, Connection = connection, + transaction?.TransactionId, TransactionName = transactionName, Exception = ex, Timestamp = Stopwatch.GetTimestamp() diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 66bd59c4e2..9f71f8a5b3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -943,7 +943,7 @@ override public object ExecuteScalar() // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); SqlStatistics statistics = null; @@ -981,11 +981,11 @@ override public object ExecuteScalar() WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); if (e != null) { - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } else { - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } } } @@ -1027,7 +1027,7 @@ override public int ExecuteNonQuery() // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); SqlStatistics statistics = null; @@ -1051,11 +1051,11 @@ override public int ExecuteNonQuery() SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } else { - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } } } @@ -1517,7 +1517,7 @@ public XmlReader ExecuteXmlReader() _pendingCancel = false; bool success = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); SqlStatistics statistics = null; long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0}", ObjectID); @@ -1553,11 +1553,11 @@ public XmlReader ExecuteXmlReader() WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); if (e != null) { - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } else { - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } } } @@ -1844,7 +1844,7 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); SqlStatistics statistics = null; bool success = false; @@ -1873,11 +1873,11 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) if (e != null) { - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } else { - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } } } @@ -2248,7 +2248,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool is public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) { SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2274,7 +2274,7 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok if (t.IsFaulted) { Exception e = t.Exception.InnerException; - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } else @@ -2287,13 +2287,13 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok { source.SetResult(t.Result); } - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } }, TaskScheduler.Default); } catch (Exception e) { - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } @@ -2337,7 +2337,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); Guid operationId = default(Guid); if (!_parentOperationStarted) - operationId = _diagnosticListener.WriteCommandBefore(this); + operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2364,7 +2364,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b { Exception e = t.Exception.InnerException; if (!_parentOperationStarted) - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } else @@ -2378,14 +2378,14 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b source.SetResult(t.Result); } if (!_parentOperationStarted) - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } }, TaskScheduler.Default); } catch (Exception e) { if (!_parentOperationStarted) - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } @@ -2397,7 +2397,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b public override Task ExecuteScalarAsync(CancellationToken cancellationToken) { _parentOperationStarted = true; - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); return ExecuteReaderAsync(cancellationToken).ContinueWith((executeTask) => { @@ -2408,7 +2408,7 @@ public override Task ExecuteScalarAsync(CancellationToken cancellationTo } else if (executeTask.IsFaulted) { - _diagnosticListener.WriteCommandError(operationId, this, executeTask.Exception.InnerException); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, executeTask.Exception.InnerException); source.SetException(executeTask.Exception.InnerException); } else @@ -2426,7 +2426,7 @@ public override Task ExecuteScalarAsync(CancellationToken cancellationTo else if (readTask.IsFaulted) { reader.Dispose(); - _diagnosticListener.WriteCommandError(operationId, this, readTask.Exception.InnerException); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, readTask.Exception.InnerException); source.SetException(readTask.Exception.InnerException); } else @@ -2454,12 +2454,12 @@ public override Task ExecuteScalarAsync(CancellationToken cancellationTo } if (exception != null) { - _diagnosticListener.WriteCommandError(operationId, this, exception); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, exception); source.SetException(exception); } else { - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); source.SetResult(result); } } @@ -2486,7 +2486,7 @@ public Task ExecuteXmlReaderAsync() public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) { SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Guid operationId = _diagnosticListener.WriteCommandBefore(this); + Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2512,7 +2512,7 @@ public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken if (t.IsFaulted) { Exception e = t.Exception.InnerException; - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } else @@ -2525,13 +2525,13 @@ public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken { source.SetResult(t.Result); } - _diagnosticListener.WriteCommandAfter(operationId, this); + _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } }, TaskScheduler.Default); } catch (Exception e) { - _diagnosticListener.WriteCommandError(operationId, this, e); + _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs index a1b395e736..fb34462779 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -137,7 +137,7 @@ internal SqlStatistics Statistics override public void Commit() { Exception e = null; - Guid operationId = s_diagnosticListener.WriteTransactionCommitBefore(_isolationLevel, _connection); + Guid operationId = s_diagnosticListener.WriteTransactionCommitBefore(_isolationLevel, _connection, InternalTransaction); ZombieCheck(); @@ -176,11 +176,11 @@ override public void Commit() SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { - s_diagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, _connection, e); + s_diagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, _connection, InternalTransaction, e); } else { - s_diagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, _connection); + s_diagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, _connection, InternalTransaction); } _isFromAPI = false; @@ -204,7 +204,7 @@ protected override void Dispose(bool disposing) override public void Rollback() { Exception e = null; - Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, null); + Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, InternalTransaction); if (IsYukonPartialZombie) { @@ -238,11 +238,11 @@ override public void Rollback() SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { - s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, null, e); + s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, InternalTransaction, e); } else { - s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, null); + s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, InternalTransaction); } _isFromAPI = false; } @@ -253,7 +253,7 @@ override public void Rollback() public void Rollback(string transactionName) { Exception e = null; - Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, transactionName); + Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, InternalTransaction, transactionName); ZombieCheck(); long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" {0} transactionName='{1}'", ObjectID, transactionName); @@ -277,11 +277,11 @@ public void Rollback(string transactionName) SqlClientEventSource.Log.ScopeLeaveEvent(scopeID); if (e != null) { - s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, transactionName, e); + s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, InternalTransaction, e, transactionName); } else { - s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, transactionName); + s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, InternalTransaction, transactionName); } _isFromAPI = false; From 68eff1157316d7e31470bbcf0c9a80b64160fa25 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 16 Apr 2020 16:47:48 -0700 Subject: [PATCH 03/13] Fix Connection Strings for Tests (#529) --- .../ManualTests/DataCommon/DataTestUtility.cs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index a65aa77064..2cc84ba8dc 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -52,24 +52,6 @@ public static class DataTestUtility private static Dictionary AvailableDatabases; private static TraceEventListener TraceListener; - public static IEnumerable ConnectionStrings - { - get - { - if (!string.IsNullOrEmpty(TCPConnectionString)) - { - yield return TCPConnectionString; - } - else if (!string.IsNullOrEmpty(NPConnectionString)) - { - yield return NPConnectionString; - } - foreach (string connStrAE in AEConnStrings) - { - yield return connStrAE; - } - } - } private class Config { @@ -174,6 +156,28 @@ static DataTestUtility() } } + public static IEnumerable ConnectionStrings + { + get + { + if (!string.IsNullOrEmpty(TCPConnectionString)) + { + yield return TCPConnectionString; + } + // Named Pipes are not supported on Unix platform and for Azure DB + if (Environment.OSVersion.Platform != PlatformID.Unix && IsNotAzureServer() && !string.IsNullOrEmpty(NPConnectionString)) + { + yield return NPConnectionString; + } + if (EnclaveEnabled) + { + foreach (var connStr in AEConnStrings) + { + yield return connStr; + } + } + } + } private static string GenerateAccessToken(string authorityURL, string aADAuthUserID, string aADAuthPassword) { return AcquireTokenAsync(authorityURL, aADAuthUserID, aADAuthPassword).Result; From 5ceec82f1fd5df5e6801bbbe7a50416e2586fd6f Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 21 Apr 2020 10:25:30 -0700 Subject: [PATCH 04/13] Introduce Docker debugging with Docker Compose orchestration support (#471) --- .dockerignore | 25 +++ BUILDGUIDE.md | 31 +++ src/.dockerignore | 25 +++ src/Microsoft.Data.SqlClient.sln | 212 ++++++++++++++++++ .../tests/DockerLinuxTest/Dockerfile | 23 ++ ...soft.Data.SqlClient.DockerLinuxTest.csproj | 15 ++ .../tests/DockerLinuxTest/Program.cs | 28 +++ .../Properties/launchSettings.json | 10 + src/docker-compose.dcproj | 18 ++ src/docker-compose.override.yml | 3 + src/docker-compose.yml | 18 ++ tools/credScan/CredScanSuppressions.json | 56 +++-- 12 files changed, 440 insertions(+), 24 deletions(-) create mode 100644 .dockerignore create mode 100644 src/.dockerignore create mode 100644 src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Dockerfile create mode 100644 src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Microsoft.Data.SqlClient.DockerLinuxTest.csproj create mode 100644 src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Program.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Properties/launchSettings.json create mode 100644 src/docker-compose.dcproj create mode 100644 src/docker-compose.override.yml create mode 100644 src/docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..e7b690f114 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index 4fb072c072..3b933dfa11 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -184,3 +184,34 @@ Managed SNI can be enabled on Windows by enabling the below AppContext switch: Scaled decimal parameter truncation can be enabled by enabling the below AppContext switch: **"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal"** + +## Debugging SqlClient on Linux from Windows + +For enhanced developer experience, we support debugging SqlClient on Linux from Windows, using the project "**Microsoft.Data.SqlClient.DockerLinuxTest**" that requires "Container Tools" to be enabled in Visual Studio. You may import configuration: [VS19Components.vsconfig](./tools/vsconfig/VS19Components.vsconfig) if not enabled already. + +This project is also included in `docker-compose.yml` to demonstrate connectivity with SQL Server docker image. + +To run the same: +1. Build the Solution in Visual Studio +2. Set `docker-compose` as Startup Project +3. Run "Docker-Compose" launch configuration. +4. You will see similar message in Debug window: + ```log + Connected to SQL Server v15.00.4023 from Unix 4.19.76.0 + The program 'dotnet' has exited with code 0 (0x0). + ``` +5. Now you can write code in [Program.cs](/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Program.cs) to debug SqlClient on Linux! + +### Troubleshooting Docker issues + +There may be times where connection cannot be made to SQL Server, we found below ideas helpful: + +- Clear Docker images to create clean image from time-to-time, and clear docker cache if needed by running `docker system prune` in Command Prompt. + +- If you face `sni.dll not found` errors when debugging, try updating below properties in netcore\Microsoft.Data.SqlClient.csproj file and try again: + ```xml + Unix + false + true + ``` + ``` diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 0000000000..e7b690f114 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 6c9a8f59b8..63597ed179 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -63,6 +63,114 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "add-ons", "add-ons", "{C972 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.XUnitExtensions", "Microsoft.Data.SqlClient\tests\tools\Microsoft.DotNet.XUnitExtensions\Microsoft.DotNet.XUnitExtensions.csproj", "{FDA6971D-9F57-4DA4-B10A-261C91684CFC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{ED952CF7-84DF-437A-B066-F516E9BE1C2C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "snippets", "snippets", "{71F356DC-DFA3-4163-8BFE-D268722CE189}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data", "Microsoft.Data", "{908C7DD3-C999-40A6-9433-9F5ACA7C36F5}" + ProjectSection(SolutionItems) = preProject + ..\doc\snippets\Microsoft.Data\OperationAbortedException.xml = ..\doc\snippets\Microsoft.Data\OperationAbortedException.xml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.Sql", "Microsoft.Data.Sql", "{0CE216CE-8072-4985-B248-61F0D0BE9C2E}" + ProjectSection(SolutionItems) = preProject + ..\doc\snippets\Microsoft.Data.Sql\SqlNotificationRequest.xml = ..\doc\snippets\Microsoft.Data.Sql\SqlNotificationRequest.xml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient", "{C05F4FFE-6A14-4409-AA0A-10630BE4F1EE}" + ProjectSection(SolutionItems) = preProject + ..\doc\snippets\Microsoft.Data.SqlClient\ApplicationIntent.xml = ..\doc\snippets\Microsoft.Data.SqlClient\ApplicationIntent.xml + ..\doc\snippets\Microsoft.Data.SqlClient\OnChangeEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\OnChangeEventHandler.xml + ..\doc\snippets\Microsoft.Data.SqlClient\PoolBlockingPeriod.xml = ..\doc\snippets\Microsoft.Data.SqlClient\PoolBlockingPeriod.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SortOrder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SortOrder.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationInitializer.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationInitializer.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationMethod.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationMethod.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationParameters.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationProvider.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationToken.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationToken.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopy.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopy.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMapping.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMapping.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMappingCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMappingCollection.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyOptions.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyOptions.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientFactory.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientFactory.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientLogger.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientLogger.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientMetaDataCollectionNames.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientMetaDataCollectionNames.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermission.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermission.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermissionAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermissionAttribute.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCertificateStoreProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCertificateStoreProvider.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCngProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCngProvider.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCspProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCspProvider.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionEnclaveProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionEnclaveProvider.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionKeyStoreProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionKeyStoreProvider.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommand.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommand.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandBuilder.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandColumnEncryptionSetting.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnection.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataReader.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataReader.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SQLDebugging.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SQLDebugging.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlDependency.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDependency.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveAttestationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveAttestationParameters.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveSession.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveSession.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlError.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlError.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlErrorCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlErrorCollection.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlException.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlException.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventArgs.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventHandler.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationEventArgs.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationInfo.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationInfo.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationSource.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationSource.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationType.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationType.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameter.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameterCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameterCollection.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventArgs.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventHandler.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventArgs.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventHandler.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.DataClassification", "Microsoft.Data.SqlClient.DataClassification", "{5D1F0032-7B0D-4FB6-A969-FCFB25C9EA1D}" + ProjectSection(SolutionItems) = preProject + ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\ColumnSensitivity.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\ColumnSensitivity.xml + ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\InformationType.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\InformationType.xml + ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\Label.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\Label.xml + ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityClassification.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityClassification.xml + ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityProperty.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityProperty.xml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.Server", "Microsoft.Data.SqlClient.Server", "{650EB7FA-EB0D-4F8E-AB2C-161C3AD8E363}" + ProjectSection(SolutionItems) = preProject + ..\doc\snippets\Microsoft.Data.SqlClient.Server\DataAccessKind.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\DataAccessKind.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\Format.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\Format.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\IBinarySerialize.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\IBinarySerialize.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\InvalidUdtException.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\InvalidUdtException.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlDataRecord.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlDataRecord.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlFacetAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlFacetAttribute.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlFunctionAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlFunctionAttribute.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlMetaData.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlMetaData.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlMethodAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlMethodAttribute.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlUserDefinedAggregateAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlUserDefinedAggregateAttribute.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlUserDefinedTypeAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlUserDefinedTypeAttribute.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\SystemDataAccessKind.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SystemDataAccessKind.xml + ..\doc\snippets\Microsoft.Data.SqlClient.Server\TriggerAction.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\TriggerAction.xml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlTypes", "Microsoft.Data.SqlTypes", "{5A7600BD-AED8-44AB-8F2A-7CB33A8D9C02}" + ProjectSection(SolutionItems) = preProject + ..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.DockerLinuxTest", "Microsoft.Data.SqlClient\tests\DockerLinuxTest\Microsoft.Data.SqlClient.DockerLinuxTest.csproj", "{833157E1-1E53-4908-B4CB-5C5507A44582}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -831,6 +939,102 @@ Global {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x64.Build.0 = Release|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x86.ActiveCfg = Release|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x86.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x64.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x64.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x86.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x86.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x64.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x64.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x86.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x86.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|Any CPU.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x64.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x64.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x86.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x86.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x86.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|Any CPU.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x64.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x64.Build.0 = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x86.ActiveCfg = Release|Any CPU + {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x86.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|Any CPU.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x64.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x64.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x86.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x86.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x64.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x64.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x86.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x86.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|Any CPU.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x64.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x64.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x86.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x86.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x86.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|Any CPU.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|Any CPU.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x64.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x64.Build.0 = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x86.ActiveCfg = Release|Any CPU + {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -854,6 +1058,14 @@ Global {412BCCC8-19F6-489A-B594-E9A506816155} = {771F3F1E-7A68-4A9D-ADA8-A24F1D5BE71D} {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7} = {C9726AED-D6A3-4AAC-BA04-92DD1F079594} {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {0CC4817A-12F3-4357-912C-09315FAAD008} + {71F356DC-DFA3-4163-8BFE-D268722CE189} = {ED952CF7-84DF-437A-B066-F516E9BE1C2C} + {908C7DD3-C999-40A6-9433-9F5ACA7C36F5} = {71F356DC-DFA3-4163-8BFE-D268722CE189} + {0CE216CE-8072-4985-B248-61F0D0BE9C2E} = {71F356DC-DFA3-4163-8BFE-D268722CE189} + {C05F4FFE-6A14-4409-AA0A-10630BE4F1EE} = {71F356DC-DFA3-4163-8BFE-D268722CE189} + {5D1F0032-7B0D-4FB6-A969-FCFB25C9EA1D} = {71F356DC-DFA3-4163-8BFE-D268722CE189} + {650EB7FA-EB0D-4F8E-AB2C-161C3AD8E363} = {71F356DC-DFA3-4163-8BFE-D268722CE189} + {5A7600BD-AED8-44AB-8F2A-7CB33A8D9C02} = {71F356DC-DFA3-4163-8BFE-D268722CE189} + {833157E1-1E53-4908-B4CB-5C5507A44582} = {0CC4817A-12F3-4357-912C-09315FAAD008} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Dockerfile b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Dockerfile new file mode 100644 index 0000000000..a6b03a1911 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Dockerfile @@ -0,0 +1,23 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build +WORKDIR /sqlclient +COPY . . + +ARG PROJNAME="Microsoft.Data.SqlClient.DockerLinuxTest" +ARG PROJFILE=$PROJNAME".csproj" +ARG DLLFILE=$PROJNAME".dll" + +WORKDIR /sqlclient/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest +RUN dotnet build $PROJFILE -c Release -o /app/build -p:OSGroup=Unix -p:GenerateDocumentationFile=false + +FROM build AS publish +RUN dotnet publish $PROJFILE -c Release -o /app/publish -p:OSGroup=Unix -p:GenerateDocumentationFile=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", $DLLFILE] diff --git a/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Microsoft.Data.SqlClient.DockerLinuxTest.csproj b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Microsoft.Data.SqlClient.DockerLinuxTest.csproj new file mode 100644 index 0000000000..7d3ecb246c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Microsoft.Data.SqlClient.DockerLinuxTest.csproj @@ -0,0 +1,15 @@ + + + Exe + netcoreapp3.1 + Linux + ..\..\..\.. + Unix + + + + + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Program.cs b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Program.cs new file mode 100644 index 0000000000..8ff4fca4e7 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Program.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Data.SqlClient; + +namespace Microsoft.Data.SqlClient.DockerLinuxTest +{ + class Program + { + static string server = "microsoft.sqlserver"; + static string user = "sa"; + // Provide password as set in docker-compose.yml + static string pwd = "P@ssw0rd!123"; + + static void Main(string[] args) + { + using (SqlConnection sqlConnection = new SqlConnection($"Server={server}; UID={user}; PWD={pwd}")) + { + sqlConnection.Open(); + Console.WriteLine($"Connected to SQL Server v{sqlConnection.ServerVersion} from {Environment.OSVersion.VersionString}"); + // Write your code here to debug inside Docker Linux containers. + } + } + } +} + diff --git a/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Properties/launchSettings.json b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Properties/launchSettings.json new file mode 100644 index 0000000000..008eabbd78 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Microsoft.Data.SqlClient.DockerLinuxTest": { + "commandName": "Project" + }, + "Docker": { + "commandName": "Docker" + } + } +} diff --git a/src/docker-compose.dcproj b/src/docker-compose.dcproj new file mode 100644 index 0000000000..8518dbac22 --- /dev/null +++ b/src/docker-compose.dcproj @@ -0,0 +1,18 @@ + + + + 2.1 + Linux + f5df2fdc-c860-4cb3-8b24-7c903c6fc076 + + + microsoft.data.sqlclient.dockertests + + + + + docker-compose.yml + + + + \ No newline at end of file diff --git a/src/docker-compose.override.yml b/src/docker-compose.override.yml new file mode 100644 index 0000000000..2a0859dacc --- /dev/null +++ b/src/docker-compose.override.yml @@ -0,0 +1,3 @@ +version: '3.4' +services: + microsoft.data.sqlclient.dockertests: diff --git a/src/docker-compose.yml b/src/docker-compose.yml new file mode 100644 index 0000000000..a55ac1ad95 --- /dev/null +++ b/src/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' + +services: + microsoft.data.sqlclient.dockertests: + image: ${DOCKER_REGISTRY-}microsoftdatasqlclientdockerlinuxtest + build: + context: ../ + dockerfile: src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Dockerfile + depends_on: + - microsoft.sqlserver + + microsoft.sqlserver: + image: mcr.microsoft.com/mssql/server:2019-latest + environment: + - SA_PASSWORD=P@ssw0rd!123 + - ACCEPT_EULA=Y + ports: + - "5434:1433" diff --git a/tools/credScan/CredScanSuppressions.json b/tools/credScan/CredScanSuppressions.json index 1a2f5437e1..7b38d855f2 100644 --- a/tools/credScan/CredScanSuppressions.json +++ b/tools/credScan/CredScanSuppressions.json @@ -1,25 +1,33 @@ { - "tool": "Credential Scanner", - "suppressions": [ - { - "file": "TdsServerCertificate.pfx", - "_justification": "The dummy certificate used for internal testing" - }, - { - "file": "config.ps1", - "_justification": "Contains dummy passwords used for internal testing" - }, - { - "file": "SqlConnectionBasicTests.cs", - "_justification": "Contains dummy passwords used for internal testing" - }, - { - "file": "ExceptionTest.cs", - "_justification": "Contains dummy passwords used for internal testing" - }, - { - "file": "TDSServerArguments.cs", - "_justification": "Contains dummy passwords used for internal testing" - } - ] -} \ No newline at end of file + "tool": "Credential Scanner", + "suppressions": [ + { + "file": "TdsServerCertificate.pfx", + "_justification": "The dummy certificate used for internal testing" + }, + { + "file": "config.ps1", + "_justification": "Contains dummy passwords used for internal testing" + }, + { + "file": "SqlConnectionBasicTests.cs", + "_justification": "Contains dummy passwords used for internal testing" + }, + { + "file": "ExceptionTest.cs", + "_justification": "Contains dummy passwords used for internal testing" + }, + { + "file": "TDSServerArguments.cs", + "_justification": "Contains dummy passwords used for internal testing" + }, + { + "file": "Program.cs", + "_justification": "Contains dummy passwords used for docker testing" + }, + { + "file": "docker-compose.yml", + "_justification": "Contains dummy passwords used for docker testing" + } + ] +} From 7d60b0df252f1a26ac7b47cf334b9a58ce07a404 Mon Sep 17 00:00:00 2001 From: David Engel Date: Tue, 21 Apr 2020 12:55:19 -0700 Subject: [PATCH 05/13] Add SqlConnectionOverrides for a fast Open() option. (#463) --- .../SqlConnection.xml | 40 +++++++++++++++++++ .../SqlConnectionOverrides.xml | 17 ++++++++ .../netcore/ref/Microsoft.Data.SqlClient.cs | 10 +++++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 15 +++++-- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 10 +++++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 11 +++++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 26 +++++++++--- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 10 +++++ .../SQL/ConnectivityTests/ConnectivityTest.cs | 33 +++++++++++++++ 9 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 doc/snippets/Microsoft.Data.SqlClient/SqlConnectionOverrides.xml diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 6eddd8df55..d878ed20cd 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -928,6 +928,46 @@ GO The tag in the app.config file has invalid or unknown elements. There are two entries with the same name in the section. + + Options to override default connection open behavior. + + Opens a database connection with the property settings specified by the . + + + + draws an open connection from the connection pool if one is available. Otherwise, it establishes a new connection to an instance of SQL Server. If overrides are specified, the first open attempt will apply the specified overrides to the open action. + +> [!NOTE] +> If the goes out of scope, it is not closed. Therefore, you must explicitly close the connection by calling . + +> [!NOTE] +> If you specify a port number other than 1433 when you are trying to connect to an instance of SQL Server and using a protocol other than TCP/IP, the method fails. To specify a port number other than 1433, include "server=machinename,port number" in the connection string, and use the TCP/IP protocol. + +> [!NOTE] +> The .NET Framework Data Provider for SQL Server requires the Security permission with "Allows calls to unmanaged assemblies" enabled ( with set to `UnmanagedCode`) to open a with SQL Debugging enabled. + +## Examples + The following example creates a , opens it, and displays some of its properties. The connection is automatically closed at the end of the `using` block. + + [!code-csharp[SqlConnection_Open Example#1](~/../sqlclient/doc/samples/SqlConnection_Open.cs#1)] + +]]> + + + Cannot open a connection without specifying a data source or server. + + or + + The connection is already open. + + A connection-level error occurred while opening the connection. If the property contains the value 18487 or 18488, this indicates that the specified password has expired or must be reset. See the method for more information. + + The tag in the app.config file has invalid or unknown elements. + There are two entries with the same name in the section. + The cancellation instruction. An asynchronous version of , which opens a database connection with the property settings specified by the . The cancellation token can be used to request that the operation be abandoned before the connection timeout elapses. Exceptions will be propagated via the returned Task. If the connection timeout time elapses without successfully connecting, the returned Task will be marked as faulted with an Exception. The implementation returns a Task without blocking the calling thread for both pooled and non-pooled connections. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionOverrides.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionOverrides.xml new file mode 100644 index 0000000000..cf6c2afb93 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionOverrides.xml @@ -0,0 +1,17 @@ + + + + + Specifies a value for Overrides. + + + + No overrides. + 0 + + + Disable transient fault handling during the initial SqlConnection Open attempt. + 1 + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 60fb125690..e037728eee 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -591,6 +591,8 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys public override System.Data.DataTable GetSchema(string collectionName, string[] restrictionValues) { throw null; } /// public override void Open() { } + /// + public void Open(SqlConnectionOverrides overrides) { } /// public override System.Threading.Tasks.Task OpenAsync(System.Threading.CancellationToken cancellationToken) { throw null; } /// @@ -598,6 +600,14 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys /// public System.Collections.IDictionary RetrieveStatistics() { throw null; } } + /// + public enum SqlConnectionOverrides + { + /// + None = 0, + /// + OpenWithoutRetry = 1, + } /// [System.ComponentModel.DefaultPropertyAttribute("DataSource")] public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 4f847bc1a7..201763a227 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -998,6 +998,12 @@ private void DisposeMe(bool disposing) /// public override void Open() + { + Open(SqlConnectionOverrides.None); + } + + /// + public void Open(SqlConnectionOverrides overrides) { long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); @@ -1014,7 +1020,7 @@ public override void Open() { statistics = SqlStatistics.StartTimer(Statistics); - if (!TryOpen(null)) + if (!TryOpen(null, overrides)) { throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending); } @@ -1469,7 +1475,7 @@ private void PrepareStatisticsForNewConnection() } } - private bool TryOpen(TaskCompletionSource retry) + private bool TryOpen(TaskCompletionSource retry, SqlConnectionOverrides overrides = SqlConnectionOverrides.None) { SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions; @@ -1489,7 +1495,7 @@ private bool TryOpen(TaskCompletionSource retry) } } - _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword) && @@ -1515,6 +1521,9 @@ private bool TryOpen(TaskCompletionSource retry) } // does not require GC.KeepAlive(this) because of ReRegisterForFinalize below. + // Set future transient fault handling based on connection options + _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + var tdsInnerConnection = (SqlInternalConnectionTds)InnerConnection; Debug.Assert(tdsInnerConnection.Parser != null, "Where's the parser?"); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 262b8bc2ca..3d735d21e7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -1078,6 +1078,16 @@ public enum SqlConnectionColumnEncryptionSetting Enabled, } + /// + [Flags] + public enum SqlConnectionOverrides + { + /// + None = 0, + /// + OpenWithoutRetry = 1, + } + /// public enum SqlCommandColumnEncryptionSetting { diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 44cc4bb3e1..c6154c3ad4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -777,6 +777,8 @@ public sealed partial class SqlConnection : System.Data.Common.DbConnection, Sys public override System.Data.DataTable GetSchema(string collectionName, string[] restrictionValues) { throw null; } /// public override void Open() { } + /// + public void Open(SqlConnectionOverrides overrides) { } /// public override System.Threading.Tasks.Task OpenAsync(System.Threading.CancellationToken cancellationToken) { throw null; } /// @@ -813,6 +815,15 @@ public enum SqlConnectionAttestationProtocol HGS = 3 } + /// + public enum SqlConnectionOverrides + { + /// + None = 0, + /// + OpenWithoutRetry = 1, + } + /// [System.ComponentModel.DefaultPropertyAttribute("DataSource")] public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index ed014abc6a..9ecacf253c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1396,6 +1396,12 @@ public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction /// override public void Open() + { + Open(SqlConnectionOverrides.None); + } + + /// + public void Open(SqlConnectionOverrides overrides) { long scopeID = SqlClientEventSource.Log.ScopeEnterEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); @@ -1420,7 +1426,7 @@ override public void Open() { statistics = SqlStatistics.StartTimer(Statistics); - if (!TryOpen(null)) + if (!TryOpen(null, overrides)) { throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending); } @@ -1814,10 +1820,13 @@ internal void Retry(Task retryTask) } } - private bool TryOpen(TaskCompletionSource retry) + private bool TryOpen(TaskCompletionSource retry, SqlConnectionOverrides overrides = SqlConnectionOverrides.None) { SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions; - _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + + bool result = false; + + _applyTransientFaultHandling = (!overrides.HasFlag(SqlConnectionOverrides.OpenWithoutRetry) && retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); if (connectionOptions != null && (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword) && @@ -1833,13 +1842,13 @@ private bool TryOpen(TaskCompletionSource retry) { if (_impersonateIdentity.User == identity.User) { - return TryOpenInner(retry); + result = TryOpenInner(retry); } else { using (WindowsImpersonationContext context = _impersonateIdentity.Impersonate()) { - return TryOpenInner(retry); + result = TryOpenInner(retry); } } } @@ -1854,8 +1863,13 @@ private bool TryOpen(TaskCompletionSource retry) { _lastIdentity = null; } - return TryOpenInner(retry); + result = TryOpenInner(retry); } + + // Set future transient fault handling based on connection options + _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0); + + return result; } private bool TryOpenInner(TaskCompletionSource retry) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index afcb377d03..204397478d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -1023,6 +1023,16 @@ public enum SqlConnectionColumnEncryptionSetting Enabled, } + /// + [Flags] + public enum SqlConnectionOverrides + { + /// + None = 0, + /// + OpenWithoutRetry = 1, + } + /// public enum SqlCommandColumnEncryptionSetting { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index 7616a0cb4d..6c049b422d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -304,5 +304,38 @@ public static void ConnectionStringPersistentInfoTest() Assert.True(connectionStringBuilder.Password != string.Empty, "Password must persist according to set the PersistSecurityInfo by true!"); } } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void ConnectionOpenDisableRetry() + { + using (SqlConnection sqlConnection = new SqlConnection((new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { InitialCatalog = "DoesNotExist0982532435423", Pooling = false }).ConnectionString)) + { + TimeSpan duration; + DateTime start = DateTime.Now; + try + { + sqlConnection.Open(SqlConnectionOverrides.OpenWithoutRetry); + Assert.True(false, "Connection succeeded to database that should not exist."); + } + catch (SqlException) + { + duration = DateTime.Now - start; + Assert.True(duration.TotalSeconds < 2, $"Connection Open() without retries took longer than expected. Expected < 2 sec. Took {duration.TotalSeconds} sec."); + } + + start = DateTime.Now; + try + { + sqlConnection.Open(); + Assert.True(false, "Connection succeeded to database that should not exist."); + } + catch (SqlException) + { + duration = DateTime.Now - start; + //Should not fail fast due to transient fault handling when DB does not exist + Assert.True(duration.TotalSeconds > 5, $"Connection Open() with retries took less time than expected. Expect > 5 sec with transient fault handling. Took {duration.TotalSeconds} sec."); + } + } + } } } From dfad9612bb270335f5163c465ada8f8aa9d1b618 Mon Sep 17 00:00:00 2001 From: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> Date: Wed, 22 Apr 2020 10:48:28 -0700 Subject: [PATCH 06/13] Fix tests failure on expected exceptions (#530) --- .../tests/ManualTests/DataCommon/DataTestUtility.cs | 6 +++--- .../ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 2cc84ba8dc..eb4009c363 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -480,7 +480,7 @@ public static void AssertEqualsWithDescription(object expectedValue, object actu try { actionThatFails(); - Console.WriteLine("ERROR: Did not get expected exception"); + Assert.False(true, "ERROR: Did not get expected exception"); return null; } catch (Exception ex) @@ -501,7 +501,7 @@ public static void AssertEqualsWithDescription(object expectedValue, object actu try { actionThatFails(); - Console.WriteLine("ERROR: Did not get expected exception"); + Assert.False(true, "ERROR: Did not get expected exception"); return null; } catch (Exception ex) @@ -522,7 +522,7 @@ public static void AssertEqualsWithDescription(object expectedValue, object actu try { actionThatFails(); - Console.WriteLine("ERROR: Did not get expected exception"); + Assert.False(true, "ERROR: Did not get expected exception"); return null; } catch (Exception ex) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index 4740daaaf2..a8cbbdf6f4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -150,6 +150,7 @@ public static void TimeoutCancel() TimeoutCancel(tcp_connStr); } + [ActiveIssue(12167)] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [PlatformSpecific(TestPlatforms.Windows)] public static void TimeoutCancelNP() From 9e13645313d69e783998de47ada0ec186b0d3f9e Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Wed, 22 Apr 2020 23:57:10 +0100 Subject: [PATCH 07/13] Combine shared elements (#519) --- .../src/Microsoft.Data.SqlClient.csproj | 16 +- .../Data/SqlClient/SqlClientLogger.cs | 39 ---- .../netfx/src/Microsoft.Data.SqlClient.csproj | 16 +- .../Data/Common/ActivityCorrelator.cs | 68 ------ .../Data/Sql/SqlNotificationRequest.cs | 82 ------- ...rectoryAuthenticationTimeoutRetryHelper.cs | 139 ----------- .../Data/SqlClient/SqlClientLogger.cs | 39 ---- .../Data/SqlTypes/SqlTypeWorkarounds.cs | 216 ------------------ .../Data/Common/ActivityCorrelator.cs | 0 .../Data/Sql/SqlNotificationRequest.cs | 12 +- ...rectoryAuthenticationTimeoutRetryHelper.cs | 0 .../Data/SqlTypes/SqlTypeWorkarounds.cs | 0 12 files changed, 30 insertions(+), 597 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ActivityCorrelator.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Sql/SqlNotificationRequest.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientLogger.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/Common/ActivityCorrelator.cs (100%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/Sql/SqlNotificationRequest.cs (64%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs (100%) rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs (100%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 102d6325b1..9d0d3c5fc1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -36,6 +36,12 @@ Microsoft\Data\SqlClient\SqlClientLogger.cs + + Microsoft\Data\Sql\SqlNotificationRequest.cs + + + Microsoft\Data\Common\ActivityCorrelator.cs + Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs @@ -51,6 +57,9 @@ Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs + Microsoft\Data\SqlClient\Server\IBinarySerialize.cs @@ -126,6 +135,9 @@ Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs + + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs + @@ -183,7 +195,6 @@ - Microsoft\Data\Common\AdapterUtil.cs @@ -238,7 +249,6 @@ - @@ -247,7 +257,6 @@ - @@ -315,7 +324,6 @@ - Microsoft\Data\SQLTypes\SQLResource.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs deleted file mode 100644 index c150399783..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientLogger.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.SqlClient -{ - /// - public class SqlClientLogger - { - internal enum LogLevel - { - Info = 0, - Error, - } - - /// - public void LogInfo(string type, string method, string message) - { - SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Info, message); - } - - /// - public void LogError(string type, string method, string message) - { - SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Info, message); - } - - /// - public bool LogAssert(bool value, string type, string method, string message) - { - if (!value) - LogError(type, method, message); - return value; - } - - /// - public bool IsLoggingEnabled => SqlClientEventSource.Log.IsEnabled(); - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 8000b94997..8a6ad37876 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -93,6 +93,12 @@ Microsoft\Data\SqlClient\SqlClientLogger.cs + + >Microsoft\Data\Common\ActivityCorrelator.cs + + + Microsoft\Data\Sql\SqlNotificationRequest.cs + Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs @@ -108,6 +114,9 @@ Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs + Microsoft\Data\SqlClient\Server\IBinarySerialize.cs @@ -183,6 +192,9 @@ Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs + + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs + @@ -193,7 +205,6 @@ - @@ -296,7 +307,6 @@ - @@ -311,7 +321,6 @@ - @@ -368,7 +377,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ActivityCorrelator.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ActivityCorrelator.cs deleted file mode 100644 index 1fc60e0676..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ActivityCorrelator.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.Common -{ - using System; - using System.Globalization; - - /// - /// This class defines the data structure for ActivityId used for correlated tracing between client (bid trace event) and server (XEvent). - /// It also includes all the APIs used to access the ActivityId. Note: ActivityId is thread based which is stored in TLS. - /// - - internal static class ActivityCorrelator - { - - internal sealed class ActivityId - { - internal readonly Guid Id; - internal readonly uint Sequence; - - internal ActivityId(uint sequence) - { - this.Id = Guid.NewGuid(); - this.Sequence = sequence; - } - - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "{0}:{1}", this.Id, this.Sequence); - } - } - - // Declare the ActivityId which will be stored in TLS. The Id is unique for each thread. - // The Sequence number will be incremented when each event happens. - // Correlation along threads is consistent with the current XEvent mechanism at server. - [ThreadStaticAttribute] - static ActivityId t_tlsActivity; - - /// - /// Get the current ActivityId - /// - internal static ActivityId Current - { - get - { - if (t_tlsActivity == null) - { - t_tlsActivity = new ActivityId(1); - } - - return t_tlsActivity; - } - } - - /// - /// Increment the sequence number and generate the new ActivityId - /// - /// ActivityId - internal static ActivityId Next() - { - t_tlsActivity = new ActivityId(t_tlsActivity?.Sequence ?? 0); - - return t_tlsActivity; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Sql/SqlNotificationRequest.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Sql/SqlNotificationRequest.cs deleted file mode 100644 index 3048aa9c5e..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Sql/SqlNotificationRequest.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.Sql -{ - - using System; - using Microsoft.Data.Common; - - /// - //[System.ComponentModel.TypeConverterAttribute(typeof(Microsoft.Data.Sql.SqlNotificationRequest.SqlNotificationRequestConverter))] - public sealed class SqlNotificationRequest - { - private string _userData; - private string _options; - private int _timeout; - - /// - public SqlNotificationRequest() - : this(null, null, SqlClient.SQL.SqlDependencyTimeoutDefault) { } - - /// - public SqlNotificationRequest(string userData, string options, int timeout) - { - UserData = userData; - Timeout = timeout; - Options = options; - } - - /// - public string Options - { - get - { - return _options; - } - set - { - if ((null != value) && (UInt16.MaxValue < value.Length)) - { - throw ADP.ArgumentOutOfRange(String.Empty, ADP.ParameterService); - } - _options = value; - } - } - - /// - public int Timeout - { - get - { - return _timeout; - } - set - { - if (0 > value) - { - throw ADP.ArgumentOutOfRange(String.Empty, ADP.ParameterTimeout); - } - _timeout = value; - } - } - - /// - public string UserData - { - get - { - return _userData; - } - set - { - if ((null != value) && (UInt16.MaxValue < value.Length)) - { - throw ADP.ArgumentOutOfRange(String.Empty, ADP.ParameterUserData); - } - _userData = value; - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs deleted file mode 100644 index 473b3b638a..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Security.Cryptography; -using System.Text; - -namespace Microsoft.Data.SqlClient -{ - /// - /// AD auth retry states. - /// - internal enum ActiveDirectoryAuthenticationTimeoutRetryState - { - NotStarted = 0, - Retrying, - HasLoggedIn, - } - - /// - /// AD auth retry helper. - /// - internal class ActiveDirectoryAuthenticationTimeoutRetryHelper - { - private ActiveDirectoryAuthenticationTimeoutRetryState _state = ActiveDirectoryAuthenticationTimeoutRetryState.NotStarted; - private SqlFedAuthToken _token; - private readonly string _typeName; - private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger(); - - /// - /// Constructor. - /// - public ActiveDirectoryAuthenticationTimeoutRetryHelper() - { - _typeName = GetType().Name; - } - - /// - /// Retry state. - /// - public ActiveDirectoryAuthenticationTimeoutRetryState State - { - get { return _state; } - set - { - switch (_state) - { - case ActiveDirectoryAuthenticationTimeoutRetryState.NotStarted: - if (value != ActiveDirectoryAuthenticationTimeoutRetryState.Retrying - && value != ActiveDirectoryAuthenticationTimeoutRetryState.HasLoggedIn) - { - throw new InvalidOperationException($"Cannot transit from {_state} to {value}."); - } - break; - case ActiveDirectoryAuthenticationTimeoutRetryState.Retrying: - if (value != ActiveDirectoryAuthenticationTimeoutRetryState.HasLoggedIn) - { - throw new InvalidOperationException($"Cannot transit from {_state} to {value}."); - } - break; - case ActiveDirectoryAuthenticationTimeoutRetryState.HasLoggedIn: - throw new InvalidOperationException($"Cannot transit from {_state} to {value}."); - default: - throw new InvalidOperationException($"Unsupported state: {value}."); - } - if (_sqlAuthLogger.IsLoggingEnabled) - { - _sqlAuthLogger.LogInfo(_typeName, "SetState", $"State changed from {_state} to {value}."); - } - _state = value; - } - } - - /// - /// Cached token. - /// - public SqlFedAuthToken CachedToken - { - get - { - if (_sqlAuthLogger.IsLoggingEnabled) - { - _sqlAuthLogger.LogInfo(_typeName, "GetCachedToken", $"Retrieved cached token {GetTokenHash(_token)}."); - } - return _token; - } - set - { - if (_sqlAuthLogger.IsLoggingEnabled) - { - _sqlAuthLogger.LogInfo(_typeName, "SetCachedToken", $"CachedToken changed from {GetTokenHash(_token)} to {GetTokenHash(value)}."); - } - _token = value; - } - } - - /// - /// Whether login can be retried after a client/server connection timeout due to a long-time token acquisition. - /// - public bool CanRetryWithSqlException(SqlException sqlex) - { - var methodName = "CheckCanRetry"; - if (_sqlAuthLogger.LogAssert(_state == ActiveDirectoryAuthenticationTimeoutRetryState.NotStarted, _typeName, methodName, $"Cannot retry due to state == {_state}.") - && _sqlAuthLogger.LogAssert(CachedToken != null, _typeName, methodName, $"Cannot retry when cached token is null.") - && _sqlAuthLogger.LogAssert(IsConnectTimeoutError(sqlex), _typeName, methodName, $"Cannot retry when exception is not timeout.")) - { - _sqlAuthLogger.LogInfo(_typeName, methodName, "All checks passed."); - return true; - } - return false; - } - - private static bool IsConnectTimeoutError(SqlException sqlex) - { - var innerException = sqlex.InnerException as Win32Exception; - if (innerException == null) - return false; - return innerException.NativeErrorCode == 10054 // Server timeout - || innerException.NativeErrorCode == 258; // Client timeout - } - - private static string GetTokenHash(SqlFedAuthToken token) - { - if (token == null) - return "null"; - - // Here we mimic how ADAL calculates hash for token. They use UTF8 instead of Unicode. - var originalTokenString = SqlAuthenticationToken.AccessTokenStringFromBytes(token.accessToken); - var bytesInUtf8 = Encoding.UTF8.GetBytes(originalTokenString); - using (var sha256 = SHA256.Create()) - { - var hash = sha256.ComputeHash(bytesInUtf8); - return Convert.ToBase64String(hash); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientLogger.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientLogger.cs deleted file mode 100644 index 0cdd9c8cff..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientLogger.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.SqlClient -{ - /// - public class SqlClientLogger - { - internal enum LogLevel - { - Info = 0, - Error, - } - - /// - public void LogInfo(string type, string method, string message) - { - SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Info, message); - } - - /// - public void LogError(string type, string method, string message) - { - SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Info, message); - } - - /// - public bool LogAssert(bool value, string type, string method, string message) - { - if (!value) - LogError(type, method, message); - return value; - } - - /// - public bool IsLoggingEnabled => SqlClientEventSource.Log.IsEnabled(); - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs deleted file mode 100644 index 18512af9df..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs +++ /dev/null @@ -1,216 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Xml; - -namespace Microsoft.Data.SqlTypes -{ - /// - /// This type provides workarounds for the separation between Microsoft.Data.Common - /// and Microsoft.Data.SqlClient. The latter wants to access internal members of the former, and - /// this class provides ways to do that. We must review and update this implementation any time the - /// implementation of the corresponding types in Microsoft.Data.Common change. - /// - internal static class SqlTypeWorkarounds - { - #region Work around inability to access SqlXml.CreateSqlXmlReader - private static readonly XmlReaderSettings s_defaultXmlReaderSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment }; - private static readonly XmlReaderSettings s_defaultXmlReaderSettingsCloseInput = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; - private static readonly XmlReaderSettings s_defaultXmlReaderSettingsAsyncCloseInput = new XmlReaderSettings() { Async = true, ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; - - internal const SqlCompareOptions SqlStringValidSqlCompareOptionMask = - SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth | - SqlCompareOptions.IgnoreNonSpace | SqlCompareOptions.IgnoreKanaType | - SqlCompareOptions.BinarySort | SqlCompareOptions.BinarySort2; - - internal static XmlReader SqlXmlCreateSqlXmlReader(Stream stream, bool closeInput = false, bool async = false) - { - Debug.Assert(closeInput || !async, "Currently we do not have pre-created settings for !closeInput+async"); - - XmlReaderSettings settingsToUse = closeInput ? - (async ? s_defaultXmlReaderSettingsAsyncCloseInput : s_defaultXmlReaderSettingsCloseInput) : - s_defaultXmlReaderSettings; - - return XmlReader.Create(stream, settingsToUse); - } - - internal static XmlReader SqlXmlCreateSqlXmlReader(TextReader textReader, bool closeInput = false, bool async = false) - { - Debug.Assert(closeInput || !async, "Currently we do not have pre-created settings for !closeInput+async"); - - XmlReaderSettings settingsToUse = closeInput ? - (async ? s_defaultXmlReaderSettingsAsyncCloseInput : s_defaultXmlReaderSettingsCloseInput) : - s_defaultXmlReaderSettings; - - return XmlReader.Create(textReader, settingsToUse); - } - #endregion - - #region Work around inability to access SqlDateTime.ToDateTime - internal static DateTime SqlDateTimeToDateTime(int daypart, int timepart) - { - // Values need to match those from SqlDateTime - const double SQLTicksPerMillisecond = 0.3; - const int SQLTicksPerSecond = 300; - const int SQLTicksPerMinute = SQLTicksPerSecond * 60; - const int SQLTicksPerHour = SQLTicksPerMinute * 60; - const int SQLTicksPerDay = SQLTicksPerHour * 24; - const int MinDay = -53690; // Jan 1 1753 - const int MaxDay = 2958463; // Dec 31 9999 is this many days from Jan 1 1900 - const int MinTime = 0; // 00:00:0:000PM - const int MaxTime = SQLTicksPerDay - 1; // = 25919999, 11:59:59:997PM - - if (daypart < MinDay || daypart > MaxDay || timepart < MinTime || timepart > MaxTime) - { - throw new OverflowException(SQLResource.DateTimeOverflowMessage); - } - - long baseDateTicks = new DateTime(1900, 1, 1).Ticks; - long dayticks = daypart * TimeSpan.TicksPerDay; - long timeticks = ((long)(timepart / SQLTicksPerMillisecond + 0.5)) * TimeSpan.TicksPerMillisecond; - - return new DateTime(baseDateTicks + dayticks + timeticks); - } - #endregion - - #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation - /// - /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists - /// only to distinguish this constructor from the constructor that takes a long. - /// Used only internally. - /// - internal static SqlMoney SqlMoneyCtor(long value, int ignored) - { - var c = default(SqlMoneyCaster); - - // Same behavior as the internal SqlMoney.ctor(long, bool) overload - c.Fake._fNotNull = true; - c.Fake._value = value; - - return c.Real; - } - - internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money) - { - var c = default(SqlMoneyCaster); - c.Real = money; - - // Same implementation as the internal SqlMoney.ToSqlInternalRepresentation implementation - if (money.IsNull) - { - throw new SqlNullValueException(); - } - return c.Fake._value; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlMoneyLookalike // exact same shape as SqlMoney, but with accessible fields - { - internal bool _fNotNull; - internal long _value; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlMoneyCaster - { - [FieldOffset(0)] - internal SqlMoney Real; - [FieldOffset(0)] - internal SqlMoneyLookalike Fake; - } - #endregion - - #region Work around inability to access SqlDecimal._data1/2/3/4 - internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4) - { - // Extract the four data elements from SqlDecimal. - var c = default(SqlDecimalCaster); - c.Real = d; - data1 = c.Fake._data1; - data2 = c.Fake._data2; - data3 = c.Fake._data3; - data4 = c.Fake._data4; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SqlDecimalLookalike // exact same shape as SqlDecimal, but with accessible fields - { - internal byte _bStatus; - internal byte _bLen; - internal byte _bPrec; - internal byte _bScale; - internal uint _data1; - internal uint _data2; - internal uint _data3; - internal uint _data4; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlDecimalCaster - { - [FieldOffset(0)] - internal SqlDecimal Real; - [FieldOffset(0)] - internal SqlDecimalLookalike Fake; - } - #endregion - - #region Work around inability to access SqlBinary.ctor(byte[], bool) - internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) - { - // Construct a SqlBinary without allocating/copying the byte[]. This provides - // the same behavior as SqlBinary.ctor(byte[], bool). - var c = default(SqlBinaryCaster); - c.Fake._value = value; - return c.Real; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlBinaryLookalike - { - internal byte[] _value; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlBinaryCaster - { - [FieldOffset(0)] - internal SqlBinary Real; - [FieldOffset(0)] - internal SqlBinaryLookalike Fake; - } - #endregion - - #region Work around inability to access SqlGuid.ctor(byte[], bool) - internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) - { - // Construct a SqlGuid without allocating/copying the byte[]. This provides - // the same behavior as SqlGuid.ctor(byte[], bool). - var c = default(SqlGuidCaster); - c.Fake._value = value; - return c.Real; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlGuidLookalike - { - internal byte[] _value; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlGuidCaster - { - [FieldOffset(0)] - internal SqlGuid Real; - [FieldOffset(0)] - internal SqlGuidLookalike Fake; - } - #endregion - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/ActivityCorrelator.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ActivityCorrelator.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/ActivityCorrelator.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ActivityCorrelator.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlNotificationRequest.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlNotificationRequest.cs similarity index 64% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlNotificationRequest.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlNotificationRequest.cs index cd754d6ca9..ccbff8fc0f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlNotificationRequest.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlNotificationRequest.cs @@ -7,18 +7,18 @@ namespace Microsoft.Data.Sql { - /// + /// public sealed class SqlNotificationRequest { private string _userData; private string _options; private int _timeout; - /// + /// public SqlNotificationRequest() : this(null, null, SQL.SqlDependencyTimeoutDefault) { } - /// + /// public SqlNotificationRequest(string userData, string options, int timeout) { UserData = userData; @@ -26,7 +26,7 @@ public SqlNotificationRequest(string userData, string options, int timeout) Options = options; } - /// + /// public string Options { get @@ -43,7 +43,7 @@ public string Options } } - /// + /// public int Timeout { get @@ -60,7 +60,7 @@ public int Timeout } } - /// + /// public string UserData { get diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs From 6e09b54db94feebf2a928c9973135535d0cb8e52 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Fri, 24 Apr 2020 00:48:53 +0100 Subject: [PATCH 08/13] Add SslOverTdsStream tests (#497) --- .../Microsoft.Data.SqlClient.Tests.csproj | 3 + .../FunctionalTests/SslOverTdsStreamTest.cs | 361 ++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/SslOverTdsStreamTest.cs diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index f575d34900..94cb627d09 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -59,6 +59,9 @@ + + + Address diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SslOverTdsStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SslOverTdsStreamTest.cs new file mode 100644 index 0000000000..56cc653744 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SslOverTdsStreamTest.cs @@ -0,0 +1,361 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Data.SqlClient.Tests +{ + public static class SslOverTdsStreamTest + { + public static TheoryData PacketSizes + { + get + { + const int EncapsulatedPacketCount = 4; + const int PassThroughPacketCount = 5; + + TheoryData data = new TheoryData(); + + data.Add(EncapsulatedPacketCount, PassThroughPacketCount, 0); + data.Add(EncapsulatedPacketCount, PassThroughPacketCount, 2); + data.Add(EncapsulatedPacketCount, PassThroughPacketCount, 128); + data.Add(EncapsulatedPacketCount, PassThroughPacketCount, 2048); + data.Add(EncapsulatedPacketCount, PassThroughPacketCount, 8192); + + return data; + } + } + + + [Theory] + [MemberData(nameof(PacketSizes))] + public static void SyncTest(int encapsulatedPacketCount, int passthroughPacketCount, int maxPacketReadLength) + { + byte[] input; + byte[] output; + SetupArrays(encapsulatedPacketCount + passthroughPacketCount, out input, out output); + + byte[] buffer = WritePackets(encapsulatedPacketCount, passthroughPacketCount, + (Stream stream, int index) => + { + stream.Write(input, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE * index, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE); + } + ); + + ReadPackets(buffer, encapsulatedPacketCount, passthroughPacketCount, maxPacketReadLength, output, + (Stream stream, byte[] bytes, int offset, int count) => + { + return stream.Read(bytes, offset, count); + } + ); + + Validate(input, output); + } + + [Theory] + [MemberData(nameof(PacketSizes))] + public static void AsyncTest(int encapsulatedPacketCount, int passthroughPacketCount, int maxPacketReadLength) + { + byte[] input; + byte[] output; + SetupArrays(encapsulatedPacketCount + passthroughPacketCount, out input, out output); + byte[] buffer = WritePackets(encapsulatedPacketCount, passthroughPacketCount, + async (Stream stream, int index) => + { + await stream.WriteAsync(input, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE * index, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE); + } + ); + + ReadPackets(buffer, encapsulatedPacketCount, passthroughPacketCount, maxPacketReadLength, output, + async (Stream stream, byte[] bytes, int offset, int count) => + { + return await stream.ReadAsync(bytes, offset, count); + } + ); + + Validate(input, output); + } + + [Theory] + [MemberData(nameof(PacketSizes))] + public static void SyncCoreTest(int encapsulatedPacketCount, int passthroughPacketCount, int maxPacketReadLength) + { + byte[] input; + byte[] output; + SetupArrays(encapsulatedPacketCount + passthroughPacketCount, out input, out output); + + byte[] buffer = WritePackets(encapsulatedPacketCount, passthroughPacketCount, + (Stream stream, int index) => + { + stream.Write(input.AsSpan(TdsEnums.DEFAULT_LOGIN_PACKET_SIZE * index, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE)); + } + ); + + ReadPackets(buffer, encapsulatedPacketCount, passthroughPacketCount, maxPacketReadLength, output, + (Stream stream, byte[] bytes, int offset, int count) => + { + return stream.Read(bytes.AsSpan(offset, count)); + } + ); + + Validate(input, output); + } + + [Theory] + [MemberData(nameof(PacketSizes))] + public static void AsyncCoreTest(int encapsulatedPacketCount, int passthroughPacketCount, int maxPacketReadLength) + { + byte[] input; + byte[] output; + SetupArrays(encapsulatedPacketCount + passthroughPacketCount, out input, out output); + + byte[] buffer = WritePackets(encapsulatedPacketCount, passthroughPacketCount, + async (Stream stream, int index) => + { + await stream.WriteAsync( + new ReadOnlyMemory(input, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE * index, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE) + ); + } + ); + + ReadPackets(buffer, encapsulatedPacketCount, passthroughPacketCount, maxPacketReadLength, output, + async (Stream stream, byte[] bytes, int offset, int count) => + { + return await stream.ReadAsync( + new Memory(bytes, offset, count) + ); + } + ); + + Validate(input, output); + } + + + private static void ReadPackets(byte[] buffer, int encapsulatedPacketCount, int passthroughPacketCount, int maxPacketReadLength, byte[] output, Func> action) + { + using (LimitedMemoryStream stream = new LimitedMemoryStream(buffer, maxPacketReadLength)) + using (Stream tdsStream = CreateSslOverTdsStream(stream)) + { + int offset = 0; + byte[] bytes = new byte[TdsEnums.DEFAULT_LOGIN_PACKET_SIZE]; + for (int index = 0; index < encapsulatedPacketCount; index++) + { + Array.Clear(bytes, 0, bytes.Length); + int packetBytes = ReadPacket(tdsStream, action, bytes).GetAwaiter().GetResult(); + Array.Copy(bytes, 0, output, offset, packetBytes); + offset += packetBytes; + } + InvokeFinishHandshake(tdsStream); + for (int index = 0; index < passthroughPacketCount; index++) + { + Array.Clear(bytes, 0, bytes.Length); + int packetBytes = ReadPacket(tdsStream, action, bytes).GetAwaiter().GetResult(); + Array.Copy(bytes, 0, output, offset, packetBytes); + offset += packetBytes; + } + } + } + + private static void InvokeFinishHandshake(Stream stream) + { + MethodInfo method = stream.GetType().GetMethod("FinishHandshake", BindingFlags.Public | BindingFlags.Instance); + method.Invoke(stream, null); + } + + private static Stream CreateSslOverTdsStream(Stream stream) + { + Type type = typeof(SqlClientFactory).Assembly.GetType("Microsoft.Data.SqlClient.SNI.SslOverTdsStream"); + ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(Stream) }); + Stream instance = (Stream)ctor.Invoke(new object[] { stream }); + return instance; + } + + private static void ReadPackets(byte[] buffer, int encapsulatedPacketCount, int passthroughPacketCount, int maxPacketReadLength, byte[] output, Func action) + { + using (LimitedMemoryStream stream = new LimitedMemoryStream(buffer, maxPacketReadLength)) + using (Stream tdsStream = CreateSslOverTdsStream(stream)) + { + int offset = 0; + byte[] bytes = new byte[TdsEnums.DEFAULT_LOGIN_PACKET_SIZE]; + for (int index = 0; index < encapsulatedPacketCount; index++) + { + Array.Clear(bytes, 0, bytes.Length); + int packetBytes = ReadPacket(tdsStream, action, bytes); + Array.Copy(bytes, 0, output, offset, packetBytes); + offset += packetBytes; + } + InvokeFinishHandshake(tdsStream); + for (int index = 0; index < passthroughPacketCount; index++) + { + Array.Clear(bytes, 0, bytes.Length); + int packetBytes = ReadPacket(tdsStream, action, bytes); + Array.Copy(bytes, 0, output, offset, packetBytes); + offset += packetBytes; + } + } + } + + private static int ReadPacket(Stream tdsStream, Func action, byte[] output) + { + int readCount; + int offset = 0; + byte[] bytes = new byte[TdsEnums.DEFAULT_LOGIN_PACKET_SIZE]; + do + { + readCount = action(tdsStream, bytes, offset, bytes.Length - offset); + if (readCount > 0) + { + offset += readCount; + } + } + while (readCount > 0 && offset < bytes.Length); + Array.Copy(bytes, 0, output, 0, offset); + return offset; + } + + private static async Task ReadPacket(Stream tdsStream, Func> action, byte[] output) + { + int readCount; + int offset = 0; + byte[] bytes = new byte[TdsEnums.DEFAULT_LOGIN_PACKET_SIZE]; + do + { + readCount = await action(tdsStream, bytes, offset, bytes.Length - offset); + if (readCount > 0) + { + offset += readCount; + } + } + while (readCount > 0 && offset < bytes.Length); + Array.Copy(bytes, 0, output, 0, offset); + return offset; + } + + private static byte[] WritePackets(int encapsulatedPacketCount, int passthroughPacketCount, Action action) + { + byte[] buffer = null; + using (LimitedMemoryStream stream = new LimitedMemoryStream()) + { + using (Stream tdsStream = CreateSslOverTdsStream(stream)) + { + for (int index = 0; index < encapsulatedPacketCount; index++) + { + action(tdsStream, index); + } + InvokeFinishHandshake(tdsStream);//tdsStream.FinishHandshake(); + for (int index = 0; index < passthroughPacketCount; index++) + { + action(tdsStream, encapsulatedPacketCount + index); + } + } + buffer = stream.ToArray(); + } + return buffer; + } + + private static void SetupArrays(int packetCount, out byte[] input, out byte[] output) + { + byte[] pattern = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; + input = new byte[packetCount * TdsEnums.DEFAULT_LOGIN_PACKET_SIZE]; + output = new byte[input.Length]; + for (int index = 0; index < packetCount; index++) + { + int position = 0; + while (position < TdsEnums.DEFAULT_LOGIN_PACKET_SIZE) + { + int copyCount = Math.Min(pattern.Length, TdsEnums.DEFAULT_LOGIN_PACKET_SIZE - position); + Array.Copy(pattern, 0, input, (TdsEnums.DEFAULT_LOGIN_PACKET_SIZE * index) + position, copyCount); + position += copyCount; + } + } + } + + private static void Validate(byte[] input, byte[] output) + { + Assert.True(input.AsSpan().SequenceEqual(output.AsSpan())); + } + + internal static class TdsEnums + { + public const int DEFAULT_LOGIN_PACKET_SIZE = 4096; + } + } + + [DebuggerStepThrough] + public sealed partial class LimitedMemoryStream : MemoryStream + { + private readonly int _readLimit; + private readonly int _delay; + + public LimitedMemoryStream(int readLimit = 0, int delay = 0) + { + _readLimit = readLimit; + _delay = delay; + } + + public LimitedMemoryStream(byte[] buffer, int readLimit = 0, int delay = 0) + : base(buffer) + { + _readLimit = readLimit; + _delay = delay; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_readLimit > 0) + { + return base.Read(buffer, offset, Math.Min(_readLimit, count)); + } + else + { + return base.Read(buffer, offset, count); + } + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (_delay > 0) + { + await Task.Delay(_delay, cancellationToken); + } + if (_readLimit > 0) + { + return await base.ReadAsync(buffer, offset, Math.Min(_readLimit, count), cancellationToken).ConfigureAwait(false); + } + else + { + return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + } + public override int Read(Span destination) + { + if (_readLimit > 0) + { + return base.Read(destination.Slice(0, Math.Min(_readLimit, destination.Length))); + } + else + { + return base.Read(destination); + } + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) + { + if (_readLimit > 0) + { + return base.ReadAsync(destination.Slice(0, Math.Min(_readLimit, destination.Length)), cancellationToken); + } + else + { + return base.ReadAsync(destination, cancellationToken); + } + } + } +} From a536885e4e6ccf2a98a86657609b6b72003c890c Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Wed, 29 Apr 2020 17:24:18 -0700 Subject: [PATCH 09/13] Remove Diagnostic Source + Configuration Manager from .NET Standard DLL as it's not supported (#535) --- .../src/Microsoft.Data.SqlClient.csproj | 12 +- ...uthenticationProviderManager.NetCoreApp.cs | 134 ++++++++++++++++++ ...thenticationProviderManager.NetStandard.cs | 16 +++ .../SqlAuthenticationProviderManager.cs | 118 +-------------- .../SqlClientDiagnosticListenerExtensions.cs | 30 ++-- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../SqlDiagnosticListener.NetCoreApp.cs | 15 ++ .../SqlDiagnosticListener.NetStandard.cs | 36 +++++ .../SqlClient/SqlInternalConnectionTds.cs | 8 +- .../Data/SqlClient/SqlTransaction.cs | 4 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 3 +- .../SqlClient/SqlInternalConnectionTds.cs | 9 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 3 +- tools/specs/Microsoft.Data.SqlClient.nuspec | 1 - 15 files changed, 241 insertions(+), 154 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs create mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetStandard.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 9d0d3c5fc1..d1ddbf37af 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -33,7 +33,7 @@ Microsoft\Data\SqlClient\SqlClientEventSource.cs - + Microsoft\Data\SqlClient\SqlClientLogger.cs @@ -66,7 +66,7 @@ Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs - + Microsoft\Data\SqlClient\OnChangedEventHandler.cs @@ -143,13 +143,17 @@ + + + + @@ -657,12 +661,12 @@ - + - + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs new file mode 100644 index 0000000000..c02a96e236 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Configuration; + +namespace Microsoft.Data.SqlClient +{ + internal partial class SqlAuthenticationProviderManager + { + private readonly SqlAuthenticationInitializer _initializer; + + static SqlAuthenticationProviderManager() + { + var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider(); + SqlAuthenticationProviderConfigurationSection configurationSection; + + try + { + configurationSection = (SqlAuthenticationProviderConfigurationSection)ConfigurationManager.GetSection(SqlAuthenticationProviderConfigurationSection.Name); + } + catch (ConfigurationErrorsException e) + { + throw SQL.CannotGetAuthProviderConfig(e); + } + + Instance = new SqlAuthenticationProviderManager(configurationSection); + Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider); + } + + /// + /// Constructor. + /// + public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSection configSection = null) + { + var methodName = "Ctor"; + _typeName = GetType().Name; + _providers = new ConcurrentDictionary(); + var authenticationsWithAppSpecifiedProvider = new HashSet(); + _authenticationsWithAppSpecifiedProvider = authenticationsWithAppSpecifiedProvider; + + if (configSection == null) + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No SqlAuthProviders configuration section found."); + return; + } + + // Create user-defined auth initializer, if any. + if (!string.IsNullOrEmpty(configSection.InitializerType)) + { + try + { + var initializerType = Type.GetType(configSection.InitializerType, true); + _initializer = (SqlAuthenticationInitializer)Activator.CreateInstance(initializerType); + _initializer.Initialize(); + } + catch (Exception e) + { + throw SQL.CannotCreateSqlAuthInitializer(configSection.InitializerType, e); + } + _sqlAuthLogger.LogInfo(_typeName, methodName, "Created user-defined SqlAuthenticationInitializer."); + } + else + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined SqlAuthenticationInitializer found."); + } + + // add user-defined providers, if any. + if (configSection.Providers != null && configSection.Providers.Count > 0) + { + foreach (ProviderSettings providerSettings in configSection.Providers) + { + SqlAuthenticationMethod authentication = AuthenticationEnumFromString(providerSettings.Name); + SqlAuthenticationProvider provider; + try + { + var providerType = Type.GetType(providerSettings.Type, true); + provider = (SqlAuthenticationProvider)Activator.CreateInstance(providerType); + } + catch (Exception e) + { + throw SQL.CannotCreateAuthProvider(authentication.ToString(), providerSettings.Type, e); + } + if (!provider.IsSupported(authentication)) + { + throw SQL.UnsupportedAuthenticationByProvider(authentication.ToString(), providerSettings.Type); + } + + _providers[authentication] = provider; + authenticationsWithAppSpecifiedProvider.Add(authentication); + _sqlAuthLogger.LogInfo(_typeName, methodName, string.Format("Added user-defined auth provider: {0} for authentication {1}.", providerSettings?.Type, authentication)); + } + } + else + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined auth providers."); + } + } + + private static SqlAuthenticationMethod AuthenticationEnumFromString(string authentication) + { + switch (authentication.ToLowerInvariant()) + { + case ActiveDirectoryPassword: + return SqlAuthenticationMethod.ActiveDirectoryPassword; + default: + throw SQL.UnsupportedAuthentication(authentication); + } + } + + /// + /// The configuration section definition for reading app.config. + /// + internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSection + { + public const string Name = "SqlAuthenticationProviders"; + + /// + /// User-defined auth providers. + /// + [ConfigurationProperty("providers")] + public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; + + /// + /// User-defined initializer. + /// + [ConfigurationProperty("initializerType")] + public string InitializerType => base["initializerType"] as string; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs new file mode 100644 index 0000000000..ef6328af0d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.SqlClient +{ + internal partial class SqlAuthenticationProviderManager + { + static SqlAuthenticationProviderManager() + { + var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider(); + Instance = new SqlAuthenticationProviderManager(); + Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 0d382aa512..b9a002a240 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -10,106 +10,31 @@ namespace Microsoft.Data.SqlClient { - /// /// Authentication provider manager. /// - internal class SqlAuthenticationProviderManager + internal partial class SqlAuthenticationProviderManager { private const string ActiveDirectoryPassword = "active directory password"; private const string ActiveDirectoryIntegrated = "active directory integrated"; private const string ActiveDirectoryInteractive = "active directory interactive"; - static SqlAuthenticationProviderManager() - { - var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider(); - SqlAuthenticationProviderConfigurationSection configurationSection; - try - { - configurationSection = (SqlAuthenticationProviderConfigurationSection)ConfigurationManager.GetSection(SqlAuthenticationProviderConfigurationSection.Name); - } - catch (ConfigurationErrorsException e) - { - throw SQL.CannotGetAuthProviderConfig(e); - } - Instance = new SqlAuthenticationProviderManager(configurationSection); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider); - } - public static readonly SqlAuthenticationProviderManager Instance; - private readonly string _typeName; - private readonly SqlAuthenticationInitializer _initializer; private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; private readonly ConcurrentDictionary _providers; private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger(); + public static readonly SqlAuthenticationProviderManager Instance; + /// /// Constructor. /// - public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSection configSection) + public SqlAuthenticationProviderManager() { _typeName = GetType().Name; - var methodName = "Ctor"; _providers = new ConcurrentDictionary(); - var authenticationsWithAppSpecifiedProvider = new HashSet(); - _authenticationsWithAppSpecifiedProvider = authenticationsWithAppSpecifiedProvider; - - if (configSection == null) - { - _sqlAuthLogger.LogInfo(_typeName, methodName, "No SqlAuthProviders configuration section found."); - return; - } - - // Create user-defined auth initializer, if any. - // - if (!string.IsNullOrEmpty(configSection.InitializerType)) - { - try - { - var initializerType = Type.GetType(configSection.InitializerType, true); - _initializer = (SqlAuthenticationInitializer)Activator.CreateInstance(initializerType); - _initializer.Initialize(); - } - catch (Exception e) - { - throw SQL.CannotCreateSqlAuthInitializer(configSection.InitializerType, e); - } - _sqlAuthLogger.LogInfo(_typeName, methodName, "Created user-defined SqlAuthenticationInitializer."); - } - else - { - _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined SqlAuthenticationInitializer found."); - } - - // add user-defined providers, if any. - // - if (configSection.Providers != null && configSection.Providers.Count > 0) - { - foreach (ProviderSettings providerSettings in configSection.Providers) - { - SqlAuthenticationMethod authentication = AuthenticationEnumFromString(providerSettings.Name); - SqlAuthenticationProvider provider; - try - { - var providerType = Type.GetType(providerSettings.Type, true); - provider = (SqlAuthenticationProvider)Activator.CreateInstance(providerType); - } - catch (Exception e) - { - throw SQL.CannotCreateAuthProvider(authentication.ToString(), providerSettings.Type, e); - } - if (!provider.IsSupported(authentication)) - throw SQL.UnsupportedAuthenticationByProvider(authentication.ToString(), providerSettings.Type); - - _providers[authentication] = provider; - authenticationsWithAppSpecifiedProvider.Add(authentication); - _sqlAuthLogger.LogInfo(_typeName, methodName, string.Format("Added user-defined auth provider: {0} for authentication {1}.", providerSettings?.Type, authentication)); - } - } - else - { - _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined auth providers."); - } + _authenticationsWithAppSpecifiedProvider = new HashSet(); + _sqlAuthLogger.LogInfo(_typeName, "Ctor", "No SqlAuthProviders configuration section found."); } /// @@ -156,17 +81,6 @@ public bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthent return true; } - private static SqlAuthenticationMethod AuthenticationEnumFromString(string authentication) - { - switch (authentication.ToLowerInvariant()) - { - case ActiveDirectoryPassword: - return SqlAuthenticationMethod.ActiveDirectoryPassword; - default: - throw SQL.UnsupportedAuthentication(authentication); - } - } - private static string GetProviderType(SqlAuthenticationProvider provider) { if (provider == null) @@ -175,26 +89,6 @@ private static string GetProviderType(SqlAuthenticationProvider provider) } } - /// - /// The configuration section definition for reading app.config. - /// - internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSection - { - public const string Name = "SqlAuthenticationProviders"; - - /// - /// User-defined auth providers. - /// - [ConfigurationProperty("providers")] - public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; - - /// - /// User-defined initializer. - /// - [ConfigurationProperty("initializerType")] - public string InitializerType => base["initializerType"] as string; - } - /// public abstract class SqlAuthenticationInitializer { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs index d8b8b073c3..7bcd7cffb5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs @@ -38,7 +38,7 @@ internal static class SqlClientDiagnosticListenerExtensions public const string SqlAfterRollbackTransaction = SqlClientPrefix + nameof(WriteTransactionRollbackAfter); public const string SqlErrorRollbackTransaction = SqlClientPrefix + nameof(WriteTransactionRollbackError); - public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand sqlCommand, SqlTransaction transaction, [CallerMemberName] string operation = "") + public static Guid WriteCommandBefore(this SqlDiagnosticListener @this, SqlCommand sqlCommand, SqlTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeExecuteCommand)) { @@ -62,7 +62,7 @@ public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand return Guid.Empty; } - public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, SqlTransaction transaction, [CallerMemberName] string operation = "") + public static void WriteCommandAfter(this SqlDiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, SqlTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterExecuteCommand)) { @@ -81,7 +81,7 @@ public static void WriteCommandAfter(this DiagnosticListener @this, Guid operati } } - public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, SqlTransaction transaction, Exception ex, [CallerMemberName] string operation = "") + public static void WriteCommandError(this SqlDiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, SqlTransaction transaction, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorExecuteCommand)) { @@ -100,7 +100,7 @@ public static void WriteCommandError(this DiagnosticListener @this, Guid operati } } - public static Guid WriteConnectionOpenBefore(this DiagnosticListener @this, SqlConnection sqlConnection, [CallerMemberName] string operation = "") + public static Guid WriteConnectionOpenBefore(this SqlDiagnosticListener @this, SqlConnection sqlConnection, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeOpenConnection)) { @@ -123,7 +123,7 @@ public static Guid WriteConnectionOpenBefore(this DiagnosticListener @this, SqlC return Guid.Empty; } - public static void WriteConnectionOpenAfter(this DiagnosticListener @this, Guid operationId, SqlConnection sqlConnection, [CallerMemberName] string operation = "") + public static void WriteConnectionOpenAfter(this SqlDiagnosticListener @this, Guid operationId, SqlConnection sqlConnection, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterOpenConnection)) { @@ -142,7 +142,7 @@ public static void WriteConnectionOpenAfter(this DiagnosticListener @this, Guid } } - public static void WriteConnectionOpenError(this DiagnosticListener @this, Guid operationId, SqlConnection sqlConnection, Exception ex, [CallerMemberName] string operation = "") + public static void WriteConnectionOpenError(this SqlDiagnosticListener @this, Guid operationId, SqlConnection sqlConnection, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorOpenConnection)) { @@ -161,7 +161,7 @@ public static void WriteConnectionOpenError(this DiagnosticListener @this, Guid } } - public static Guid WriteConnectionCloseBefore(this DiagnosticListener @this, SqlConnection sqlConnection, [CallerMemberName] string operation = "") + public static Guid WriteConnectionCloseBefore(this SqlDiagnosticListener @this, SqlConnection sqlConnection, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeCloseConnection)) { @@ -185,7 +185,7 @@ public static Guid WriteConnectionCloseBefore(this DiagnosticListener @this, Sql return Guid.Empty; } - public static void WriteConnectionCloseAfter(this DiagnosticListener @this, Guid operationId, Guid clientConnectionId, SqlConnection sqlConnection, [CallerMemberName] string operation = "") + public static void WriteConnectionCloseAfter(this SqlDiagnosticListener @this, Guid operationId, Guid clientConnectionId, SqlConnection sqlConnection, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterCloseConnection)) { @@ -203,7 +203,7 @@ public static void WriteConnectionCloseAfter(this DiagnosticListener @this, Guid } } - public static void WriteConnectionCloseError(this DiagnosticListener @this, Guid operationId, Guid clientConnectionId, SqlConnection sqlConnection, Exception ex, [CallerMemberName] string operation = "") + public static void WriteConnectionCloseError(this SqlDiagnosticListener @this, Guid operationId, Guid clientConnectionId, SqlConnection sqlConnection, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorCloseConnection)) { @@ -222,7 +222,7 @@ public static void WriteConnectionCloseError(this DiagnosticListener @this, Guid } } - public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operation = "") + public static Guid WriteTransactionCommitBefore(this SqlDiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeCommitTransaction)) { @@ -246,7 +246,7 @@ public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, I return Guid.Empty; } - public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operation = "") + public static void WriteTransactionCommitAfter(this SqlDiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterCommitTransaction)) { @@ -264,7 +264,7 @@ public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Gu } } - public static void WriteTransactionCommitError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, Exception ex, [CallerMemberName] string operation = "") + public static void WriteTransactionCommitError(this SqlDiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorCommitTransaction)) { @@ -283,7 +283,7 @@ public static void WriteTransactionCommitError(this DiagnosticListener @this, Gu } } - public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName = null, [CallerMemberName] string operation = "") + public static Guid WriteTransactionRollbackBefore(this SqlDiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName = null, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlBeforeRollbackTransaction)) { @@ -308,7 +308,7 @@ public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, return Guid.Empty; } - public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName = null, [CallerMemberName] string operation = "") + public static void WriteTransactionRollbackAfter(this SqlDiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName = null, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlAfterRollbackTransaction)) { @@ -327,7 +327,7 @@ public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, } } - public static void WriteTransactionRollbackError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, Exception ex, string transactionName = null, [CallerMemberName] string operation = "") + public static void WriteTransactionRollbackError(this SqlDiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, Exception ex, string transactionName = null, [CallerMemberName] string operation = "") { if (@this.IsEnabled(SqlErrorRollbackTransaction)) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 9f71f8a5b3..a862d74bcf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -69,7 +69,7 @@ public sealed partial class SqlCommand : DbCommand, ICloneable private static bool _forceInternalEndQuery = false; #endif - private static readonly DiagnosticListener _diagnosticListener = new DiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + private static readonly SqlDiagnosticListener _diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); private bool _parentOperationStarted = false; internal static readonly Action s_cancelIgnoreFailure = CancelIgnoreFailureCallback; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 201763a227..344702dae2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -61,7 +61,7 @@ private enum CultureCheckState : uint private int _reconnectCount; // diagnostics listener - private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); // 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 @@ -2065,5 +2065,3 @@ private SqlUdtInfo GetInfoFromType(Type t) } } } - - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs new file mode 100644 index 0000000000..ec3b6549b3 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Microsoft.Data.SqlClient +{ + internal sealed class SqlDiagnosticListener : DiagnosticListener + { + public SqlDiagnosticListener(string name) : base(name) + { + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetStandard.cs new file mode 100644 index 0000000000..726a24c934 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetStandard.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Data.SqlClient +{ + internal sealed class SqlDiagnosticListener : IObservable> + { + public SqlDiagnosticListener(string name) + { + } + + public IDisposable Subscribe(IObserver> observer) + { + throw new NotSupportedException(); + } + + public bool IsEnabled() + { + return false; + } + + public bool IsEnabled(string name) + { + return false; + } + + internal void Write(string sqlBeforeExecuteCommand, object p) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index c43c10471c..0021538d70 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -367,8 +367,7 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal SessionData reconnectSessionData = null, bool applyTransientFaultHandling = false, string accessToken = null, - DbConnectionPool pool = null, - SqlAuthenticationProviderManager sqlAuthProviderManager = null + DbConnectionPool pool = null ) : base(connectionOptions) { @@ -403,7 +402,7 @@ internal SqlConnectionTimeoutErrorInternal TimeoutErrorInternal } _activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper(); - _sqlAuthenticationProviderManager = sqlAuthProviderManager ?? SqlAuthenticationProviderManager.Instance; + _sqlAuthenticationProviderManager = SqlAuthenticationProviderManager.Instance; _identity = identity; Debug.Assert(newSecurePassword != null || newPassword != null, "cannot have both new secure change password and string based change password to be null"); @@ -1753,8 +1752,7 @@ private void ResolveExtendedServerName(ServerInfo serverInfo, bool aliasLookup, ConnectionOptions.TrustServerCertificate, ConnectionOptions.IntegratedSecurity, withFailover, - ConnectionOptions.Authentication, - _sqlAuthenticationProviderManager); + ConnectionOptions.Authentication); _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); _timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs index fb34462779..4a936b4c7c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -14,8 +14,7 @@ namespace Microsoft.Data.SqlClient /// public sealed class SqlTransaction : DbTransaction { - private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); - + private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; @@ -350,4 +349,3 @@ private void ZombieCheck() } } } - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 02f05a7822..07c5c56a7c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -344,8 +344,7 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) bool trustServerCert, bool integratedSecurity, bool withFailover, - SqlAuthenticationMethod authType, - SqlAuthenticationProviderManager sqlAuthProviderManager) + SqlAuthenticationMethod authType) { if (_state != TdsParserState.Closed) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index eb42d38f1a..5a778a1b43 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -363,9 +363,7 @@ static SqlInternalConnectionTds() DbConnectionPool pool = null, string accessToken = null, SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo = null, - bool applyTransientFaultHandling = false, - SqlAuthenticationProviderManager sqlAuthProviderManager = null - ) : base(connectionOptions) + bool applyTransientFaultHandling = false) : base(connectionOptions) { #if DEBUG @@ -421,7 +419,7 @@ static SqlInternalConnectionTds() } _activeDirectoryAuthTimeoutRetryHelper = new ActiveDirectoryAuthenticationTimeoutRetryHelper(); - _sqlAuthenticationProviderManager = sqlAuthProviderManager ?? SqlAuthenticationProviderManager.Instance; + _sqlAuthenticationProviderManager = SqlAuthenticationProviderManager.Instance; _serverCallback = serverCallback; _clientCallback = clientCallback; @@ -2203,8 +2201,7 @@ private void AttemptOneLogin(ServerInfo serverInfo, string newPassword, SecureSt _serverCallback, _clientCallback, _originalNetworkAddressInfo != null, - disableTnir, - _sqlAuthenticationProviderManager); + disableTnir); timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 3ebf40d816..f23859b523 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -483,8 +483,7 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) ServerCertificateValidationCallback serverCallback, ClientCertificateRetrievalCallback clientCallback, bool useOriginalAddressInfo, - bool disableTnir, - SqlAuthenticationProviderManager sqlAuthProviderManager) + bool disableTnir) { if (_state != TdsParserState.Closed) { diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 5466991bb1..b4aaf443ac 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -60,7 +60,6 @@ When using NuGet 3.x this package requires at least version 3.4. - From 0af9f3230dac2577df6a637ca2fc60839047dc5a Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 1 May 2020 08:51:30 -0700 Subject: [PATCH 10/13] Fix | Avoid enlistment of pooled connection in aborted transaction (#543) --- .../Data/SqlClient/SqlDelegatedTransaction.cs | 416 ++++++++++-------- .../Data/SqlClient/SqlDelegatedTransaction.cs | 394 +++++++++-------- 2 files changed, 434 insertions(+), 376 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index 2ad32b4d05..490e58277b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -137,79 +137,92 @@ public byte[] Promote() // from an operational standpoint (i.e. logging connection's ObjectID should be fine, // but the PromotedDTCToken can change over calls. so that must be protected). SqlInternalConnection connection = GetValidConnection(); - Exception promoteException; byte[] returnValue = null; - SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, promoting transaction.", ObjectID, connection.ObjectID); - RuntimeHelpers.PrepareConstrainedRegions(); - try + + if (null != connection) { - lock (connection) + SqlConnection usersConnection = connection.Connection; + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, promoting transaction.", ObjectID, connection.ObjectID); + RuntimeHelpers.PrepareConstrainedRegions(); + try { - try + lock (connection) { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); + try + { + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - returnValue = _connection.PromotedDTCToken; + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + returnValue = _connection.PromotedDTCToken; - // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. - if (_connection.IsGlobalTransaction) - { - if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) + // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. + if (_connection.IsGlobalTransaction) { - throw SQL.UnsupportedSysTxForGlobalTransactions(); - } + if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) + { + throw SQL.UnsupportedSysTxForGlobalTransactions(); + } - if (!_connection.IsGlobalTransactionsEnabledForServer) - { - throw SQL.GlobalTransactionsNotEnabled(); + if (!_connection.IsGlobalTransactionsEnabledForServer) + { + throw SQL.GlobalTransactionsNotEnabled(); + } + + SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); } - SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); + promoteException = null; } + catch (SqlException e) + { + promoteException = e; - promoteException = null; + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + } + catch (InvalidOperationException e) + { + promoteException = e; + connection.DoomThisConnection(); + } } - catch (SqlException e) - { - promoteException = e; + } + catch (System.OutOfMemoryException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + usersConnection.Abort(e); + throw; + } - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - } - catch (InvalidOperationException e) - { - promoteException = e; - connection.DoomThisConnection(); - } + //Throw exception only if Transaction is still active and not yet aborted. + if (promoteException != null && Transaction.TransactionInformation.Status != TransactionStatus.Aborted) + { + throw SQL.PromotionFailed(promoteException); + } + else + { + // The transaction was aborted externally, since it's already doomed above, we only log the same. + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted during promotion.", ObjectID, connection.ObjectID); } } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - usersConnection.Abort(e); - throw; - } - - if (promoteException != null) + else { - throw SQL.PromotionFailed(promoteException); + // The transaction was aborted externally, doom the connection to make sure it's eventually rolled back and log the same. + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted before promoting.", ObjectID, connection.ObjectID); } - return returnValue; } @@ -217,72 +230,81 @@ public byte[] Promote() public void Rollback(SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); - SqlInternalConnection connection = GetValidConnection(); - SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborting transaction.", ObjectID, connection.ObjectID); - RuntimeHelpers.PrepareConstrainedRegions(); - try + + if (null != connection) { - lock (connection) + SqlConnection usersConnection = connection.Connection; + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, rolling back transaction.", ObjectID, connection.ObjectID); + RuntimeHelpers.PrepareConstrainedRegions(); + try { - try + lock (connection) { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); - _active = false; // set to inactive first, doesn't matter how the execute completes, this transaction is done. - _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event + try + { + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); + _active = false; // set to inactive first, doesn't matter how the execute completes, this transaction is done. + _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event - // If we haven't already rolled back (or aborted) then tell the SQL Server to roll back - if (!_internalTransaction.IsAborted) + // If we haven't already rolled back (or aborted) then tell the SQL Server to roll back + if (!_internalTransaction.IsAborted) + { + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + } + } + catch (SqlException) { - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + + // Unlike SinglePhaseCommit, a rollback is a rollback, regardless + // of how it happens, so SysTx won't throw an exception, and we + // don't want to throw an exception either, because SysTx isn't + // handling it and it may create a fail fast scenario. In the end, + // there is no way for us to communicate to the consumer that this + // failed for more serious reasons than usual. + // + // This is a bit like "should you throw if Close fails", however, + // it only matters when you really need to know. In that case, + // we have the tracing that we're doing to fallback on for the + // investigation. + } + catch (InvalidOperationException) + { + connection.DoomThisConnection(); } } - catch (SqlException) - { - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - - // Unlike SinglePhaseCommit, a rollback is a rollback, regardless - // of how it happens, so SysTx won't throw an exception, and we - // don't want to throw an exception either, because SysTx isn't - // handling it and it may create a fail fast scenario. In the end, - // there is no way for us to communicate to the consumer that this - // failed for more serious reasons than usual. - // - // This is a bit like "should you throw if Close fails", however, - // it only matters when you really need to know. In that case, - // we have the tracing that we're doing to fallback on for the - // investigation. - } - catch (InvalidOperationException) - { - connection.DoomThisConnection(); - } - } - // it doesn't matter whether the rollback succeeded or not, we presume - // that the transaction is aborted, because it will be eventually. - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - enlistment.Aborted(); - } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; + // it doesn't matter whether the rollback succeeded or not, we presume + // that the transaction is aborted, because it will be eventually. + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + enlistment.Aborted(); + } + catch (System.OutOfMemoryException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + usersConnection.Abort(e); + throw; + } } - catch (System.Threading.ThreadAbortException e) + else { - usersConnection.Abort(e); - throw; + // The transaction was aborted, report that to SysTx and log the same. + enlistment.Aborted(); + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted before rollback.", ObjectID, connection.ObjectID); } } @@ -290,107 +312,116 @@ public void Rollback(SinglePhaseEnlistment enlistment) public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); - SqlInternalConnection connection = GetValidConnection(); - SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, committing transaction.", ObjectID, connection.ObjectID); - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - // If the connection is doomed, we can be certain that the - // transaction will eventually be rolled back, and we shouldn't - // attempt to commit it. - if (connection.IsConnectionDoomed) - { - lock (connection) - { - _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; - } - enlistment.Aborted(SQL.ConnectionDoomed()); - } - else + if (null != connection) + { + SqlConnection usersConnection = connection.Connection; + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, committing transaction.", ObjectID, connection.ObjectID); + RuntimeHelpers.PrepareConstrainedRegions(); + try { - Exception commitException; - lock (connection) + // If the connection is doomed, we can be certain that the + // transaction will eventually be rolled back, and we shouldn't + // attempt to commit it. + if (connection.IsConnectionDoomed) { - try + lock (connection) { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); - _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event - - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - commitException = null; + _connection = null; } - catch (SqlException e) - { - commitException = e; - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - } - catch (InvalidOperationException e) - { - commitException = e; - connection.DoomThisConnection(); - } + enlistment.Aborted(SQL.ConnectionDoomed()); } - if (commitException != null) + else { - // connection.ExecuteTransaction failed with exception - if (_internalTransaction.IsCommitted) + Exception commitException; + lock (connection) { - // Even though we got an exception, the transaction - // was committed by the server. - enlistment.Committed(); + try + { + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); + + _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. + _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event + + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); + commitException = null; + } + catch (SqlException e) + { + commitException = e; + + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + } + catch (InvalidOperationException e) + { + commitException = e; + connection.DoomThisConnection(); + } } - else if (_internalTransaction.IsAborted) + if (commitException != null) { - // The transaction was aborted, report that to - // SysTx. - enlistment.Aborted(commitException); + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) + { + // Even though we got an exception, the transaction + // was committed by the server. + enlistment.Committed(); + } + else if (_internalTransaction.IsAborted) + { + // The transaction was aborted, report that to + // SysTx. + enlistment.Aborted(commitException); + } + else + { + // The transaction is still active, we cannot + // know the state of the transaction. + enlistment.InDoubt(commitException); + } + + // We eat the exception. This is called on the SysTx + // thread, not the applications thread. If we don't + // eat the exception an UnhandledException will occur, + // causing the process to FailFast. } - else + + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + if (commitException == null) { - // The transaction is still active, we cannot - // know the state of the transaction. - enlistment.InDoubt(commitException); + // connection.ExecuteTransaction succeeded + enlistment.Committed(); } - - // We eat the exception. This is called on the SysTx - // thread, not the applications thread. If we don't - // eat the exception an UnhandledException will occur, - // causing the process to FailFast. - } - - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - if (commitException == null) - { - // connection.ExecuteTransaction succeeded - enlistment.Committed(); } } + catch (System.OutOfMemoryException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + usersConnection.Abort(e); + throw; + } } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) + else { - usersConnection.Abort(e); - throw; + // The transaction was aborted before we could commit, report that to SysTx and log the same. + enlistment.Aborted(); + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted before commit.", ObjectID, connection.ObjectID); } } @@ -413,6 +444,9 @@ internal void TransactionEnded(Transaction transaction) _active = false; _connection = null; } + // Safest approach is to doom this connection, whose transaction has been aborted externally. + // If we want to avoid dooming the connection for performance, state needs to be properly restored. (future TODO) + connection.DoomThisConnection(); } } } @@ -421,7 +455,7 @@ internal void TransactionEnded(Transaction transaction) private SqlInternalConnection GetValidConnection() { SqlInternalConnection connection = _connection; - if (null == connection) + if (null == connection && Transaction.TransactionInformation.Status != TransactionStatus.Aborted) { throw ADP.ObjectDisposed(this); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index 21a5966f3e..f98f746286 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -36,7 +36,6 @@ internal int ObjectID private SqlInternalConnection _connection; // the internal connection that is the root of the transaction private IsolationLevel _isolationLevel; // the IsolationLevel of the transaction we delegated to the server private SqlInternalTransaction _internalTransaction; // the SQL Server transaction we're delegating to - private SysTx.Transaction _atomicTransaction; private bool _active; // Is the transaction active? @@ -167,97 +166,110 @@ public Byte[] Promote() Exception promoteException; byte[] returnValue = null; - SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, promoting transaction.", ObjectID, connection.ObjectID); - RuntimeHelpers.PrepareConstrainedRegions(); - - try + if (null != connection) { -#if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); - + SqlConnection usersConnection = connection.Connection; + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, promoting transaction.", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); + try { - tdsReliabilitySection.Start(); +#if DEBUG + TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + tdsReliabilitySection.Start(); #else - { -#endif //DEBUG - lock (connection) { - try +#endif //DEBUG + lock (connection) { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); + try + { + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, IsolationLevel.Unspecified, _internalTransaction, true); - returnValue = _connection.PromotedDTCToken; + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Promote, null, IsolationLevel.Unspecified, _internalTransaction, true); + returnValue = _connection.PromotedDTCToken; - // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. - if (_connection.IsGlobalTransaction) - { - if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) + // For Global Transactions, we need to set the Transaction Id since we use a Non-MSDTC Promoter type. + if (_connection.IsGlobalTransaction) { - throw SQL.UnsupportedSysTxForGlobalTransactions(); - } + if (SysTxForGlobalTransactions.SetDistributedTransactionIdentifier == null) + { + throw SQL.UnsupportedSysTxForGlobalTransactions(); + } - if (!_connection.IsGlobalTransactionsEnabledForServer) - { - throw SQL.GlobalTransactionsNotEnabled(); + if (!_connection.IsGlobalTransactionsEnabledForServer) + { + throw SQL.GlobalTransactionsNotEnabled(); + } + + SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); } - SysTxForGlobalTransactions.SetDistributedTransactionIdentifier.Invoke(_atomicTransaction, new object[] { this, GetGlobalTxnIdentifierFromToken() }); + promoteException = null; } + catch (SqlException e) + { + promoteException = e; - promoteException = null; - } - catch (SqlException e) - { - promoteException = e; - - ADP.TraceExceptionWithoutRethrow(e); + ADP.TraceExceptionWithoutRethrow(e); - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); - } - catch (InvalidOperationException e) - { - promoteException = e; - ADP.TraceExceptionWithoutRethrow(e); - connection.DoomThisConnection(); + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + } + catch (InvalidOperationException e) + { + promoteException = e; + ADP.TraceExceptionWithoutRethrow(e); + connection.DoomThisConnection(); + } } } - } #if DEBUG - finally + finally + { + tdsReliabilitySection.Stop(); + } +#endif //DEBUG + } + catch (System.OutOfMemoryException e) { - tdsReliabilitySection.Stop(); + usersConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + usersConnection.Abort(e); + throw; } -#endif //DEBUG - } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - usersConnection.Abort(e); - throw; - } - if (promoteException != null) + //Throw exception only if Transaction is still active and not yet aborted. + if (promoteException != null && Transaction.TransactionInformation.Status != SysTx.TransactionStatus.Aborted) + { + throw SQL.PromotionFailed(promoteException); + } + else + { + // The transaction was aborted externally, since it's already doomed above, we only log the same. + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted during promote.", ObjectID, connection.ObjectID); + } + } + else { - throw SQL.PromotionFailed(promoteException); + // The transaction was aborted externally, doom the connection to make sure it's eventually rolled back and log the same. + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted before promote.", ObjectID, connection.ObjectID); } - return returnValue; } @@ -265,24 +277,19 @@ public Byte[] Promote() public void Rollback(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); - SqlInternalConnection connection = GetValidConnection(); - SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborting transaction.", ObjectID, connection.ObjectID); - RuntimeHelpers.PrepareConstrainedRegions(); - try + if (null != connection) { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); - + tdsReliabilitySection.Start(); +#endif //DEBUG + SqlConnection usersConnection = connection.Connection; RuntimeHelpers.PrepareConstrainedRegions(); + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, rolling back transaction.", ObjectID, connection.ObjectID); try { - tdsReliabilitySection.Start(); -#else - { -#endif //DEBUG lock (connection) { try @@ -331,6 +338,21 @@ public void Rollback(SysTx.SinglePhaseEnlistment enlistment) connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); enlistment.Aborted(); } + catch (System.OutOfMemoryException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + usersConnection.Abort(e); + throw; + } #if DEBUG finally { @@ -338,20 +360,11 @@ public void Rollback(SysTx.SinglePhaseEnlistment enlistment) } #endif //DEBUG } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) + else { - usersConnection.Abort(e); - throw; + // The transaction was aborted, report that to SysTx and log the same. + enlistment.Aborted(); + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted before rollback.", ObjectID, connection.ObjectID); } } @@ -359,128 +372,136 @@ public void Rollback(SysTx.SinglePhaseEnlistment enlistment) public void SinglePhaseCommit(SysTx.SinglePhaseEnlistment enlistment) { Debug.Assert(null != enlistment, "null enlistment?"); - SqlInternalConnection connection = GetValidConnection(); - SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, committing transaction.", ObjectID, connection.ObjectID); - RuntimeHelpers.PrepareConstrainedRegions(); - try - { -#if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + if (null != connection) + { + SqlConnection usersConnection = connection.Connection; + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, committing transaction.", ObjectID, connection.ObjectID); RuntimeHelpers.PrepareConstrainedRegions(); + try { - tdsReliabilitySection.Start(); +#if DEBUG + TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + try + { + tdsReliabilitySection.Start(); #else - { -#endif //DEBUG - // If the connection is dooomed, we can be certain that the - // transaction will eventually be rolled back, and we shouldn't - // attempt to commit it. - if (connection.IsConnectionDoomed) { - lock (connection) +#endif //DEBUG + // If the connection is doomed, we can be certain that the + // transaction will eventually be rolled back, and we shouldn't + // attempt to commit it. + if (connection.IsConnectionDoomed) { - _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; - } + lock (connection) + { + _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. + _connection = null; + } - enlistment.Aborted(SQL.ConnectionDoomed()); - } - else - { - Exception commitException; - lock (connection) + enlistment.Aborted(SQL.ConnectionDoomed()); + } + else { - try + Exception commitException; + lock (connection) { - // Now that we've acquired the lock, make sure we still have valid state for this operation. - ValidateActiveOnConnection(connection); + try + { + // Now that we've acquired the lock, make sure we still have valid state for this operation. + ValidateActiveOnConnection(connection); - _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. - _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event + _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. + _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event - connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, _internalTransaction, true); - commitException = null; - } - catch (SqlException e) - { - commitException = e; + connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, _internalTransaction, true); + commitException = null; + } + catch (SqlException e) + { + commitException = e; - ADP.TraceExceptionWithoutRethrow(e); + ADP.TraceExceptionWithoutRethrow(e); - // Doom the connection, to make sure that the transaction is - // eventually rolled back. - // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event - connection.DoomThisConnection(); + // Doom the connection, to make sure that the transaction is + // eventually rolled back. + // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event + connection.DoomThisConnection(); + } + catch (InvalidOperationException e) + { + commitException = e; + ADP.TraceExceptionWithoutRethrow(e); + connection.DoomThisConnection(); + } } - catch (InvalidOperationException e) + if (commitException != null) { - commitException = e; - ADP.TraceExceptionWithoutRethrow(e); - connection.DoomThisConnection(); + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) + { + // Even though we got an exception, the transaction + // was committed by the server. + enlistment.Committed(); + } + else if (_internalTransaction.IsAborted) + { + // The transaction was aborted, report that to + // SysTx. + enlistment.Aborted(commitException); + } + else + { + // The transaction is still active, we cannot + // know the state of the transaction. + enlistment.InDoubt(commitException); + } + + // We eat the exception. This is called on the SysTx + // thread, not the applications thread. If we don't + // eat the exception an UnhandledException will occur, + // causing the process to FailFast. } - } - if (commitException != null) - { - // connection.ExecuteTransaction failed with exception - if (_internalTransaction.IsCommitted) + + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); + if (commitException == null) { - // Even though we got an exception, the transaction - // was committed by the server. + // connection.ExecuteTransaction succeeded enlistment.Committed(); } - else if (_internalTransaction.IsAborted) - { - // The transaction was aborted, report that to - // SysTx. - enlistment.Aborted(commitException); - } - else - { - // The transaction is still active, we cannot - // know the state of the transaction. - enlistment.InDoubt(commitException); - } - - // We eat the exception. This is called on the SysTx - // thread, not the applications thread. If we don't - // eat the exception an UnhandledException will occur, - // causing the process to FailFast. - } - - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - if (commitException == null) - { - // connection.ExecuteTransaction succeeded - enlistment.Committed(); } } - } #if DEBUG - finally + finally + { + tdsReliabilitySection.Stop(); + } +#endif //DEBUG + } + catch (System.OutOfMemoryException e) { - tdsReliabilitySection.Stop(); + usersConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + usersConnection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + usersConnection.Abort(e); + throw; } -#endif //DEBUG - } - catch (System.OutOfMemoryException e) - { - usersConnection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - usersConnection.Abort(e); - throw; } - catch (System.Threading.ThreadAbortException e) + else { - usersConnection.Abort(e); - throw; + // The transaction was aborted before we could commit, report that to SysTx and log the same. + enlistment.Aborted(); + SqlClientEventSource.Log.TraceEvent(" {0}, Connection {1}, aborted before commit.", ObjectID, connection.ObjectID); } } @@ -504,6 +525,9 @@ internal void TransactionEnded(SysTx.Transaction transaction) _active = false; _connection = null; } + // Safest approach is to doom this connection, whose transaction has been aborted externally. + // If we want to avoid dooming the connection for performance, state needs to be properly restored. (future TODO) + connection.DoomThisConnection(); } } } @@ -512,7 +536,7 @@ internal void TransactionEnded(SysTx.Transaction transaction) private SqlInternalConnection GetValidConnection() { SqlInternalConnection connection = _connection; - if (null == connection) + if (null == connection && _atomicTransaction.TransactionInformation.Status != SysTx.TransactionStatus.Aborted) { throw ADP.ObjectDisposed(this); } From c489f196716d4ce0dbe76e12255d33573cfcf256 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 1 May 2020 09:39:14 -0700 Subject: [PATCH 11/13] Revert change that made `SqlDataReader.ReadAsync()` non-blocking (#547) --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 9 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 3 +- .../SQL/SqlCommand/SqlCommandCancelTest.cs | 100 ++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 07c5c56a7c..cfac61e4c7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -2846,6 +2846,12 @@ private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavio ushort status; int count; + // This is added back since removing it from here introduces regressions in Managed SNI. + // It forces SqlDataReader.ReadAsync() method to run synchronously, + // and will block the calling thread until data is fed from SQL Server. + // TODO Investigate better solution to support non-blocking ReadAsync(). + stateObj._syncOverAsync = true; + // status // command // rowcount (valid only if DONE_COUNT bit is set) @@ -2945,11 +2951,10 @@ private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavio } } - // _attentionSent set by 'SendAttention' // _pendingData set by e.g. 'TdsExecuteSQLBatch' // _hasOpenResult always set to true by 'WriteMarsHeader' // - if (!stateObj._attentionSent && !stateObj.HasPendingData && stateObj.HasOpenResult) + if (!stateObj.HasPendingData && stateObj.HasOpenResult) { /* Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) || diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index f23859b523..d97a5aa2bd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3336,11 +3336,10 @@ private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavio } } - // _attentionSent set by 'SendAttention' // _pendingData set by e.g. 'TdsExecuteSQLBatch' // _hasOpenResult always set to true by 'WriteMarsHeader' // - if (!stateObj._attentionSent && !stateObj._pendingData && stateObj._hasOpenResult) + if (!stateObj._pendingData && stateObj._hasOpenResult) { /* Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) || diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index a8cbbdf6f4..121f992d02 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -212,6 +212,106 @@ public static void AsyncCancelDoesNotWaitNP() AsyncCancelDoesNotWait(np_connStr).Wait(); } + [CheckConnStrSetupFact] + public static void TCPAttentionPacketTestTransaction() + { + CancelFollowedByTransaction(tcp_connStr); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [PlatformSpecific(TestPlatforms.Windows)] + public static void NPAttentionPacketTestTransaction() + { + CancelFollowedByTransaction(np_connStr); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + public static void TCPAttentionPacketTestAlerts() + { + CancelFollowedByAlert(tcp_connStr); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [PlatformSpecific(TestPlatforms.Windows)] + public static void NPAttentionPacketTestAlerts() + { + CancelFollowedByAlert(np_connStr); + } + + private static void CancelFollowedByTransaction(string constr) + { + using (SqlConnection connection = new SqlConnection(constr)) + { + connection.Open(); + using (SqlCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = @"SELECT @@VERSION"; + using (var r = cmd.ExecuteReader()) + { + cmd.Cancel(); + } + } + using (SqlTransaction transaction = connection.BeginTransaction()) + { } + } + } + + private static void CancelFollowedByAlert(string constr) + { + var alertName = "myAlert" + Guid.NewGuid().ToString(); + // Since Alert conditions are randomly generated, + // we will retry on unexpected error messages to avoid collision in pipelines. + var n = new Random().Next(1, 100); + bool retry = true; + int retryAttempt = 0; + while (retry && retryAttempt < 3) + { + try + { + using (var conn = new SqlConnection(constr)) + { + conn.Open(); + using (SqlCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = "SELECT @@VERSION"; + using (var reader = cmd.ExecuteReader()) + { + cmd.Cancel(); // Sends Attention + } + } + using (SqlCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = $@"EXEC msdb.dbo.sp_add_alert @name=N'{alertName}', + @performance_condition = N'SQLServer:General Statistics|User Connections||>|{n}'"; + cmd.ExecuteNonQuery(); + cmd.CommandText = @"USE [msdb]"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"/****** Object: Alert [{alertName}] Script Date: {DateTime.Now} ******/ + IF EXISTS (SELECT name FROM msdb.dbo.sysalerts WHERE name = N'{alertName}') + EXEC msdb.dbo.sp_delete_alert @name=N'{alertName}'"; + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + if (retryAttempt >= 3 || e.Message.Contains("The transaction operation cannot be performed")) + { + Assert.False(true, $"Retry Attempt: {retryAttempt} | Unexpected Exception occurred: {e.Message}"); + } + else + { + retry = true; + retryAttempt++; + Console.WriteLine($"CancelFollowedByAlert Test retry attempt : {retryAttempt}"); + Thread.Sleep(500); + continue; + } + } + retry = false; + } + } + private static void MultiThreadedCancel(string constr, bool async) { using (SqlConnection con = new SqlConnection(constr)) From 09705f52fa2b6f4fe9a247dd66e365c1e3d9a0e2 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 1 May 2020 11:02:58 -0700 Subject: [PATCH 12/13] Rename App Context switch name for Managed SNI on Windows (#548) --- BUILDGUIDE.md | 2 +- .../Data/SqlClient/TdsParserStateObjectFactory.Windows.cs | 2 +- .../tests/ManualTests/DataCommon/DataTestUtility.cs | 2 +- tools/specs/Microsoft.Data.SqlClient.nuspec | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index 3b933dfa11..825af0c93a 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -177,7 +177,7 @@ Tests can be built and run with custom Target Frameworks. See the below examples Managed SNI can be enabled on Windows by enabling the below AppContext switch: -**"Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"** +**"Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"** ## Set truncation on for scaled decimal parameters diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs index c96c32a551..193babd231 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs @@ -11,7 +11,7 @@ internal sealed class TdsParserStateObjectFactory { public static readonly TdsParserStateObjectFactory Singleton = new TdsParserStateObjectFactory(); - private const string UseManagedNetworkingOnWindows = "Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"; + private const string UseManagedNetworkingOnWindows = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"; private static bool shouldUseManagedSNI; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index eb4009c363..b26a732d16 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -43,7 +43,7 @@ public static class DataTestUtility public const string UdtTestDbName = "UdtTestDb"; public const string AKVKeyName = "TestSqlClientAzureKeyVaultProvider"; - private const string ManagedNetworkingAppContextSwitch = "Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"; + private const string ManagedNetworkingAppContextSwitch = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"; private static readonly string[] AzureSqlServerEndpoints = {".database.windows.net", ".database.cloudapi.de", diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index b4aaf443ac..a5079f9f52 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -63,7 +63,6 @@ When using NuGet 3.x this package requires at least version 3.4. - From ffc6bd06c109412fecb0f866a5bffa4ba8bc2a17 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 1 May 2020 11:40:55 -0700 Subject: [PATCH 13/13] Release Notes for v2.0.0-preview3 (#549) --- CHANGELOG.md | 24 +++++++ release-notes/2.0/2.0.0-preview3.md | 99 +++++++++++++++++++++++++++++ release-notes/2.0/2.0.md | 1 + release-notes/2.0/README.md | 1 + 4 files changed, 125 insertions(+) create mode 100644 release-notes/2.0/2.0.0-preview3.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c71fe5d10..6f280ae723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Preview Release 2.0.0-preview3.20122.2] - 2020-05-01 + +### Added +- Allow passing username with Active Directory Interactive Authentication in .NET Framework [#492](https://github.com/dotnet/SqlClient/pull/492) +- Allow large UDT buffers for .NET Framework [#456](https://github.com/dotnet/SqlClient/pull/456) +- Added "Transaction Id" and "Client Version" in Diagnostic Source traces [#515](https://github.com/dotnet/SqlClient/pull/515) +- Added new `SqlConnectionOverrides` APIs to perform `SqlConnection.Open()` with fail fast option [#463](https://github.com/dotnet/SqlClient/pull/463) + +### Fixed +- Addressed MARS TDS Header errors by reverting changes to make `SqlDataReader.ReadAsync()` non-blocking [#547](https://github.com/dotnet/SqlClient/pull/547) +- Fixed driver behavior to not perform enlistment of pooled connection in aborted transaction [#543](https://github.com/dotnet/SqlClient/pull/543) +- Fixed wrong application domain selected when starting `SqlDependencyListener` [#410](https://github.com/dotnet/SqlClient/pull/410) +- Added missing refs for `RowCopied` property in `SqlBulkCopy` [#508](https://github.com/dotnet/SqlClient/pull/508) + +### Changes +- Improved performance by removing unwanted method calls in Event Source tracing [#506](https://github.com/dotnet/SqlClient/pull/506) +- Removed Diagnostic Source and Configuration Manager dependencies from .NET Standard implementation [#535](https://github.com/dotnet/SqlClient/pull/535) +- Removed redundant calls to `DbConnectionPoolKey.GetType()` [#512](https://github.com/dotnet/SqlClient/pull/512) + +### Breaking Changes +- Updated driver to perform decimal scale rounding to match SQL Server behavior [#470](https://github.com/dotnet/SqlClient/pull/470) +- Standardized App Context switch name that enables Managed SNI on Windows for .NET Core and .NET Standard (break only applies to 2.0 preview releases that introduced the switch) [#548](https://github.com/dotnet/SqlClient/pull/548) + + ## [Stable Release 1.1.2] - 2020-04-15 ### Added diff --git a/release-notes/2.0/2.0.0-preview3.md b/release-notes/2.0/2.0.0-preview3.md new file mode 100644 index 0000000000..4817b63302 --- /dev/null +++ b/release-notes/2.0/2.0.0-preview3.md @@ -0,0 +1,99 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.0.0-preview3.20122.2 released 1 May 2020 + +This update brings the below changes over the previous release: + +### Added +- Allow passing username with Active Directory Interactive Authentication in .NET Framework [#492](https://github.com/dotnet/SqlClient/pull/492) +- Allow large UDT buffers for .NET Framework [#456](https://github.com/dotnet/SqlClient/pull/456) +- Added "Transaction Id" and "Client Version" in Diagnostic Source traces [#515](https://github.com/dotnet/SqlClient/pull/515) +- Added new `SqlConnectionOverrides` APIs to perform `SqlConnection.Open()` with fail fast option [#463](https://github.com/dotnet/SqlClient/pull/463) + +### Fixed +- Addressed MARS TDS Header errors by reverting changes to make `SqlDataReader.ReadAsync()` non-blocking [#547](https://github.com/dotnet/SqlClient/pull/547) +- Fixed driver behavior to not perform enlistment of pooled connection in aborted transaction [#543](https://github.com/dotnet/SqlClient/pull/543) +- Fixed wrong application domain selected when starting `SqlDependencyListener` [#410](https://github.com/dotnet/SqlClient/pull/410) +- Added missing refs for `RowCopied` property in `SqlBulkCopy` [#508](https://github.com/dotnet/SqlClient/pull/508) + +### Changes +- Improved performance by removing unwanted method calls in Event Source tracing [#506](https://github.com/dotnet/SqlClient/pull/506) +- Removed Diagnostic Source and Configuration Manager dependencies from .NET Standard implementation [#535](https://github.com/dotnet/SqlClient/pull/535) +- Removed redundant calls to `DbConnectionPoolKey.GetType()` [#512](https://github.com/dotnet/SqlClient/pull/512) + +### Breaking Changes +- Updated driver to perform decimal scale rounding to match SQL Server behavior [#470](https://github.com/dotnet/SqlClient/pull/470) +- Standardized App Context switch that enables Managed SNI on Windows for .NET Core and .NET Standard (break only applies to 2.0 preview releases that introduced the switch) [#548](https://github.com/dotnet/SqlClient/pull/548) + + +### Enabling decimal truncation behavior conditionally +Starting with v2.0.0-preview3, the decimal data scale will be rounded by the driver by default as is done by SQL Server. +For backwards compatibility, you can set the [AppContext](https://docs.microsoft.com/en-us/dotnet/api/system.appcontext?view=netframework-4.8) switch "Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal" to "true". + +To set the switch at application startup, specify: + +```cs +AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal", true); +``` + +### Enabling Managed networking on Windows +This release introduces the renamed AppContext switch "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows" that enables the use of Managed SNI instead of native SNI on Windows for testing and debugging purposes. This switch will toggle the driver's behavior to use Managed SNI in .NET Core 2.1+ and .NET Standard 2.0+ projects on Windows. + +To set the switch at application startup, specify: + +```cs +AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", true); +``` + +## Target Platform Support + +- .NET Framework 4.6+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 1.1.0+ +- Microsoft.Identity.Client 3.0.8 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Core 2.1 + +- runtime.native.System.Data.SqlClient.sni 4.4.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.7.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Core 3.1 + +- runtime.native.System.Data.SqlClient.sni 4.4.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.7.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- runtime.native.System.Data.SqlClient.sni 4.4.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.0 +- System.Memory 4.5.3 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- Microsoft.Identity.Client 4.7.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 diff --git a/release-notes/2.0/2.0.md b/release-notes/2.0/2.0.md index 7b48fdeedf..d8d9a839a5 100644 --- a/release-notes/2.0/2.0.md +++ b/release-notes/2.0/2.0.md @@ -6,3 +6,4 @@ The following Microsoft.Data.SqlClient 2.0 preview releases have been shipped: | :-- | :-- | :--: | | 2020/01/21 | 2.0.0-preview1.20021.1 | [release notes](2.0.0-preview1.md) | | 2020/03/24 | 2.0.0-preview2.20084.1 | [release notes](2.0.0-preview2.md) | +| 2020/05/01 | 2.0.0-preview3.20122.2 | [release notes](2.0.0-preview3.md) | diff --git a/release-notes/2.0/README.md b/release-notes/2.0/README.md index 7b48fdeedf..d8d9a839a5 100644 --- a/release-notes/2.0/README.md +++ b/release-notes/2.0/README.md @@ -6,3 +6,4 @@ The following Microsoft.Data.SqlClient 2.0 preview releases have been shipped: | :-- | :-- | :--: | | 2020/01/21 | 2.0.0-preview1.20021.1 | [release notes](2.0.0-preview1.md) | | 2020/03/24 | 2.0.0-preview2.20084.1 | [release notes](2.0.0-preview2.md) | +| 2020/05/01 | 2.0.0-preview3.20122.2 | [release notes](2.0.0-preview3.md) |