@@ -1450,12 +1450,12 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14501450 SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.ProcessSNIError|ERR> SNIContext must not be None = {0}, _fMARS = {1}, TDS Parser State = {2}", stateObj.DebugOnlyCopyOfSniContext, _fMARS, _state);
14511451
14521452#endif
1453- SNIErrorDetails details = GetSniErrorDetails ();
1453+ TdsParserStateObject.SniErrorDetails details = stateObj.GetErrorDetails ();
14541454
1455- if (details.sniErrorNumber != 0)
1455+ if (details.SniErrorNumber != 0)
14561456 {
14571457 // handle special SNI error codes that are converted into exception which is not a SqlException.
1458- switch (details.sniErrorNumber )
1458+ switch (details.SniErrorNumber )
14591459 {
14601460 case SniErrors.MultiSubnetFailoverWithMoreThan64IPs:
14611461 // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
@@ -1476,8 +1476,8 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14761476 }
14771477 // PInvoke code automatically sets the length of the string for us
14781478 // So no need to look for \0
1479- string errorMessage = details.errorMessage ;
1480- SqlClientEventSource.Log.TryAdvancedTraceEvent("< sc.TdsParser.ProcessSNIError |ERR|ADV > Error message Detail: {0}", details.errorMessage );
1479+ string errorMessage = details.ErrorMessage ;
1480+ SqlClientEventSource.Log.TryAdvancedTraceEvent("< sc.TdsParser.ProcessSNIError |ERR|ADV > Error message Detail: {0}", details.ErrorMessage );
14811481
14821482 /* Format SNI errors and add Context Information
14831483 *
@@ -1494,25 +1494,25 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14941494
14951495 if (TdsParserStateObjectFactory.UseManagedSNI)
14961496 {
1497- Debug.Assert(!string.IsNullOrEmpty(details.errorMessage ) || details.sniErrorNumber != 0, "Empty error message received from SNI");
1498- 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 );
1497+ Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage ) || details.SniErrorNumber != 0, "Empty error message received from SNI");
1498+ 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 );
14991499 }
15001500 else
15011501 {
1502- Debug.Assert(!string.IsNullOrEmpty(details.errorMessage ), "Empty error message received from SNI");
1503- SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}", details.errorMessage );
1502+ Debug.Assert(!string.IsNullOrEmpty(details.ErrorMessage ), "Empty error message received from SNI");
1503+ SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Empty error message received from SNI. Error Message = {0}", details.ErrorMessage );
15041504 }
15051505
15061506 string sqlContextInfo = StringsHelper.GetResourceString(stateObj.SniContext.ToString());
1507- string providerRid = string.Format("SNI_PN{0}", details.provider );
1507+ string providerRid = string.Format("SNI_PN{0}", details.Provider );
15081508 string providerName = StringsHelper.GetResourceString(providerRid);
15091509 Debug.Assert(!string.IsNullOrEmpty(providerName), $"invalid providerResourceId '{providerRid}'");
1510- uint win32ErrorCode = details.nativeError ;
1510+ uint win32ErrorCode = details.NativeError ;
15111511
15121512 SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Native Error Code = {0}", win32ErrorCode);
1513- if (details.sniErrorNumber == 0)
1513+ if (details.SniErrorNumber == 0)
15141514 {
1515- // Provider error. The message from provider is preceeded with non-localizable info from SNI
1515+ // Provider error. The message from provider is preceded with non-localizable info from SNI
15161516 // strip provider info from SNI
15171517 //
15181518 int iColon = errorMessage.IndexOf(':');
@@ -1544,33 +1544,33 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
15441544 if (TdsParserStateObjectFactory.UseManagedSNI)
15451545 {
15461546 // SNI error. Append additional error message info if available and hasn't been included.
1547- string sniLookupMessage = SQL.GetSNIErrorMessage(details.sniErrorNumber );
1547+ string sniLookupMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber );
15481548 errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage))
15491549 ? sniLookupMessage
15501550 : (sniLookupMessage + ": " + errorMessage);
15511551 }
15521552 else
15531553 {
15541554 // SNI error. Replace the entire message.
1555- errorMessage = SQL.GetSNIErrorMessage(details.sniErrorNumber );
1555+ errorMessage = SQL.GetSNIErrorMessage(details.SniErrorNumber );
15561556
15571557 // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
1558- if (details.sniErrorNumber == SniErrors.LocalDBErrorCode)
1558+ if (details.SniErrorNumber == SniErrors.LocalDBErrorCode)
15591559 {
1560- errorMessage += LocalDbApi.GetLocalDbMessage((int)details.nativeError );
1560+ errorMessage += LocalDbApi.GetLocalDbMessage((int)details.NativeError );
15611561 win32ErrorCode = 0;
15621562 }
15631563 SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage);
15641564 }
15651565 }
15661566 errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
1567- sqlContextInfo, providerName, (int)details.sniErrorNumber , errorMessage);
1567+ sqlContextInfo, providerName, (int)details.SniErrorNumber , errorMessage);
15681568
15691569 SqlClientEventSource.Log.TryAdvancedTraceErrorEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Error Message. Native Error = {0}, Line Number ={1}, Function ={2}, Exception ={3}, Server = {4}",
1570- (int)details.nativeError , (int)details.lineNumber , details.function , details.exception , _server);
1570+ (int)details.NativeError , (int)details.LineNumber , details.Function , details.Exception , _server);
15711571
1572- return new SqlError(infoNumber: (int)details.nativeError , errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server,
1573- errorMessage, details.function , (int)details.lineNumber , win32ErrorCode: details.nativeError , details.exception );
1572+ return new SqlError(infoNumber: (int)details.NativeError , errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server,
1573+ errorMessage, details.Function , (int)details.LineNumber , win32ErrorCode: details.NativeError , details.Exception );
15741574 }
15751575 }
15761576
@@ -8579,6 +8579,49 @@ internal int WriteVectorSupportFeatureRequest(bool write)
85798579 return len;
85808580 }
85818581
8582+ /// <summary>
8583+ /// Writes the User Agent feature request to the physical state object.
8584+ /// The request includes the feature ID, feature data length, version number and encoded JSON payload.
8585+ /// </summary>
8586+ /// <param name="userAgentJsonPayload"> Byte array of UTF-8 encoded JSON payload for User Agent</param>
8587+ /// <param name="write">
8588+ /// If true, writes the feature request to the physical state object.
8589+ /// If false, just calculates the length.
8590+ /// </param>
8591+ /// <returns>The length of the feature request in bytes.</returns>
8592+ /// <remarks>
8593+ /// The feature request consists of:
8594+ /// - 1 byte for the feature ID.
8595+ /// - 4 bytes for the feature data length.
8596+ /// - 1 byte for the version number.
8597+ /// - N bytes for the JSON payload
8598+ /// </remarks>
8599+ internal int WriteUserAgentFeatureRequest(byte[] userAgentJsonPayload,
8600+ bool write)
8601+ {
8602+ // 1byte (Feature Version) + size of UTF-8 encoded JSON payload
8603+ int dataLen = 1 + userAgentJsonPayload.Length;
8604+ // 1byte (Feature ID) + 4bytes (Feature Data Length) + 1byte (Version) + N(JSON payload size)
8605+ int totalLen = 1 + 4 + dataLen;
8606+
8607+ if (write)
8608+ {
8609+ // Write Feature ID
8610+ _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_USERAGENT);
8611+
8612+ // Feature Data Length
8613+ WriteInt(dataLen, _physicalStateObj);
8614+
8615+ // Write Feature Version
8616+ _physicalStateObj.WriteByte(TdsEnums.SUPPORTED_USER_AGENT_VERSION);
8617+
8618+ // Write encoded JSON payload
8619+ _physicalStateObj.WriteByteArray(userAgentJsonPayload, userAgentJsonPayload.Length, 0);
8620+ }
8621+
8622+ return totalLen;
8623+ }
8624+
85828625 private void WriteLoginData(SqlLogin rec,
85838626 TdsEnums.FeatureExtension requestedFeatures,
85848627 SessionData recoverySessionData,
0 commit comments