Skip to content

Merge | TdsParser.GetSniErrorDetails #3434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,5 @@ private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersi
{
// No - Op
}

private SNIErrorDetails GetSniErrorDetails()
{
SNIErrorDetails details;
SniError sniError = SniProxy.Instance.GetLastError();
details.sniErrorNumber = sniError.sniError;
details.errorMessage = sniError.errorMessage;
details.nativeError = sniError.nativeError;
details.provider = (int)sniError.provider;
details.lineNumber = sniError.lineNumber;
details.function = sniError.function;
details.exception = sniError.exception;

return details;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

using System;
using System.Diagnostics;
using Interop.Windows.Sni;
using Microsoft.Data.SqlClient.ManagedSni;
using SniError = Microsoft.Data.SqlClient.ManagedSni.SniError;

namespace Microsoft.Data.SqlClient
{
Expand Down Expand Up @@ -62,34 +59,5 @@ private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersi
ThrowExceptionAndWarning(_physicalStateObj);
}
}

private SNIErrorDetails GetSniErrorDetails()
{
SNIErrorDetails details = new SNIErrorDetails();

if (TdsParserStateObjectFactory.UseManagedSNI)
{
SniError sniError = SniProxy.Instance.GetLastError();
details.sniErrorNumber = sniError.sniError;
details.errorMessage = sniError.errorMessage;
details.nativeError = sniError.nativeError;
details.provider = (int)sniError.provider;
details.lineNumber = sniError.lineNumber;
details.function = sniError.function;
details.exception = sniError.exception;
}
else
{
SniNativeWrapper.SniGetLastError(out Interop.Windows.Sni.SniError sniError);
details.sniErrorNumber = sniError.sniError;
details.errorMessage = sniError.errorMessage;
details.nativeError = sniError.nativeError;
details.provider = (int)sniError.provider;
details.lineNumber = sniError.lineNumber;
details.function = sniError.function;
}
return details;
}

} // tdsparser
}//namespace
Original file line number Diff line number Diff line change
Expand Up @@ -1450,12 +1450,12 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.ProcessSNIError|ERR> SNIContext must not be None = {0}, _fMARS = {1}, TDS Parser State = {2}", stateObj.DebugOnlyCopyOfSniContext, _fMARS, _state);

#endif
SNIErrorDetails details = GetSniErrorDetails();
TdsParserStateObject.SniErrorDetails details = stateObj.GetErrorDetails();

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

/* Format SNI errors and add Context Information
*
Expand All @@ -1494,25 +1494,25 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)

if (TdsParserStateObjectFactory.UseManagedSNI)
{
Debug.Assert(!string.IsNullOrEmpty(details.errorMessage) || details.sniErrorNumber != 0, "Empty error message received from SNI");
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);
Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage) || details.SniErrorNumber != 0, "Empty error message received from SNI");
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);
}
else
{
Debug.Assert(!string.IsNullOrEmpty(details.errorMessage), "Empty error message received from SNI");
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}", details.errorMessage);
Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage), "Empty error message received from SNI");
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}", details.ErrorMessage);
}

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

SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Native Error Code = {0}", win32ErrorCode);
if (details.sniErrorNumber == 0)
if (details.SniErrorNumber == 0)
{
// Provider error. The message from provider is preceeded with non-localizable info from SNI
// Provider error. The message from provider is preceded with non-localizable info from SNI
// strip provider info from SNI
//
int iColon = errorMessage.IndexOf(':');
Expand Down Expand Up @@ -1544,33 +1544,33 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
if (TdsParserStateObjectFactory.UseManagedSNI)
{
// SNI error. Append additional error message info if available and hasn't been included.
string sniLookupMessage = SQL.GetSNIErrorMessage(details.sniErrorNumber);
string sniLookupMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber);
errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage))
? sniLookupMessage
: (sniLookupMessage + ": " + errorMessage);
}
else
{
// SNI error. Replace the entire message.
errorMessage = SQL.GetSNIErrorMessage(details.sniErrorNumber);
errorMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber);

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

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,15 @@ internal override uint WaitForSSLHandShakeToComplete(out int protocolVersion)
return 0;
}

internal override SniErrorDetails GetErrorDetails()
{
SniError sniError = SniProxy.Instance.GetLastError();

return new SniErrorDetails(sniError.errorMessage, sniError.nativeError, sniError.sniError,
(int)sniError.provider, sniError.lineNumber, sniError.function,
sniError.exception);
}

private SniHandle GetSessionSNIHandleHandleOrThrow()
{
SniHandle? sessionHandle = _sessionHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,14 @@ internal override uint WaitForSSLHandShakeToComplete(out int protocolVersion)
return returnValue;
}

internal override SniErrorDetails GetErrorDetails()
{
SniNativeWrapper.SniGetLastError(out SniError sniError);

return new SniErrorDetails(sniError.errorMessage, sniError.nativeError, sniError.sniError,
(int)sniError.provider, sniError.lineNumber, sniError.function);
}

internal override void DisposePacketCache()
{
lock (_writePacketLockObject)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1542,14 +1542,13 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
// There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error
Debug.Assert(SniContext.Undefined != stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None");
#endif
SniError details = new SniError();
SniNativeWrapper.SniGetLastError(out details);
TdsParserStateObject.SniErrorDetails details = stateObj.GetErrorDetails();

if (details.sniError != 0)
if (details.SniErrorNumber != 0)
{

// handle special SNI error codes that are converted into exception which is not a SqlException.
switch (details.sniError)
switch (details.SniErrorNumber)
{
case SniErrors.MultiSubnetFailoverWithMoreThan64IPs:
// Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
Expand All @@ -1569,7 +1568,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)

// error.errorMessage is null terminated with garbage beyond that, since fixed length
string errorMessage;
errorMessage = string.IsNullOrEmpty(details.errorMessage) ? string.Empty : details.errorMessage;
errorMessage = string.IsNullOrEmpty(details.ErrorMessage) ? string.Empty : details.ErrorMessage;
/* Format SNI errors and add Context Information
*
* General syntax is:
Expand All @@ -1586,12 +1585,12 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Empty error message received from SNI");

string sqlContextInfo = StringsHelper.GetString(Enum.GetName(typeof(SniContext), stateObj.SniContext));
string providerRid = string.Format("SNI_PN{0}", (int)details.provider);
string providerRid = string.Format("SNI_PN{0}", (int)details.Provider);
string providerName = StringsHelper.GetString(providerRid);
Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'");
uint win32ErrorCode = details.nativeError;
uint win32ErrorCode = details.NativeError;

if (details.sniError == 0)
if (details.SniErrorNumber == 0)
{
// Provider error. The message from provider is preceeded with non-localizable info from SNI
// strip provider info from SNI
Expand Down Expand Up @@ -1622,20 +1621,20 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
else
{
// SNI error. Replace the entire message.
errorMessage = SQL.GetSNIErrorMessage(details.sniError);
errorMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber);

// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
if (details.sniError == SniErrors.LocalDBErrorCode)
if (details.SniErrorNumber == SniErrors.LocalDBErrorCode)
{
errorMessage += LocalDbApi.GetLocalDbMessage((int)details.nativeError);
errorMessage += LocalDbApi.GetLocalDbMessage((int)details.NativeError);
win32ErrorCode = 0;
}
}
errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
sqlContextInfo, providerName, (int)details.sniError, errorMessage);
sqlContextInfo, providerName, (int)details.SniErrorNumber, errorMessage);

return new SqlError((int)details.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
_server, errorMessage, details.function, (int)details.lineNumber, win32ErrorCode);
return new SqlError((int)details.NativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
_server, errorMessage, details.Function, (int)details.LineNumber, win32ErrorCode);
}

internal void CheckResetConnection(TdsParserStateObject stateObj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ internal override uint EnableMars(ref uint info)
internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize)
=> SniNativeWrapper.SniSetInfo(Handle, QueryType.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize);

internal override SniErrorDetails GetErrorDetails()
{
SniNativeWrapper.SniGetLastError(out SniError sniError);

return new SniErrorDetails(sniError.errorMessage, sniError.nativeError, sniError.sniError, (int)sniError.provider, sniError.lineNumber, sniError.function);
}

internal override void DisposePacketCache()
{
lock (_writePacketLockObject)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ internal enum SnapshottedStateFlags : byte
AttentionReceived = 1 << 5 // NOTE: Received is not volatile as it is only ever accessed\modified by TryRun its callees (i.e. single threaded access)
}

internal readonly struct SniErrorDetails
{
public readonly string ErrorMessage;
public readonly uint NativeError;
public readonly uint SniErrorNumber;
public readonly int Provider;
public readonly uint LineNumber;
public readonly string Function;
public readonly Exception Exception;

internal SniErrorDetails(string errorMessage, uint nativeError, uint sniErrorNumber, int provider, uint lineNumber, string function, Exception exception = null)
{
ErrorMessage = errorMessage;
NativeError = nativeError;
SniErrorNumber = sniErrorNumber;
Provider = provider;
LineNumber = lineNumber;
Function = function;
Exception = exception;
}
}

private sealed class TimeoutState
{
public const int Stopped = 0;
Expand Down Expand Up @@ -521,6 +543,8 @@ internal long TimeoutTime

internal abstract void DisposePacketCache();

internal abstract SniErrorDetails GetErrorDetails();

internal int GetTimeoutRemaining()
{
int remaining;
Expand Down
Loading