private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavior run, TdsParserStateObject stateObj) { ushort curCmd; ushort status; int count; // Can't retry TryProcessDone stateObj._syncOverAsync = true; // status // command // rowcount (valid only if DONE_COUNT bit is set) if (!stateObj.TryReadUInt16(out status)) { return false; } if (!stateObj.TryReadUInt16(out curCmd)) { return false; } long longCount; if (!stateObj.TryReadInt64(out longCount)) { return false; } count = (int)longCount; // We get a done token with the attention bit set if (TdsEnums.DONE_ATTN == (status & TdsEnums.DONE_ATTN)) { Debug.Assert(TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE), "Not expecting DONE_MORE when receiving DONE_ATTN"); Debug.Assert(stateObj._attentionSent, "Received attention done without sending one!"); stateObj._attentionReceived = true; Debug.Assert(stateObj._inBytesUsed == stateObj._inBytesRead && stateObj._inBytesPacket == 0, "DONE_ATTN received with more data left on wire"); } if ((null != cmd) && (TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) { if (curCmd != TdsEnums.SELECT) { cmd.InternalRecordsAffected = count; } // Skip the bogus DONE counts sent by the server if (stateObj._receivedColMetaData || (curCmd != TdsEnums.SELECT)) { cmd.OnStatementCompleted(count); } } stateObj._receivedColMetaData = false; // Surface exception for DONE_ERROR in the case we did not receive an error token // in the stream, but an error occurred. In these cases, we throw a general server error. The // situations where this can occur are: an invalid buffer received from client, login error // and the server refused our connection, and the case where we are trying to log in but // the server has reached its max connection limit. Bottom line, we need to throw general // error in the cases where we did not receive a error token along with the DONE_ERROR. if ((TdsEnums.DONE_ERROR == (TdsEnums.DONE_ERROR & status)) && stateObj.ErrorCount == 0 && stateObj._errorTokenReceived == false && (RunBehavior.Clean != (RunBehavior.Clean & run))) { stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0)); if (null != reader) { if (!reader.IsInitialized) { run = RunBehavior.UntilDone; } } } // Similar to above, only with a more severe error. In this case, if we received // the done_srverror, this exception will be added to the collection regardless. // The server will always break the connection in this case. if ((TdsEnums.DONE_SRVERROR == (TdsEnums.DONE_SRVERROR & status)) && (RunBehavior.Clean != (RunBehavior.Clean & run))) { stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0)); if (null != reader) { if (!reader.IsInitialized) { run = RunBehavior.UntilDone; } } } ProcessSqlStatistics(curCmd, status, count); // stop if the DONE_MORE bit isn't set (see above for attention handling) if (TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE)) { stateObj._errorTokenReceived = false; if (stateObj._inBytesUsed >= stateObj._inBytesRead) { stateObj._pendingData = false; } } // _pendingData set by e.g. 'TdsExecuteSQLBatch' // _hasOpenResult always set to true by 'WriteMarsHeader' // if (!stateObj._pendingData && stateObj._hasOpenResult) { /* Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) || (_userStartedLocalTransaction != null && _distributedTransaction != null)) , "ProcessDone - have both distributed and local transactions not null!"); */ // WebData 112722 stateObj.DecrementOpenResultCount(); } return true; }
internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, TdsParserStateObject stateObj) { switch (tdsType) { case TdsEnums.SQLBIT: case TdsEnums.SQLBITN: Debug.Assert(length == 1, "invalid length for SqlBoolean type!"); byte byteValue; if (!stateObj.TryReadByte(out byteValue)) { return false; } value.Boolean = (byteValue != 0); break; case TdsEnums.SQLINTN: if (length == 1) { goto case TdsEnums.SQLINT1; } else if (length == 2) { goto case TdsEnums.SQLINT2; } else if (length == 4) { goto case TdsEnums.SQLINT4; } else { goto case TdsEnums.SQLINT8; } case TdsEnums.SQLINT1: Debug.Assert(length == 1, "invalid length for SqlByte type!"); if (!stateObj.TryReadByte(out byteValue)) { return false; } value.Byte = byteValue; break; case TdsEnums.SQLINT2: Debug.Assert(length == 2, "invalid length for SqlInt16 type!"); short shortValue; if (!stateObj.TryReadInt16(out shortValue)) { return false; } value.Int16 = shortValue; break; case TdsEnums.SQLINT4: Debug.Assert(length == 4, "invalid length for SqlInt32 type!"); int intValue; if (!stateObj.TryReadInt32(out intValue)) { return false; } value.Int32 = intValue; break; case TdsEnums.SQLINT8: Debug.Assert(length == 8, "invalid length for SqlInt64 type!"); long longValue; if (!stateObj.TryReadInt64(out longValue)) { return false; } value.Int64 = longValue; break; case TdsEnums.SQLFLTN: if (length == 4) { goto case TdsEnums.SQLFLT4; } else { goto case TdsEnums.SQLFLT8; } case TdsEnums.SQLFLT4: Debug.Assert(length == 4, "invalid length for SqlSingle type!"); float singleValue; if (!stateObj.TryReadSingle(out singleValue)) { return false; } value.Single = singleValue; break; case TdsEnums.SQLFLT8: Debug.Assert(length == 8, "invalid length for SqlDouble type!"); double doubleValue; if (!stateObj.TryReadDouble(out doubleValue)) { return false; } value.Double = doubleValue; break; case TdsEnums.SQLMONEYN: if (length == 4) { goto case TdsEnums.SQLMONEY4; } else { goto case TdsEnums.SQLMONEY; } case TdsEnums.SQLMONEY: { int mid; uint lo; if (!stateObj.TryReadInt32(out mid)) { return false; } if (!stateObj.TryReadUInt32(out lo)) { return false; } long l = (((long)mid) << 0x20) + ((long)lo); value.SetToMoney(l); break; } case TdsEnums.SQLMONEY4: if (!stateObj.TryReadInt32(out intValue)) { return false; } value.SetToMoney(intValue); break; case TdsEnums.SQLDATETIMN: if (length == 4) { goto case TdsEnums.SQLDATETIM4; } else { goto case TdsEnums.SQLDATETIME; } case TdsEnums.SQLDATETIM4: ushort daypartShort, timepartShort; if (!stateObj.TryReadUInt16(out daypartShort)) { return false; } if (!stateObj.TryReadUInt16(out timepartShort)) { return false; } value.SetToDateTime(daypartShort, timepartShort * SqlDateTime.SQLTicksPerMinute); break; case TdsEnums.SQLDATETIME: int daypart; uint timepart; if (!stateObj.TryReadInt32(out daypart)) { return false; } if (!stateObj.TryReadUInt32(out timepart)) { return false; } value.SetToDateTime(daypart, (int)timepart); break; case TdsEnums.SQLUNIQUEID: { Debug.Assert(length == 16, "invalid length for SqlGuid type!"); byte[] b = new byte[length]; if (!stateObj.TryReadByteArray(b, 0, length)) { return false; } value.SqlGuid = new SqlGuid(b, true); // doesn't copy the byte array break; } case TdsEnums.SQLBINARY: case TdsEnums.SQLBIGBINARY: case TdsEnums.SQLBIGVARBINARY: case TdsEnums.SQLVARBINARY: case TdsEnums.SQLIMAGE: { // Note: Better not come here with plp data!! Debug.Assert(length <= TdsEnums.MAXSIZE); byte[] b = new byte[length]; if (!stateObj.TryReadByteArray(b, 0, length)) { return false; } value.SqlBinary = new SqlBinary(b, true); // doesn't copy the byte array break; } case TdsEnums.SQLVARIANT: if (!TryReadSqlVariant(value, length, stateObj)) { return false; } break; default: Debug.Assert(false, "Unknown SqlType!" + tdsType.ToString(CultureInfo.InvariantCulture)); break; } // switch return true; }
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; UInt32 result = SNINativeMethodWrapper.SNISetInfo(_physicalStateObj.Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize); 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; } UInt16 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); UInt16 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; }