Skip to content

Commit d535929

Browse files
paulmedynskiedwardnealDavid-Engel
authored
[6.1] Align SqlException Numbers across platforms (#3475)
* Reduce automated test crashes (#2968) * Converted Threads to long-running Tasks The key advantage is that exceptions propagate properly. If a thread throws an exception (as a result of a failed test assertion, or otherwise) then the test host crashes and must be restarted. * Corrected the instantiation of the cancellation task - missing state parameter. * Changes to TestSqlCommandCancel, eliminating timing-specific cancellation behaviour testing. This should also allow the test to run on both netcore and netfx. * Responding to code review. * Removed two unnecessary iterations from DatabaseHelper. * Added explanatory comments to ApiShould. * Switched to using Task.WaitAll rather than waiting for each Task in sequence. * Improve cancellation detection Cancellation can trigger one of several different errors, resulting in a flakier test. Also ensure that the query always takes more than 150ms, ensuring that a quick query execution doesn't cause the test to fail. Finally, make sure that we try to read everything from the SqlDataReader. * Correcting previous merge * Align SqlException Numbers across platforms (#3461) * - Align SqlException Numbers across platforms - Better capture error scenarios in TCP managed SNI. - Fix logging bug in SqlClientEventSource. - Change nativeError from uint to int * - Removed duplicate SniErrorDetails object and aligned field names. * - Added localized string for the new connection timed out exception. * - Fixed tests sensitive to OS newlines. --------- Co-authored-by: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> * Align SqlException Numbers across platforms - Manually fixing some error reporting that wasn't cherr-picked because it was part of larger changes. * Updated TdsParser.Windows.cs to use the SniErrorDetails struct from TdsParserStateObject. --------- Co-authored-by: Edward Neal <55035479+edwardneal@users.noreply.github.com> Co-authored-by: David Engel <davidengel@microsoft.com>
1 parent 0b8eb64 commit d535929

File tree

22 files changed

+370
-212
lines changed

22 files changed

+370
-212
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,18 @@ private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersi
2323
// No - Op
2424
}
2525

26-
private SNIErrorDetails GetSniErrorDetails()
26+
private TdsParserStateObject.SniErrorDetails GetSniErrorDetails()
2727
{
28-
SNIErrorDetails details;
2928
SniError sniError = SniProxy.Instance.GetLastError();
30-
details.sniErrorNumber = sniError.sniError;
31-
details.errorMessage = sniError.errorMessage;
32-
details.nativeError = sniError.nativeError;
33-
details.provider = (int)sniError.provider;
34-
details.lineNumber = sniError.lineNumber;
35-
details.function = sniError.function;
36-
details.exception = sniError.exception;
37-
38-
return details;
39-
}
4029

30+
return new(
31+
sniError.errorMessage,
32+
sniError.nativeError,
33+
sniError.sniError,
34+
(int)sniError.provider,
35+
sniError.lineNumber,
36+
sniError.function,
37+
sniError.exception);
38+
}
4139
}
4240
}

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,32 +63,31 @@ private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersi
6363
}
6464
}
6565

66-
private SNIErrorDetails GetSniErrorDetails()
66+
private TdsParserStateObject.SniErrorDetails GetSniErrorDetails()
6767
{
68-
SNIErrorDetails details = new SNIErrorDetails();
69-
7068
if (TdsParserStateObjectFactory.UseManagedSNI)
7169
{
7270
SniError sniError = SniProxy.Instance.GetLastError();
73-
details.sniErrorNumber = sniError.sniError;
74-
details.errorMessage = sniError.errorMessage;
75-
details.nativeError = sniError.nativeError;
76-
details.provider = (int)sniError.provider;
77-
details.lineNumber = sniError.lineNumber;
78-
details.function = sniError.function;
79-
details.exception = sniError.exception;
71+
return new(
72+
sniError.errorMessage,
73+
sniError.nativeError,
74+
sniError.sniError,
75+
(int)sniError.provider,
76+
sniError.lineNumber,
77+
sniError.function,
78+
sniError.exception);
8079
}
8180
else
8281
{
8382
SniNativeWrapper.SniGetLastError(out Interop.Windows.Sni.SniError sniError);
84-
details.sniErrorNumber = sniError.sniError;
85-
details.errorMessage = sniError.errorMessage;
86-
details.nativeError = sniError.nativeError;
87-
details.provider = (int)sniError.provider;
88-
details.lineNumber = sniError.lineNumber;
89-
details.function = sniError.function;
83+
return new(
84+
sniError.errorMessage,
85+
sniError.nativeError,
86+
sniError.sniError,
87+
(int)sniError.provider,
88+
sniError.lineNumber,
89+
sniError.function);
9090
}
91-
return details;
9291
}
9392

9493
} // tdsparser

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,12 +1447,12 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14471447
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.ProcessSNIError|ERR> SNIContext must not be None = {0}, _fMARS = {1}, TDS Parser State = {2}", stateObj.DebugOnlyCopyOfSniContext, _fMARS, _state);
14481448

14491449
#endif
1450-
SNIErrorDetails details = GetSniErrorDetails();
1450+
TdsParserStateObject.SniErrorDetails details = GetSniErrorDetails();
14511451

1452-
if (details.sniErrorNumber != 0)
1452+
if (details.SniErrorNumber != 0)
14531453
{
14541454
// handle special SNI error codes that are converted into exception which is not a SqlException.
1455-
switch (details.sniErrorNumber)
1455+
switch (details.SniErrorNumber)
14561456
{
14571457
case SniErrors.MultiSubnetFailoverWithMoreThan64IPs:
14581458
// Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
@@ -1473,8 +1473,8 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14731473
}
14741474
// PInvoke code automatically sets the length of the string for us
14751475
// So no need to look for \0
1476-
string errorMessage = details.errorMessage;
1477-
SqlClientEventSource.Log.TryAdvancedTraceEvent("< sc.TdsParser.ProcessSNIError |ERR|ADV > Error message Detail: {0}", details.errorMessage);
1476+
string errorMessage = details.ErrorMessage;
1477+
SqlClientEventSource.Log.TryAdvancedTraceEvent("< sc.TdsParser.ProcessSNIError |ERR|ADV > Error message Detail: {0}", details.ErrorMessage);
14781478

14791479
/* Format SNI errors and add Context Information
14801480
*
@@ -1491,23 +1491,23 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14911491

14921492
if (TdsParserStateObjectFactory.UseManagedSNI)
14931493
{
1494-
Debug.Assert(!string.IsNullOrEmpty(details.errorMessage) || details.sniErrorNumber != 0, "Empty error message received from SNI");
1495-
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}, SNI Error Number ={1}", details.errorMessage, details.sniErrorNumber);
1494+
Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage) || details.SniErrorNumber != 0, "Empty error message received from SNI");
1495+
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}, SNI Error Number ={1}", details.ErrorMessage, details.SniErrorNumber);
14961496
}
14971497
else
14981498
{
1499-
Debug.Assert(!string.IsNullOrEmpty(details.errorMessage), "Empty error message received from SNI");
1500-
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}", details.errorMessage);
1499+
Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage), "Empty error message received from SNI");
1500+
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}", details.ErrorMessage);
15011501
}
15021502

15031503
string sqlContextInfo = StringsHelper.GetResourceString(stateObj.SniContext.ToString());
1504-
string providerRid = string.Format("SNI_PN{0}", details.provider);
1504+
string providerRid = string.Format("SNI_PN{0}", details.Provider);
15051505
string providerName = StringsHelper.GetResourceString(providerRid);
15061506
Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'");
1507-
uint win32ErrorCode = details.nativeError;
1507+
int win32ErrorCode = details.NativeError;
15081508

15091509
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Native Error Code = {0}", win32ErrorCode);
1510-
if (details.sniErrorNumber == 0)
1510+
if (details.SniErrorNumber == 0)
15111511
{
15121512
// Provider error. The message from provider is preceeded with non-localizable info from SNI
15131513
// strip provider info from SNI
@@ -1541,33 +1541,33 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
15411541
if (TdsParserStateObjectFactory.UseManagedSNI)
15421542
{
15431543
// SNI error. Append additional error message info if available and hasn't been included.
1544-
string sniLookupMessage = SQL.GetSNIErrorMessage(details.sniErrorNumber);
1544+
string sniLookupMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber);
15451545
errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage))
15461546
? sniLookupMessage
15471547
: (sniLookupMessage + ": " + errorMessage);
15481548
}
15491549
else
15501550
{
15511551
// SNI error. Replace the entire message.
1552-
errorMessage = SQL.GetSNIErrorMessage(details.sniErrorNumber);
1552+
errorMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber);
15531553

15541554
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
1555-
if (details.sniErrorNumber == SniErrors.LocalDBErrorCode)
1555+
if (details.SniErrorNumber == SniErrors.LocalDBErrorCode)
15561556
{
1557-
errorMessage += LocalDbApi.GetLocalDbMessage((int)details.nativeError);
1557+
errorMessage += LocalDbApi.GetLocalDbMessage(details.NativeError);
15581558
win32ErrorCode = 0;
15591559
}
15601560
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage);
15611561
}
15621562
}
15631563
errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
1564-
sqlContextInfo, providerName, (int)details.sniErrorNumber, errorMessage);
1564+
sqlContextInfo, providerName, (int)details.SniErrorNumber, errorMessage);
15651565

15661566
SqlClientEventSource.Log.TryAdvancedTraceErrorEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Error Message. Native Error = {0}, Line Number ={1}, Function ={2}, Exception ={3}, Server = {4}",
1567-
(int)details.nativeError, (int)details.lineNumber, details.function, details.exception, _server);
1567+
details.NativeError, (int)details.LineNumber, details.Function, details.Exception, _server);
15681568

1569-
return new SqlError(infoNumber: (int)details.nativeError, errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server,
1570-
errorMessage, details.function, (int)details.lineNumber, win32ErrorCode: details.nativeError, details.exception);
1569+
return new SqlError(infoNumber: details.NativeError, errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server,
1570+
errorMessage, details.Function, (int)details.LineNumber, win32ErrorCode: details.NativeError, details.Exception);
15711571
}
15721572
}
15731573

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.netcore.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,6 @@ namespace Microsoft.Data.SqlClient
1010
{
1111
internal sealed partial class TdsParser
1212
{
13-
internal struct SNIErrorDetails
14-
{
15-
public string errorMessage;
16-
public uint nativeError;
17-
public uint sniErrorNumber;
18-
public int provider;
19-
public uint lineNumber;
20-
public string function;
21-
public Exception exception;
22-
}
23-
2413
internal static void FillGuidBytes(Guid guid, Span<byte> buffer) => guid.TryWriteBytes(buffer);
2514

2615
internal static void FillDoubleBytes(double value, Span<byte> buffer) => BinaryPrimitives.TryWriteInt64LittleEndian(buffer, BitConverter.DoubleToInt64Bits(value));

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,7 +1586,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
15861586
string providerRid = string.Format("SNI_PN{0}", (int)details.provider);
15871587
string providerName = StringsHelper.GetString(providerRid);
15881588
Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'");
1589-
uint win32ErrorCode = details.nativeError;
1589+
int win32ErrorCode = details.nativeError;
15901590

15911591
if (details.sniError == 0)
15921592
{
@@ -1624,14 +1624,14 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
16241624
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
16251625
if (details.sniError == SniErrors.LocalDBErrorCode)
16261626
{
1627-
errorMessage += LocalDbApi.GetLocalDbMessage((int)details.nativeError);
1627+
errorMessage += LocalDbApi.GetLocalDbMessage(details.nativeError);
16281628
win32ErrorCode = 0;
16291629
}
16301630
}
16311631
errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
16321632
sqlContextInfo, providerName, (int)details.sniError, errorMessage);
16331633

1634-
return new SqlError((int)details.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
1634+
return new SqlError(details.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
16351635
_server, errorMessage, details.function, (int)details.lineNumber, win32ErrorCode);
16361636
}
16371637

src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniError.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal struct SniError
1212
internal Provider provider;
1313
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 261)]
1414
internal string errorMessage;
15-
internal uint nativeError;
15+
internal int nativeError;
1616
internal uint sniError;
1717
[MarshalAs(UnmanagedType.LPWStr)]
1818
internal string fileName;

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniCommon.netcore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ internal static IPAddress[] GetDnsIpAddresses(string serverName)
189189
/// <param name="sniError">SNI error code</param>
190190
/// <param name="errorMessage">Error message</param>
191191
/// <returns></returns>
192-
internal static uint ReportSNIError(SniProviders provider, uint nativeError, uint sniError, string errorMessage)
192+
internal static uint ReportSNIError(SniProviders provider, int nativeError, uint sniError, string errorMessage)
193193
{
194194
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniCommon), EventType.ERR, "Provider = {0}, native Error = {1}, SNI Error = {2}, Error Message = {3}", args0: provider, args1: nativeError, args2: sniError, args3: errorMessage);
195195
return ReportSNIError(new SniError(provider, nativeError, sniError, errorMessage));
@@ -203,7 +203,7 @@ internal static uint ReportSNIError(SniProviders provider, uint nativeError, uin
203203
/// <param name="sniException">SNI Exception</param>
204204
/// <param name="nativeErrorCode">Native SNI error code</param>
205205
/// <returns></returns>
206-
internal static uint ReportSNIError(SniProviders provider, uint sniError, Exception sniException, uint nativeErrorCode = 0)
206+
internal static uint ReportSNIError(SniProviders provider, uint sniError, Exception sniException, int nativeErrorCode = 0)
207207
{
208208
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniCommon), EventType.ERR, "Provider = {0}, SNI Error = {1}, Exception = {2}", args0: provider, args1: sniError, args2: sniException?.Message);
209209
return ReportSNIError(new SniError(provider, sniError, sniException, nativeErrorCode));

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniError.netcore.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#if NET
66

77
using System;
8+
using System.ComponentModel;
9+
using System.Net.Sockets;
810

911
namespace Microsoft.Data.SqlClient.ManagedSni
1012
{
@@ -14,17 +16,18 @@ namespace Microsoft.Data.SqlClient.ManagedSni
1416
internal class SniError
1517
{
1618
// Error numbers from native SNI implementation
17-
internal const uint CertificateValidationErrorCode = 2148074277;
19+
// This is signed int representation of the error code 0x80090325
20+
internal const int CertificateValidationErrorCode = -2146893019;
1821

1922
public readonly SniProviders provider;
2023
public readonly string errorMessage;
21-
public readonly uint nativeError;
24+
public readonly int nativeError;
2225
public readonly uint sniError;
2326
public readonly string function;
2427
public readonly uint lineNumber;
2528
public readonly Exception exception;
2629

27-
public SniError(SniProviders provider, uint nativeError, uint sniErrorCode, string errorMessage)
30+
public SniError(SniProviders provider, int nativeError, uint sniErrorCode, string errorMessage)
2831
{
2932
lineNumber = 0;
3033
function = string.Empty;
@@ -35,12 +38,25 @@ public SniError(SniProviders provider, uint nativeError, uint sniErrorCode, stri
3538
exception = null;
3639
}
3740

38-
public SniError(SniProviders provider, uint sniErrorCode, Exception sniException, uint nativeErrorCode = 0)
41+
public SniError(SniProviders provider, uint sniErrorCode, Exception sniException, int nativeErrorCode = 0)
3942
{
4043
lineNumber = 0;
4144
function = string.Empty;
4245
this.provider = provider;
4346
nativeError = nativeErrorCode;
47+
if (nativeErrorCode == 0)
48+
{
49+
if (sniException is SocketException socketException)
50+
{
51+
// SocketErrorCode values are cross-plat consistent in .NET (matching native Windows error codes)
52+
// underlying type of SocketErrorCode is int
53+
nativeError = (int)socketException.SocketErrorCode;
54+
}
55+
else if (sniException is Win32Exception win32Exception)
56+
{
57+
nativeError = win32Exception.NativeErrorCode; // Replicates native SNI behavior
58+
}
59+
}
4460
sniError = sniErrorCode;
4561
errorMessage = string.Empty;
4662
exception = sniException;

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ManagedSni/SniNpHandle.netcore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public override uint Receive(out SniPacket packet, int timeout)
203203
packet = null;
204204
var e = new Win32Exception();
205205
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SniNpHandle), EventType.ERR, "Connection Id {0}, Packet length found 0, Win32 exception raised: {1}", args0: _connectionId, args1: e?.Message);
206-
return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message);
206+
return ReportErrorAndReleasePacket(errorPacket, e.NativeErrorCode, 0, e.Message);
207207
}
208208
}
209209
catch (ObjectDisposedException ode)
@@ -413,7 +413,7 @@ private uint ReportErrorAndReleasePacket(SniPacket packet, Exception sniExceptio
413413
return SniCommon.ReportSNIError(SniProviders.NP_PROV, SniCommon.InternalExceptionError, sniException);
414414
}
415415

416-
private uint ReportErrorAndReleasePacket(SniPacket packet, uint nativeError, uint sniError, string errorMessage)
416+
private uint ReportErrorAndReleasePacket(SniPacket packet, int nativeError, uint sniError, string errorMessage)
417417
{
418418
if (packet != null)
419419
{

0 commit comments

Comments
 (0)