Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4689f8b
netfx, netcore: sync tracing
edwardneal Sep 20, 2025
19163f5
netfx, netcore: whitespace, comment and naming changes
edwardneal Sep 20, 2025
69099ac
netfx, netcore: merge TNIR handling
edwardneal Sep 20, 2025
a5f114f
netfx: sync DNS caching
edwardneal Sep 20, 2025
96ef625
netfx, netcore: merge CER mopup
edwardneal Sep 20, 2025
f2de25f
netfx, netcore: merge debug assertions
edwardneal Sep 20, 2025
83f112b
netfx: sync SNI-specific and endian-specific logic
edwardneal Sep 20, 2025
b2e4fed
netcore: merge netfx synthesis of default column name from operand
edwardneal Sep 20, 2025
d937f90
netfx: ensure disposal of ConstrainedTextWriter and XmlWriter
edwardneal Sep 20, 2025
1c03e7c
netfx, netcore: sync handling of SqlError generation
edwardneal Sep 20, 2025
13f7a22
netfx: port #232
edwardneal Sep 20, 2025
894f8ca
netfx: port TryRunSetupSpinWaitContinuation
edwardneal Sep 20, 2025
b73d240
netcore: sync _resetConnectionEvent.WaitOne
edwardneal Sep 20, 2025
4eb8697
netfx: port skipping read of fields from SqlLoginAck and SqlReturnValue
edwardneal Sep 20, 2025
5c00879
netcore: supporting infrastructure for CER cleanup
edwardneal Sep 20, 2025
70a622f
netfx: sync GetCodePage
edwardneal Sep 20, 2025
aeab751
netcore, netfx: sync SqlGuid handling
edwardneal Sep 20, 2025
b2cb0fe
netcore, netfx: sync SqlBinary handling
edwardneal Sep 20, 2025
844ad1e
netcore: sync date/time handling
edwardneal Sep 20, 2025
3249a81
netcore, netfx: sync decimal handling
edwardneal Sep 20, 2025
1d1e925
netfx, netcore: sync minor cleanup of if conditions
edwardneal Sep 20, 2025
52dd551
netfx, netcore: sync creation of LocalDB instances
edwardneal Sep 20, 2025
7880b5f
netcore: run ApplyFeatureExData in a checked context
edwardneal Sep 20, 2025
143de69
netcore, netfx: sync creation of a TryEventScope
edwardneal Sep 20, 2025
c9585a3
netfx: sync order of variable assignment
edwardneal Sep 20, 2025
5a21361
netcore: merge static constructor
edwardneal Sep 20, 2025
ec02267
Merge branch 'main' into merge/tdsparser-complete
edwardneal Sep 24, 2025
e2d846b
Merge conditional compilation of netfx-only features
edwardneal Sep 24, 2025
af79e6b
Port TryReadByteArray usage from netcore to netfx
edwardneal Sep 24, 2025
69c81be
Rename existing shared TdsParser.cs to TdsParser.SSPI.cs
edwardneal Sep 24, 2025
6bfb6ef
Final merge of TdsParser.cs and redirection of references
edwardneal Sep 24, 2025
0839219
Code review response
edwardneal Sep 26, 2025
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 @@ -386,10 +386,13 @@ internal void Connect(ServerInfo serverInfo,
}

// Encryption is not supported on SQL Local DB - disable it if they have only specified Mandatory
if (connHandler.ConnectionOptions.LocalDBInstance != null && encrypt == SqlConnectionEncryptOption.Mandatory)
if (connHandler.ConnectionOptions.LocalDBInstance != null)
{
encrypt = SqlConnectionEncryptOption.Optional;
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Encryption will be disabled as target server is a SQL Local DB instance.");
if (encrypt == SqlConnectionEncryptOption.Mandatory)
{
encrypt = SqlConnectionEncryptOption.Optional;
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Encryption will be disabled as target server is a SQL Local DB instance.");
}
}

_authenticationProvider = null;
Expand All @@ -398,6 +401,7 @@ internal void Connect(ServerInfo serverInfo,
if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
{
_authenticationProvider = Connection._sspiContextProvider ?? _physicalStateObj.CreateSspiContextProvider();

SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | SSPI or Active Directory Authentication Library loaded for SQL Server based integrated authentication");
}

Expand Down Expand Up @@ -848,6 +852,7 @@ private void SendPreLoginHandshake(
int actIdSize = GUID_SIZE + sizeof(uint);
offset += actIdSize;
optionDataSize += actIdSize;

SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.SendPreLoginHandshake|INFO> ClientConnectionID {0}, ActivityID {1}", _connHandler?._clientConnectionId, actId);
break;

Expand Down Expand Up @@ -1479,11 +1484,12 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
// Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError|ERR|ADV> 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
// continue building SqlError instance

// details.ErrorMessage is null terminated with garbage beyond that, since fixed length
// PInvoke code automatically sets the length of the string for us, so no need to look for \0
string errorMessage = details.ErrorMessage;

/* Format SNI errors and add Context Information
Expand Down Expand Up @@ -1529,6 +1535,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > ':' character missing in sni errorMessage. Error Message index of ':' = {0}", iColon);
Debug.Assert(errorMessage.Length > iColon + 1 && errorMessage[iColon + 1] == ' ', "Expecting a space after the ':' character");
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Expecting a space after the ':' character. Error Message Length = {0}", errorMessage.Length);

// extract the message excluding the colon and trailing cr/lf chars
if (0 <= iColon)
{
Expand Down Expand Up @@ -6994,8 +7001,8 @@ internal TdsOperationStatus TryReadSqlValueInternal(SqlBuffer value, byte tdsTyp
{
return result;
}
value.SqlBinary = SqlBinary.WrapBytes(b); // doesn't copy the byte array

value.SqlBinary = SqlBinary.WrapBytes(b); // doesn't copy the byte array
break;
}

Expand Down Expand Up @@ -7868,13 +7875,17 @@ internal byte[] SerializeSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj)

// sign
if (d.IsPositive)
{
bytes[current++] = 1;
}
else
{
bytes[current++] = 0;

}

Span<uint> data = stackalloc uint[4];
d.WriteTdsValue(data);

byte[] bytesPart = SerializeUnsignedInt(data[0], stateObj);
Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
current += 4;
Expand All @@ -7894,12 +7905,17 @@ internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj)
{
// sign
if (d.IsPositive)
{
stateObj.WriteByte(1);
}
else
{
stateObj.WriteByte(0);
}

Span<uint> data = stackalloc uint[4];
d.WriteTdsValue(data);

WriteUnsignedInt(data[0], stateObj);
WriteUnsignedInt(data[1], stateObj);
WriteUnsignedInt(data[2], stateObj);
Expand Down Expand Up @@ -10103,7 +10119,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
maxsize = 1;
}

WriteParameterVarLen(mt, maxsize, false /*IsNull*/, stateObj);
WriteParameterVarLen(mt, maxsize, isNull: false, stateObj);
}
}
else
Expand Down Expand Up @@ -10227,9 +10243,9 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
// vector value when communicating with SQL Server.
var sqlVectorProps = ((ISqlVector)param.Value);
maxsize = sqlVectorProps.Size;
}
}

WriteParameterVarLen(mt, maxsize, false /*IsNull*/, stateObj);
WriteParameterVarLen(mt, maxsize, isNull: false, stateObj);
}
}

Expand All @@ -10256,6 +10272,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
// For vector type we need to write scale as the element type of the vector.
stateObj.WriteByte(((ISqlVector)param.Value).ElementType);
}

// write out collation or xml metadata

if ((mt.SqlDbType == SqlDbType.Xml || mt.SqlDbType == SqlDbTypeExtensions.Json))
Expand Down Expand Up @@ -10372,7 +10389,7 @@ private void TDSExecuteRPCParameterSetupWriteCompletion(SqlCommand cmd, IList<_S
);
}

// This is in its own method to avoid always allocating the lambda in TDSExecuteRPCParameter
// This is in its own method to avoid always allocating the lambda in TDSExecuteRPCParameter
private void TDSExecuteRPCParameterSetupFlushCompletion(TdsParserStateObject stateObj, TaskCompletionSource<object> completion, Task execFlushTask, bool taskReleaseConnectionLock)
{
execFlushTask.ContinueWith(tsk => ExecuteFlushTaskCallback(tsk, stateObj, completion, taskReleaseConnectionLock), TaskScheduler.Default);
Expand Down Expand Up @@ -10504,7 +10521,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa
// Value for TVP default is empty list, not NULL
if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued)
{
value = Array.Empty<SqlDataRecord>();
value = Array.Empty<Server.SqlDataRecord>();
typeCode = ExtendedClrTypeCode.IEnumerableOfSqlDataRecord;
}
else
Expand All @@ -10526,7 +10543,10 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa
{
value = param.GetCoercedValue();
typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(
metaData.SqlDbType, metaData.IsMultiValued, value, null);
metaData.SqlDbType,
metaData.IsMultiValued,
value,
udtType: null);
}

if (advancedTraceIsOn)
Expand Down Expand Up @@ -11673,21 +11693,19 @@ private void WriteTokenLength(byte token, int length, TdsParserStateObject state
// For Plp fields, this should only be used when writing to metadata header.
// For actual data length, WriteDataLength should be used.
// For Xml fields, there is no token length field. For MAX fields it is 0xffff.
if (TdsEnums.SQLUDT == token)
{
if (TdsEnums.SQLUDT == token)
{
tokenLength = 8;
}
else if (token == TdsEnums.SQLXMLTYPE)
{
tokenLength = 8;
}
else if (token == TdsEnums.SQLVECTOR)
{
tokenLength = 2;
WriteShort(length, stateObj);
return;
}
tokenLength = 8;
}
else if (token == TdsEnums.SQLXMLTYPE)
{
tokenLength = 8;
}
else if (token == TdsEnums.SQLVECTOR)
{
tokenLength = 2;
WriteShort(length, stateObj);
return;
}

if (tokenLength == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,29 +388,27 @@ internal void Connect(ServerInfo serverInfo,
authType == SqlAuthenticationMethod.NotSpecified ? SqlAuthenticationMethod.SqlPassword.ToString() : authType.ToString());
}

//Create LocalDB instance if necessary
// Encryption is not supported on SQL Local DB - disable it if they have only specified Mandatory
if (connHandler.ConnectionOptions.LocalDBInstance != null)
{
// Create LocalDB instance if necessary
LocalDbApi.CreateLocalDbInstance(connHandler.ConnectionOptions.LocalDBInstance);
if (encrypt == SqlConnectionEncryptOption.Mandatory)
{
// Encryption is not supported on SQL Local DB - disable it for current session.
encrypt = SqlConnectionEncryptOption.Optional;
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Encryption will be disabled as target server is a SQL Local DB instance.");
}
}

_authenticationProvider = null;

// AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server
if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
{
_authenticationProvider = Connection._sspiContextProvider ?? _physicalStateObj.CreateSspiContextProvider();

SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | SSPI or Active Directory Authentication Library loaded for SQL Server based integrated authentication");
}
else
{
_authenticationProvider = null;
}

// if Strict encryption (i.e. isTlsFirst) is chosen trust server certificate should always be false.
if (isTlsFirst)
Expand Down Expand Up @@ -447,6 +445,7 @@ internal void Connect(ServerInfo serverInfo,
FQDNforDNSCache = FQDNforDNSCache.Substring(0, commaPos);
}

// AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server
_physicalStateObj.CreatePhysicalSNIHandle(
serverInfo.ExtendedServerName,
timeout,
Expand Down Expand Up @@ -571,6 +570,7 @@ internal void Connect(ServerInfo serverInfo,
}

uint retCode = _physicalStateObj.SniGetConnectionId(ref _connHandler._clientConnectionId);

Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Sending prelogin handshake");

Expand Down Expand Up @@ -1501,14 +1501,15 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
// Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError|ERR|ADV> 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
}
}
// continue building SqlError instance

// error.errorMessage is null terminated with garbage beyond that, since fixed length
// details.ErrorMessage is null terminated with garbage beyond that, since fixed length
// PInvoke code automatically sets the length of the string for us, so no need to look for \0
string errorMessage;
errorMessage = string.IsNullOrEmpty(details.ErrorMessage) ? string.Empty : details.ErrorMessage;

/* Format SNI errors and add Context Information
*
* General syntax is:
Expand Down Expand Up @@ -1544,7 +1545,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Native Error Code = {0}", win32ErrorCode);
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 @@ -1591,8 +1592,8 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
SqlClientEventSource.Log.TryAdvancedTraceErrorEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > SNI Error Message. Native Error = {0}, Line Number ={1}, Function ={2}, Exception ={3}, Server = {4}",
details.NativeError, (int)details.LineNumber, details.Function, details.Exception, _server);

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

internal void CheckResetConnection(TdsParserStateObject stateObj)
Expand Down Expand Up @@ -4782,7 +4783,7 @@ internal void DrainData(TdsParserStateObject stateObj)
SqlDataReader.SharedState sharedState = stateObj._readerState;
if (sharedState != null && sharedState._dataReady)
{
var metadata = stateObj._cleanupMetaData;
_SqlMetaDataSet metadata = stateObj._cleanupMetaData;
TdsOperationStatus result;
if (stateObj._partialHeaderBytesRead > 0)
{
Expand Down Expand Up @@ -4826,7 +4827,6 @@ internal void DrainData(TdsParserStateObject stateObj)
throw SQL.SynchronousCallMayNotPend();
}
}

}


Expand All @@ -4840,12 +4840,12 @@ internal void DrainData(TdsParserStateObject stateObj)
}
Run(RunBehavior.Clean, null, null, null, stateObj);
}
// @TODO: CER Exception Handling was removed here (see GH#3581)
catch
{
_connHandler.DoomThisConnection();
throw;
}
// @TODO: CER Exception Handling was removed here (see GH#3581)
}


Expand Down Expand Up @@ -9780,7 +9780,8 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout
if (
!(cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled
||
(cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && cmd.Connection.IsColumnEncryptionSettingEnabled)))
(cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && cmd.Connection.IsColumnEncryptionSettingEnabled))
)
{
throw SQL.ParamInvalidForceColumnEncryptionSetting(param.ParameterName, rpcext.GetCommandTextOrRpcName());
}
Expand Down Expand Up @@ -10163,7 +10164,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
maxsize = 1;
}

WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
WriteParameterVarLen(mt, maxsize, isNull: false, stateObj);
}
}
else
Expand Down Expand Up @@ -10289,7 +10290,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
maxsize = sqlVectorProps.Size;
}

WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
WriteParameterVarLen(mt, maxsize, isNull: false, stateObj);
}
}

Expand All @@ -10311,9 +10312,9 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
{
stateObj.WriteByte(param.GetActualScale());
}
// For vector type we need to write scale as the element type of the vector.
else if (mt.SqlDbType == SqlDbTypeExtensions.Vector)
{
// For vector type we need to write scale as the element type of the vector.
stateObj.WriteByte(((ISqlVector)param.Value).ElementType);
}

Expand Down Expand Up @@ -10479,6 +10480,7 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T
if (tsk.Exception != null)
{
Exception exc = tsk.Exception.InnerException;

try
{
FailureCleanup(stateObj, tsk.Exception);
Expand Down Expand Up @@ -13447,7 +13449,6 @@ bool writeDataSizeToSnapshot
charsLeft -= totalCharsRead;
offst += totalCharsRead;


while (charsLeft > 0)
{
if (!partialReadInProgress)
Expand Down