private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, out SqlEnvChange[] sqlEnvChange)
{
// There could be multiple environment change messages following this token.
byte byteLength;
int processedLength = 0;
int nvalues = 0;
SqlEnvChange[] envarray = new SqlEnvChange[3]; // Why is this hardcoded to 3?
sqlEnvChange = null;
while (tokenLength > processedLength)
{
if (nvalues >= envarray.Length)
{
// This is a rare path. Most of the time we will have 1 or 2 envchange data streams.
SqlEnvChange[] newenvarray = new SqlEnvChange[envarray.Length + 3];
for (int ii = 0; ii < envarray.Length; ii++)
newenvarray[ii] = envarray[ii];
envarray = newenvarray;
}
SqlEnvChange env = new SqlEnvChange();
if (!stateObj.TryReadByte(out env.type))
{
return false;
}
envarray[nvalues] = env;
nvalues++;
switch (env.type)
{
case TdsEnums.ENV_DATABASE:
case TdsEnums.ENV_LANG:
if (!TryReadTwoStringFields(env, stateObj))
{
return false;
}
break;
case TdsEnums.ENV_CHARSET:
// we copied this behavior directly from luxor - see charset envchange
// section from sqlctokn.c
if (!TryReadTwoStringFields(env, stateObj))
{
return false;
}
if (env.newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING)
{
_defaultCodePage = TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE;
_defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
}
else
{
Debug.Assert(env.newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10");
string stringCodePage = env.newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET);
_defaultCodePage = Int32.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture);
_defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
}
break;
case TdsEnums.ENV_PACKETSIZE:
// take care of packet size right here
Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
if (!TryReadTwoStringFields(env, stateObj))
{
// Changing packet size does not support retry, should not pend"
throw SQL.SynchronousCallMayNotPend();
}
// Only set on physical state object - this should only occur on LoginAck prior
// to MARS initialization!
Int32 packetSize = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
if (_physicalStateObj.SetPacketSize(packetSize))
{
// If packet size changed, we need to release our SNIPackets since
// those are tied to packet size of connection.
_physicalStateObj.ClearAllWritePackets();
// Update SNI ConsumerInfo value to be resulting packet size
UInt32 unsignedPacketSize = (UInt32)packetSize;
#if MANAGED_SNI
UInt32 result = SNIProxy.Singleton.SetConnectionBufferSize(_physicalStateObj.Handle, unsignedPacketSize);
#else
UInt32 result = SNINativeMethodWrapper.SNISetInfo(_physicalStateObj.Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize);
#endif // MANAGED_SNI
Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SNISetInfo");
}
break;
case TdsEnums.ENV_LOCALEID:
if (!TryReadTwoStringFields(env, stateObj))
{
return false;
}
_defaultLCID = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
break;
case TdsEnums.ENV_COMPFLAGS:
if (!TryReadTwoStringFields(env, stateObj))
{
return false;
}
break;
case TdsEnums.ENV_COLLATION:
Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!");
if (!stateObj.TryReadByte(out byteLength))
{
return false;
}
env.newLength = byteLength;
if (env.newLength == 5)
{
if (!TryProcessCollation(stateObj, out env.newCollation))
{
return false;
}
// give the parser the new collation values in case parameters don't specify one
_defaultCollation = env.newCollation;
int newCodePage = GetCodePage(env.newCollation, stateObj);
if (newCodePage != _defaultCodePage)
{
_defaultCodePage = newCodePage;
_defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
}
_defaultLCID = env.newCollation.LCID;
}
if (!stateObj.TryReadByte(out byteLength))
{
return false;
}
env.oldLength = byteLength;
Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!");
if (env.oldLength == 5)
{
if (!TryProcessCollation(stateObj, out env.oldCollation))
{
return false;
}
}
env.length = 3 + env.newLength + env.oldLength;
break;
case TdsEnums.ENV_BEGINTRAN:
case TdsEnums.ENV_COMMITTRAN:
case TdsEnums.ENV_ROLLBACKTRAN:
if (!stateObj.TryReadByte(out byteLength))
{
return false;
}
env.newLength = byteLength;
Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!");
if (env.newLength > 0)
{
if (!stateObj.TryReadInt64(out env.newLongValue))
{
return false;
}
Debug.Assert(env.newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
}
else
{
env.newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id.
}
if (!stateObj.TryReadByte(out byteLength))
{
return false;
}
env.oldLength = byteLength;
Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!");
if (env.oldLength > 0)
{
if (!stateObj.TryReadInt64(out env.oldLongValue))
{
return false;
}
Debug.Assert(env.oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
}
else
{
env.oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id.
}
// env.length includes 1 byte type token
env.length = 3 + env.newLength + env.oldLength;
break;
case TdsEnums.ENV_LOGSHIPNODE:
// env.newBinValue is secondary node, env.oldBinValue is witness node
// comes before LoginAck so we can't assert this
if (!TryReadTwoStringFields(env, stateObj))
{
return false;
}
break;
case TdsEnums.ENV_SPRESETCONNECTIONACK:
if (!TryReadTwoBinaryFields(env, stateObj))
{
return false;
}
break;
case TdsEnums.ENV_USERINSTANCE:
if (!TryReadTwoStringFields(env, stateObj))
{
return false;
}
break;
case TdsEnums.ENV_ROUTING:
ushort newLength;
if (!stateObj.TryReadUInt16(out newLength))
{
return false;
}
env.newLength = newLength;
byte protocol;
if (!stateObj.TryReadByte(out protocol))
{
return false;
}
ushort port;
if (!stateObj.TryReadUInt16(out port))
{
return false;
}
ushort serverLen;
if (!stateObj.TryReadUInt16(out serverLen))
{
return false;
}
string serverName;
if (!stateObj.TryReadString(serverLen, out serverName))
{
return false;
}
env.newRoutingInfo = new RoutingInfo(protocol, port, serverName);
ushort oldLength;
if (!stateObj.TryReadUInt16(out oldLength))
{
return false;
}
if (!stateObj.TrySkipBytes(oldLength))
{
return false;
}
env.length = env.newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
break;
// ENVCHANGE tokens not supported by CoreCLR
case TdsEnums.ENV_ENLISTDTC:
case TdsEnums.ENV_DEFECTDTC:
case TdsEnums.ENV_TRANSACTIONENDED:
case TdsEnums.ENV_PROMOTETRANSACTION:
case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS:
throw SQL.UnsupportedFeatureAndToken(_connHandler, ((TdsEnums.EnvChangeType)env.type).ToString());
default:
Debug.Assert(false, "Unknown environment change token: " + env.type);
break;
}
processedLength += env.length;
}
sqlEnvChange = envarray;
return true;
}