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 b5e1dc903c..1db7c3ab1d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -489,7 +489,9 @@ - + + Microsoft\Data\SqlClient\SqlStatistics.cs + 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 2ceeed013b..e36768383b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -477,7 +477,9 @@ - + + Microsoft\Data\SqlClient\SqlStatistics.cs + 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 c0dd91e2d7..1116e94e8f 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 @@ -2869,11 +2869,11 @@ public IDictionary RetrieveStatistics() if (null != Statistics) { UpdateStatistics(); - return Statistics.GetHashtable(); + return Statistics.GetDictionary(); } else { - return new SqlStatistics().GetHashtable(); + return new SqlStatistics().GetDictionary(); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs deleted file mode 100644 index 7c72711cd2..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs +++ /dev/null @@ -1,221 +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.Collections; -using System.Diagnostics; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - internal sealed class SqlStatistics - { - - static internal SqlStatistics StartTimer(SqlStatistics statistics) - { - if ((null != statistics) && !statistics.RequestExecutionTimer()) - { - // we're re-entrant -- don't bother. - statistics = null; - } - return statistics; - } - - static internal void StopTimer(SqlStatistics statistics) - { - if (null != statistics) - { - statistics.ReleaseAndUpdateExecutionTimer(); - } - } - - // internal values that are not exposed through properties - internal long _closeTimestamp; - internal long _openTimestamp; - internal long _startExecutionTimestamp; - internal long _startFetchTimestamp; - internal long _startNetworkServerTimestamp; - - // internal values that are exposed through properties - internal long _buffersReceived; - internal long _buffersSent; - internal long _bytesReceived; - internal long _bytesSent; - internal long _connectionTime; - internal long _cursorOpens; - internal long _executionTime; - internal long _iduCount; - internal long _iduRows; - internal long _networkServerTime; - internal long _preparedExecs; - internal long _prepares; - internal long _selectCount; - internal long _selectRows; - internal long _serverRoundtrips; - internal long _sumResultSets; - internal long _transactions; - internal long _unpreparedExecs; - - // these flags are required if statistics is turned on/off in the middle of command execution - private bool _waitForDoneAfterRow; - private bool _waitForReply; - - - internal bool WaitForDoneAfterRow - { - get - { - return _waitForDoneAfterRow; - } - set - { - _waitForDoneAfterRow = value; - } - } - - internal bool WaitForReply - { - get - { - return _waitForReply; - } - } - - internal SqlStatistics() - { - } - - internal void ContinueOnNewConnection() - { - _startExecutionTimestamp = 0; - _startFetchTimestamp = 0; - _waitForDoneAfterRow = false; - _waitForReply = false; - } - - internal IDictionary GetHashtable() - { - Hashtable ht = new Hashtable(18); - - ht.Add("BuffersReceived", _buffersReceived); - ht.Add("BuffersSent", _buffersSent); - ht.Add("BytesReceived", _bytesReceived); - ht.Add("BytesSent", _bytesSent); - ht.Add("CursorOpens", _cursorOpens); - ht.Add("IduCount", _iduCount); - ht.Add("IduRows", _iduRows); - ht.Add("PreparedExecs", _preparedExecs); - ht.Add("Prepares", _prepares); - ht.Add("SelectCount", _selectCount); - ht.Add("SelectRows", _selectRows); - ht.Add("ServerRoundtrips", _serverRoundtrips); - ht.Add("SumResultSets", _sumResultSets); - ht.Add("Transactions", _transactions); - ht.Add("UnpreparedExecs", _unpreparedExecs); - - ht.Add("ConnectionTime", ADP.TimerToMilliseconds(_connectionTime)); - ht.Add("ExecutionTime", ADP.TimerToMilliseconds(_executionTime)); - ht.Add("NetworkServerTime", ADP.TimerToMilliseconds(_networkServerTime)); - - return ht; - } - - internal bool RequestExecutionTimer() - { - if (_startExecutionTimestamp == 0) - { - _startExecutionTimestamp = ADP.TimerCurrent(); - return true; - } - return false; - } - - internal void RequestNetworkServerTimer() - { - Debug.Assert(_startExecutionTimestamp != 0, "No network time expected outside execution period"); - if (_startNetworkServerTimestamp == 0) - { - _startNetworkServerTimestamp = ADP.TimerCurrent(); - } - _waitForReply = true; - } - - internal void ReleaseAndUpdateExecutionTimer() - { - if (_startExecutionTimestamp > 0) - { - _executionTime += (ADP.TimerCurrent() - _startExecutionTimestamp); - _startExecutionTimestamp = 0; - } - } - - internal void ReleaseAndUpdateNetworkServerTimer() - { - if (_waitForReply && _startNetworkServerTimestamp > 0) - { - _networkServerTime += (ADP.TimerCurrent() - _startNetworkServerTimestamp); - _startNetworkServerTimestamp = 0; - } - _waitForReply = false; - } - - internal void Reset() - { - _buffersReceived = 0; - _buffersSent = 0; - _bytesReceived = 0; - _bytesSent = 0; - _connectionTime = 0; - _cursorOpens = 0; - _executionTime = 0; - _iduCount = 0; - _iduRows = 0; - _networkServerTime = 0; - _preparedExecs = 0; - _prepares = 0; - _selectCount = 0; - _selectRows = 0; - _serverRoundtrips = 0; - _sumResultSets = 0; - _transactions = 0; - _unpreparedExecs = 0; - _waitForDoneAfterRow = false; - _waitForReply = false; - _startExecutionTimestamp = 0; - _startNetworkServerTimestamp = 0; - } - - internal void SafeAdd(ref long value, long summand) - { - if (long.MaxValue - value > summand) - { - value += summand; - } - else - { - value = long.MaxValue; - } - } - - internal long SafeIncrement(ref long value) - { - if (value < long.MaxValue) - value++; - return value; - } - - internal void UpdateStatistics() - { - // update connection time - if (_closeTimestamp >= _openTimestamp && long.MaxValue > _closeTimestamp - _openTimestamp) - { - _connectionTime = _closeTimestamp - _openTimestamp; - } - else - { - _connectionTime = long.MaxValue; - } - } - } -} - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index af7390b2a9..d1cdf1157f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -1824,6 +1824,42 @@ internal static string ADP_VersionDoesNotSupportDataType { } } + /// + /// Looks up a localized string similar to Destination array is not long enough to copy all the items in the collection. Check array index and length.. + /// + internal static string Arg_ArrayPlusOffTooSmall { + get { + return ResourceManager.GetString("Arg_ArrayPlusOffTooSmall", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only single dimensional arrays are supported for the requested action.. + /// + internal static string Arg_RankMultiDimNotSupported { + get { + return ResourceManager.GetString("Arg_RankMultiDimNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot remove the specified item because it was not found in the specified Collection.. + /// + internal static string Arg_RemoveArgNotFound { + get { + return ResourceManager.GetString("Arg_RemoveArgNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Non-negative number required.. + /// + internal static string ArgumentOutOfRange_NeedNonNegNum { + get { + return ResourceManager.GetString("ArgumentOutOfRange_NeedNonNegNum", resourceCulture); + } + } + /// /// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index d1b907c6e9..bd8efd2529 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4617,4 +4617,16 @@ '{0}' is not less than '{1}'; '{2}' cannot be greater than '{3}'. - + + Destination array is not long enough to copy all the items in the collection. Check array index and length. + + + Only single dimensional arrays are supported for the requested action. + + + Cannot remove the specified item because it was not found in the specified Collection. + + + Non-negative number required. + + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs similarity index 91% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs index 4aeeb28322..7a5e37198b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs @@ -30,6 +30,11 @@ internal static void StopTimer(SqlStatistics statistics) } } + internal static ValueSqlStatisticsScope TimedScope(SqlStatistics statistics) + { + return new ValueSqlStatisticsScope(statistics); + } + // internal values that are not exposed through properties internal long _closeTimestamp; internal long _openTimestamp; @@ -61,30 +66,17 @@ internal static void StopTimer(SqlStatistics statistics) private bool _waitForDoneAfterRow; private bool _waitForReply; - - internal bool WaitForDoneAfterRow + internal SqlStatistics() { - get - { - return _waitForDoneAfterRow; - } - set - { - _waitForDoneAfterRow = value; - } } - internal bool WaitForReply + internal bool WaitForDoneAfterRow { - get - { - return _waitForReply; - } + get => _waitForDoneAfterRow; + set => _waitForDoneAfterRow = value; } - internal SqlStatistics() - { - } + internal bool WaitForReply => _waitForReply; internal void ContinueOnNewConnection() { @@ -203,7 +195,9 @@ internal void SafeAdd(ref long value, long summand) internal long SafeIncrement(ref long value) { if (value < long.MaxValue) + { value++; + } return value; } @@ -289,13 +283,21 @@ private void CopyValues(Array array, int arrayIndex) private void ValidateCopyToArguments(Array array, int arrayIndex) { if (array == null) + { throw new ArgumentNullException(nameof(array)); + } if (array.Rank != 1) + { throw new ArgumentException(Strings.Arg_RankMultiDimNotSupported); + } if (arrayIndex < 0) + { throw new ArgumentOutOfRangeException(nameof(arrayIndex), Strings.ArgumentOutOfRange_NeedNonNegNum); + } if (array.Length - arrayIndex < Count) + { throw new ArgumentException(Strings.Arg_ArrayPlusOffTooSmall); + } } private sealed class Collection : ICollection @@ -335,4 +337,16 @@ void ICollection.CopyTo(Array array, int arrayIndex) } } } + + // This is a ref struct to prevent it being included in async closures accidentally. + // Async functions should manage the timer directly using the Start and Stop method + // in their invoke and completion functions + internal readonly ref struct ValueSqlStatisticsScope // : IDisposable // ref structs cannot implement interfaces but the compiler will use pattern matching to allow use of using on them + { + private readonly SqlStatistics _statistics; + + public ValueSqlStatisticsScope(SqlStatistics statistics) => _statistics = SqlStatistics.StartTimer(statistics); + + public void Dispose() => SqlStatistics.StopTimer(_statistics); + } }