internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
{
#if DEBUG
// 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
#if MANAGED_SNI
SNIError sniError = SNIProxy.Singleton.GetLastError();
#else
SNINativeMethodWrapper.SNI_Error sniError;
SNINativeMethodWrapper.SNIGetLastError(out sniError);
#endif // MANAGED_SNI
if (sniError.sniError != 0)
{
// handle special SNI error codes that are converted into exception which is not a SqlException.
switch (sniError.sniError)
{
case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithMoreThan64IPs:
// Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
throw SQL.MultiSubnetFailoverWithMoreThan64IPs();
case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithInstanceSpecified:
// Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported.
throw SQL.MultiSubnetFailoverWithInstanceSpecified();
case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithNonTcpProtocol:
// Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
throw SQL.MultiSubnetFailoverWithNonTcpProtocol();
// continue building SqlError instance
}
}
// PInvoke code automatically sets the length of the string for us
// So no need to look for \0
string errorMessage = sniError.errorMessage;
// Format SNI errors and add Context Information
//
// General syntax is:
// <sqlclient message>
// (provider:<SNIx provider>, error: <SNIx error code> - <SNIx error message>)
//
// errorMessage | sniError |
// -------------------------------------------
// ==null | x | must never happen
// !=null | != 0 | retrieve corresponding errorMessage from resources
// !=null | == 0 | replace text left of errorMessage
//
#if MANAGED_SNI
Debug.Assert(!string.IsNullOrEmpty(errorMessage) || sniError.sniError != 0, "Empty error message received from SNI");
#else
Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Empty error message received from SNI");
#endif
string sqlContextInfo = SR.GetResourceString(Enum.GetName(typeof(SniContext), stateObj.SniContext), Enum.GetName(typeof(SniContext), stateObj.SniContext));
string providerRid = String.Format((IFormatProvider)null, "SNI_PN{0}", (int)sniError.provider);
string providerName = SR.GetResourceString(providerRid, providerRid);
Debug.Assert(!string.IsNullOrEmpty(providerName), String.Format((IFormatProvider)null, "invalid providerResourceId '{0}'", providerRid));
uint win32ErrorCode = sniError.nativeError;
if (sniError.sniError == 0)
{
// Provider error. The message from provider is preceded with non-localizable info from SNI
// strip provider info from SNI
//
int iColon = errorMessage.IndexOf(':');
Debug.Assert(0 <= iColon, "':' character missing in sni errorMessage");
Debug.Assert(errorMessage.Length > iColon + 1 && errorMessage[iColon + 1] == ' ', "Expecting a space after the ':' character");
// extract the message excluding the colon and trailing cr/lf chars
if (0 <= iColon)
{
int len = errorMessage.Length;
len -= Environment.NewLine.Length; // exclude newline sequence
iColon += 2; // skip over ": " sequence
len -= iColon;
/*
The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
If the message is received on a Win9x OS, the error message will not contain MESSAGE TEXT
If we get a error message with no message text, just return the entire message otherwise
return just the message text.
*/
if (len > 0)
{
errorMessage = errorMessage.Substring(iColon, len);
}
}
}
else
{
#if MANAGED_SNI
// SNI error. Append additional error message info if available.
//
string sniLookupMessage = SQL.GetSNIErrorMessage((int)sniError.sniError);
errorMessage = (sniError.errorMessage != string.Empty) ?
(sniLookupMessage + ": " + sniError.errorMessage) :
sniLookupMessage;
#else
// SNI error. Replace the entire message.
//
errorMessage = SQL.GetSNIErrorMessage((int)sniError.sniError);
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
if (sniError.sniError == (int)SNINativeMethodWrapper.SniSpecialErrors.LocalDBErrorCode)
{
errorMessage += LocalDBAPI.GetLocalDBMessage((int)sniError.nativeError);
win32ErrorCode = 0;
}
#endif // MANAGED_SNI
}
errorMessage = String.Format((IFormatProvider)null, "{0} (provider: {1}, error: {2} - {3})",
sqlContextInfo, providerName, (int)sniError.sniError, errorMessage);
#if MANAGED_SNI
return new SqlError((int)sniError.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
_server, errorMessage, sniError.function, (int)sniError.lineNumber, win32ErrorCode, sniError.exception);
#else
return new SqlError((int)sniError.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
_server, errorMessage, sniError.function, (int)sniError.lineNumber, win32ErrorCode);
#endif
}