internal bool TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) { Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!"); metaData = null; _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns); int[] indexMap = new int[cColumns]; if (!stateObj.TryReadUInt16(out altMetaDataSet.id)) { return false; } byte byCols; if (!stateObj.TryReadByte(out byCols)) { return false; } while (byCols > 0) { if (!stateObj.TrySkipBytes(2)) { // ignore ColNum ... return false; } byCols--; } // pass 1, read the meta data off the wire for (int i = 0; i < cColumns; i++) { // internal meta data class _SqlMetaData col = altMetaDataSet[i]; byte op; if (!stateObj.TryReadByte(out op)) { return false; } ushort operand; if (!stateObj.TryReadUInt16(out operand)) { return false; } if (!TryCommonProcessMetaData(stateObj, col)) { return false; } indexMap[i] = i; } altMetaDataSet.indexMap = indexMap; altMetaDataSet.visibleColumns = cColumns; metaData = altMetaDataSet; return true; }
private bool TryProcessOneTable(TdsParserStateObject stateObj, ref int length, out MultiPartTableName multiPartTableName) { ushort tableLen; MultiPartTableName mpt; string value; multiPartTableName = default(MultiPartTableName); mpt = new MultiPartTableName(); byte nParts; // Find out how many parts in the TDS stream if (!stateObj.TryReadByte(out nParts)) { return false; } length--; if (nParts == 4) { if (!stateObj.TryReadUInt16(out tableLen)) { return false; } length -= 2; if (!stateObj.TryReadString(tableLen, out value)) { return false; } mpt.ServerName = value; nParts--; length -= (tableLen * 2); // wide bytes } if (nParts == 3) { if (!stateObj.TryReadUInt16(out tableLen)) { return false; } length -= 2; if (!stateObj.TryReadString(tableLen, out value)) { return false; } mpt.CatalogName = value; length -= (tableLen * 2); // wide bytes nParts--; } if (nParts == 2) { if (!stateObj.TryReadUInt16(out tableLen)) { return false; } length -= 2; if (!stateObj.TryReadString(tableLen, out value)) { return false; } mpt.SchemaName = value; length -= (tableLen * 2); // wide bytes nParts--; } if (nParts == 1) { if (!stateObj.TryReadUInt16(out tableLen)) { return false; } length -= 2; if (!stateObj.TryReadString(tableLen, out value)) { return false; } mpt.TableName = value; length -= (tableLen * 2); // wide bytes nParts--; } Debug.Assert(nParts == 0, "ProcessTableName:Unidentified parts in the table name token stream!"); multiPartTableName = mpt; return true; }
internal bool TryProcessError(byte token, TdsParserStateObject stateObj, out SqlError error) { ushort shortLen; byte byteLen; int number; byte state; byte errorClass; error = null; if (!stateObj.TryReadInt32(out number)) { return false; } if (!stateObj.TryReadByte(out state)) { return false; } if (!stateObj.TryReadByte(out errorClass)) { return false; } Debug.Assert(((errorClass >= TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLERROR) || ((errorClass < TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLINFO), "class and token don't match!"); if (!stateObj.TryReadUInt16(out shortLen)) { return false; } string message; if (!stateObj.TryReadString(shortLen, out message)) { return false; } if (!stateObj.TryReadByte(out byteLen)) { return false; } string server; // If the server field is not recieved use the locally cached value. if (byteLen == 0) { server = _server; } else { if (!stateObj.TryReadString(byteLen, out server)) { return false; } } if (!stateObj.TryReadByte(out byteLen)) { return false; } string procedure; if (!stateObj.TryReadString(byteLen, out procedure)) { return false; } int line; if (!stateObj.TryReadInt32(out line)) { return false; } error = new SqlError(number, state, errorClass, _server, message, procedure, line); return true; }
internal bool TryProcessReturnValue(int length, TdsParserStateObject stateObj, out SqlReturnValue returnValue) { returnValue = null; SqlReturnValue rec = new SqlReturnValue(); rec.length = length; // In Yukon this length is -1 ushort parameterIndex; if (!stateObj.TryReadUInt16(out parameterIndex)) { return false; } byte len; if (!stateObj.TryReadByte(out len)) { // Length of parameter name return false; } if (len > 0) { if (!stateObj.TryReadString(len, out rec.parameter)) { return false; } } // read status and ignore byte ignored; if (!stateObj.TryReadByte(out ignored)) { return false; } UInt32 userType; // read user type - 4 bytes Yukon, 2 backwards if (!stateObj.TryReadUInt32(out userType)) { return false; } // read off the flags ushort ignoredFlags; if (!stateObj.TryReadUInt16(out ignoredFlags)) { return false; } // read the type byte tdsType; if (!stateObj.TryReadByte(out tdsType)) { return false; } // read the MaxLen // For xml datatpyes, there is no tokenLength int tdsLen; if (tdsType == TdsEnums.SQLXMLTYPE) { tdsLen = TdsEnums.SQL_USHORTVARMAXLEN; } else if (IsVarTimeTds(tdsType)) tdsLen = 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN else if (tdsType == TdsEnums.SQLDATE) { tdsLen = 3; } else { if (!TryGetTokenLength(tdsType, stateObj, out tdsLen)) { return false; } } rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen); rec.type = rec.metaType.SqlDbType; // always use the nullable type for parameters if Shiloh or later // Sphinx sometimes sends fixed length return values rec.tdsType = rec.metaType.NullableType; rec.isNullable = true; if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) { rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType); } if (rec.type == SqlDbType.Decimal) { if (!stateObj.TryReadByte(out rec.precision)) { return false; } if (!stateObj.TryReadByte(out rec.scale)) { return false; } } if (rec.metaType.IsVarTime) { if (!stateObj.TryReadByte(out rec.scale)) { return false; } } if (tdsType == TdsEnums.SQLUDT) { throw SQL.UnsupportedFeatureAndToken(_connHandler, SqlDbType.Udt.ToString()); } if (rec.type == SqlDbType.Xml) { // Read schema info byte schemapresent; if (!stateObj.TryReadByte(out schemapresent)) { return false; } if ((schemapresent & 1) != 0) { if (!stateObj.TryReadByte(out len)) { return false; } if (len != 0) { if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionDatabase)) { return false; } } if (!stateObj.TryReadByte(out len)) { return false; } if (len != 0) { if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionOwningSchema)) { return false; } } short slen; if (!stateObj.TryReadInt16(out slen)) { return false; } if (slen != 0) { if (!stateObj.TryReadString(slen, out rec.xmlSchemaCollectionName)) { return false; } } } } else if (rec.metaType.IsCharType) { // read the collation for 8.x servers if (!TryProcessCollation(stateObj, out rec.collation)) { return false; } int codePage = GetCodePage(rec.collation, stateObj); // if the column lcid is the same as the default, use the default encoder if (codePage == _defaultCodePage) { rec.codePage = _defaultCodePage; rec.encoding = _defaultEncoding; } else { rec.codePage = codePage; rec.encoding = System.Text.Encoding.GetEncoding(rec.codePage); } } // for now we coerce return values into a SQLVariant, not good... bool isNull = false; ulong valLen; if (!TryProcessColumnHeaderNoNBC(rec, stateObj, out isNull, out valLen)) { return false; } // always read as sql types Debug.Assert(valLen < (ulong)(Int32.MaxValue), "ProcessReturnValue received data size > 2Gb"); int intlen = valLen > (ulong)(Int32.MaxValue) ? Int32.MaxValue : (int)valLen; if (rec.metaType.IsPlp) { intlen = Int32.MaxValue; // If plp data, read it all } if (isNull) { GetNullSqlValue(rec.value, rec); } else { if (!TryReadSqlValue(rec.value, rec, intlen, stateObj)) { return false; } } returnValue = rec; return true; }
internal bool TryProcessReturnValue(int length, TdsParserStateObject stateObj, out SqlReturnValue returnValue, SqlCommandColumnEncryptionSetting columnEncryptionSetting) { returnValue = null; SqlReturnValue rec = new SqlReturnValue(); rec.length = length; // In Yukon this length is -1 if (_isYukon) { if (!stateObj.TryReadUInt16(out rec.parmIndex)) { return false; } } byte len; if (!stateObj.TryReadByte(out len)) { // Length of parameter name return false; } rec.parameter = null; if (len > 0) { if (!stateObj.TryReadString(len, out rec.parameter)) { return false; } } // read status and ignore byte ignored; if (!stateObj.TryReadByte(out ignored)) { return false; } UInt32 userType; // read user type - 4 bytes Yukon, 2 backwards if (IsYukonOrNewer) { if (!stateObj.TryReadUInt32(out userType)) { return false; } } else { ushort userTypeShort; if (!stateObj.TryReadUInt16(out userTypeShort)) { return false; } userType = userTypeShort; } // Read off the flags. // The first byte is ignored since it doesn't contain any interesting information. byte flags; if (!stateObj.TryReadByte(out flags)) { return false; } if (!stateObj.TryReadByte(out flags)) { return false; } // Check if the column is encrypted. if (_serverSupportsColumnEncryption) { rec.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } // read the type byte tdsType; if (!stateObj.TryReadByte(out tdsType)) { return false; } // read the MaxLen // For xml datatpyes, there is no tokenLength int tdsLen; if (tdsType == TdsEnums.SQLXMLTYPE) { tdsLen = TdsEnums.SQL_USHORTVARMAXLEN; } else if (IsVarTimeTds(tdsType)) tdsLen = 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN else if (tdsType == TdsEnums.SQLDATE) { tdsLen = 3; } else { if (!TryGetTokenLength(tdsType, stateObj, out tdsLen)) { return false; } } rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen); rec.type = rec.metaType.SqlDbType; // always use the nullable type for parameters if Shiloh or later // Sphinx sometimes sends fixed length return values if (_isShiloh) { rec.tdsType = rec.metaType.NullableType; rec.isNullable = true; if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) { Debug.Assert(_isYukon, "plp data from pre-Yukon server"); rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType); } } else { // For sphinx, keep the fixed type if that is what is returned if (rec.metaType.NullableType == tdsType) rec.isNullable = true; rec.tdsType = (byte)tdsType; } if (rec.type == SqlDbType.Decimal) { if (!stateObj.TryReadByte(out rec.precision)) { return false; } if (!stateObj.TryReadByte(out rec.scale)) { return false; } } if (rec.metaType.IsVarTime) { if (!stateObj.TryReadByte(out rec.scale)) { return false; } } if (tdsType == TdsEnums.SQLUDT) { if (!TryProcessUDTMetaData((SqlMetaDataPriv) rec, stateObj)) { return false; } } if (rec.type == SqlDbType.Xml) { // Read schema info byte schemapresent; if (!stateObj.TryReadByte(out schemapresent)) { return false; } if ((schemapresent & 1) != 0) { if (!stateObj.TryReadByte(out len)) { return false; } if (len != 0) { if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionDatabase)) { return false; } } if (!stateObj.TryReadByte(out len)) { return false; } if (len != 0) { if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionOwningSchema)) { return false; } } short slen; if (!stateObj.TryReadInt16(out slen)) { return false; } if (slen != 0) { if (!stateObj.TryReadString(slen, out rec.xmlSchemaCollectionName)) { return false; } } } } else if (_isShiloh && rec.metaType.IsCharType) { // read the collation for 8.x servers if (!TryProcessCollation(stateObj, out rec.collation)) { return false; } int codePage = GetCodePage(rec.collation, stateObj); // if the column lcid is the same as the default, use the default encoder if (codePage == _defaultCodePage) { rec.codePage = _defaultCodePage; rec.encoding = _defaultEncoding; } else { rec.codePage = codePage; rec.encoding = System.Text.Encoding.GetEncoding(rec.codePage); } } // For encrypted parameters, read the unencrypted type and encryption information. if (_serverSupportsColumnEncryption && rec.isEncrypted) { if (!TryProcessTceCryptoMetadata(stateObj, rec, cipherTable: null, columnEncryptionSetting: columnEncryptionSetting, isReturnValue: true)) { return false; } } // for now we coerce return values into a SQLVariant, not good... bool isNull = false; ulong valLen; if (!TryProcessColumnHeaderNoNBC(rec, stateObj, out isNull, out valLen)) { return false; } // always read as sql types Debug.Assert(valLen < (ulong)(Int32.MaxValue), "ProcessReturnValue received data size > 2Gb"); int intlen = valLen > (ulong)(Int32.MaxValue) ? Int32.MaxValue : (int)valLen; if (rec.metaType.IsPlp) { intlen = Int32.MaxValue; // If plp data, read it all } if (isNull) { GetNullSqlValue(rec.value, rec, SqlCommandColumnEncryptionSetting.Disabled, _connHandler); } else { // We should never do any decryption here, so pass disabled as the command encryption override. // We only read the binary value and decryption will be performed by OnReturnValue(). if (!TryReadSqlValue(rec.value, rec, intlen, stateObj, SqlCommandColumnEncryptionSetting.Disabled, columnName:null /*Not used*/)) { return false; } } returnValue = rec; return true; }
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 TryProcessError(byte token, TdsParserStateObject stateObj, out SqlError error) { ushort shortLen; byte byteLen; int number; byte state; byte errorClass; error = null; if (!stateObj.TryReadInt32(out number)) { return false; } if (!stateObj.TryReadByte(out state)) { return false; } if (!stateObj.TryReadByte(out errorClass)) { return false; } Debug.Assert(((errorClass >= TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLERROR) || ((errorClass < TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLINFO), "class and token don't match!"); if (!stateObj.TryReadUInt16(out shortLen)) { return false; } string message; if (!stateObj.TryReadString(shortLen, out message)) { return false; } if (!stateObj.TryReadByte(out byteLen)) { return false; } string server; // If the server field is not received use the locally cached value. if (byteLen == 0) { server = _server; } else { if (!stateObj.TryReadString(byteLen, out server)) { return false; } } if (!stateObj.TryReadByte(out byteLen)) { return false; } string procedure; if (!stateObj.TryReadString(byteLen, out procedure)) { return false; } int line; if (_isYukon) { if (!stateObj.TryReadInt32(out line)) { return false; } } else { ushort shortLine; if (!stateObj.TryReadUInt16(out shortLine)) { return false; } line = shortLine; // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server // In that case we still have to read another 2 bytes if (_state == TdsParserState.OpenNotLoggedIn) { // Login incomplete byte b; if (!stateObj.TryPeekByte(out b)) { return false; } if (b == 0) { // This is an invalid token value ushort value; if (!stateObj.TryReadUInt16(out value)) { return false; } line = (line << 16) + value; } } } error = new SqlError(number, state, errorClass, _server, message, procedure, line); return true; }
// // Read in a SQLVariant // // SQLVariant looks like: // struct // { // BYTE TypeTag // BYTE cbPropBytes // BYTE[] Properties // BYTE[] DataVal // } internal bool TryReadSqlVariant(SqlBuffer value, int lenTotal, TdsParserStateObject stateObj) { // get the SQLVariant type byte type; if (!stateObj.TryReadByte(out type)) { return false; } ushort lenMax = 0; // maximum lenData of value inside variant // read cbPropBytes byte cbPropsActual; if (!stateObj.TryReadByte(out cbPropsActual)) { return false; } MetaType mt = MetaType.GetSqlDataType(type, 0 /*no user datatype*/, 0 /* no lenData, non-nullable type */); byte cbPropsExpected = mt.PropBytes; int lenConsumed = TdsEnums.SQLVARIANT_SIZE + cbPropsActual; // type, count of propBytes, and actual propBytes int lenData = lenTotal - lenConsumed; // length of actual data // read known properties and skip unknown properties Debug.Assert(cbPropsActual >= cbPropsExpected, "cbPropsActual is less that cbPropsExpected!"); // // now read the value // switch (type) { case TdsEnums.SQLBIT: case TdsEnums.SQLINT1: case TdsEnums.SQLINT2: case TdsEnums.SQLINT4: case TdsEnums.SQLINT8: case TdsEnums.SQLFLT4: case TdsEnums.SQLFLT8: case TdsEnums.SQLMONEY: case TdsEnums.SQLMONEY4: case TdsEnums.SQLDATETIME: case TdsEnums.SQLDATETIM4: case TdsEnums.SQLUNIQUEID: if (!TryReadSqlValueInternal(value, type, lenData, stateObj)) { return false; } break; case TdsEnums.SQLDECIMALN: case TdsEnums.SQLNUMERICN: { Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for decimal/numeric type!"); byte precision; if (!stateObj.TryReadByte(out precision)) { return false; } byte scale; if (!stateObj.TryReadByte(out scale)) { return false; } // skip over unknown properties if (cbPropsActual > cbPropsExpected) { if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) { return false; } } if (!TryReadSqlDecimal(value, TdsEnums.MAX_NUMERIC_LEN, precision, scale, stateObj)) { return false; } break; } case TdsEnums.SQLBIGBINARY: case TdsEnums.SQLBIGVARBINARY: //Debug.Assert(TdsEnums.VARNULL == lenData, "SqlVariant: data length for Binary indicates null?"); Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for binary type!"); if (!stateObj.TryReadUInt16(out lenMax)) { return false; } Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarbinary(max) in a sqlvariant"); // skip over unknown properties if (cbPropsActual > cbPropsExpected) { if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) { return false; } } goto case TdsEnums.SQLBIT; case TdsEnums.SQLBIGCHAR: case TdsEnums.SQLBIGVARCHAR: case TdsEnums.SQLNCHAR: case TdsEnums.SQLNVARCHAR: { Debug.Assert(cbPropsExpected == 7, "SqlVariant: invalid PropBytes for character type!"); SqlCollation collation; if (!TryProcessCollation(stateObj, out collation)) { return false; } if (!stateObj.TryReadUInt16(out lenMax)) { return false; } Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarchar(max) or nvarchar(max) in a sqlvariant"); // skip over unknown properties if (cbPropsActual > cbPropsExpected) { if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) { return false; } } Encoding encoding = Encoding.GetEncoding(GetCodePage(collation, stateObj)); if (!TryReadSqlStringValue(value, type, lenData, encoding, false, stateObj)) { return false; } break; } case TdsEnums.SQLDATE: if (!TryReadSqlDateTime(value, type, lenData, 0, stateObj)) { return false; } break; case TdsEnums.SQLTIME: case TdsEnums.SQLDATETIME2: case TdsEnums.SQLDATETIMEOFFSET: { Debug.Assert(cbPropsExpected == 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!"); byte scale; if (!stateObj.TryReadByte(out scale)) { return false; } // skip over unknown properties if (cbPropsActual > cbPropsExpected) { if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) { return false; } } if (!TryReadSqlDateTime(value, type, lenData, scale, stateObj)) { return false; } break; } default: Debug.Assert(false, "Unknown tds type in SqlVariant!" + type.ToString(CultureInfo.InvariantCulture)); break; } // switch return true; }
private bool TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col, SqlTceCipherInfoTable? cipherTable, bool fColMD, SqlCommandColumnEncryptionSetting columnEncryptionSetting) { byte byteLen; UInt32 userType; // read user type - 4 bytes Yukon, 2 backwards if (IsYukonOrNewer) { if (!stateObj.TryReadUInt32(out userType)) { return false; } } else { ushort userTypeShort; if (!stateObj.TryReadUInt16(out userTypeShort)) { return false; } userType = userTypeShort; } // read flags and set appropriate flags in structure byte flags; if (!stateObj.TryReadByte(out flags)) { return false; } col.updatability = (byte)((flags & TdsEnums.Updatability) >> 2); col.isNullable = (TdsEnums.Nullable == (flags & TdsEnums.Nullable)); col.isIdentity = (TdsEnums.Identity == (flags & TdsEnums.Identity)); // read second byte of column metadata flags if (!stateObj.TryReadByte(out flags)) { return false; } col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet)); if (fColMD && _serverSupportsColumnEncryption) { col.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } // Read TypeInfo if (!TryProcessTypeInfo (stateObj, col, userType)) { return false; } // Read tablename if present if (col.metaType.IsLong && !col.metaType.IsPlp) { if (_isYukon) { int unusedLen = 0xFFFF; //We ignore this value if (!TryProcessOneTable(stateObj, ref unusedLen, out col.multiPartTableName)) { return false; } } else { ushort shortLen; if (!stateObj.TryReadUInt16(out shortLen)) { return false; } string tableName; if (!stateObj.TryReadString(shortLen, out tableName)) { return false; } // with Sql2000 this is returned as an unquoted mix of catalog.owner.table // all of which may contain "." and unable to parse correctly from the string alone // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios col.multiPartTableName = new MultiPartTableName(tableName); } } // Read the TCE column cryptoinfo if (fColMD && _serverSupportsColumnEncryption && col.isEncrypted) { // If the column is encrypted, we should have a valid cipherTable if (cipherTable.HasValue && !TryProcessTceCryptoMetadata (stateObj, col, cipherTable.Value, columnEncryptionSetting, isReturnValue: false)) { return false; } } // Read the column name if (!stateObj.TryReadByte(out byteLen)) { return false; } if (!stateObj.TryReadString(byteLen, out col.column)) { return false; } // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings. // We only need to fire this event when we actually have a meta data stream with 0 or more rows. stateObj._receivedColMetaData = true; return true; }
private bool TryProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObject stateObj) { ushort shortLength; byte byteLength; if (!stateObj.TryReadUInt16(out shortLength)) { // max byte size return false; } metaData.length = shortLength; // database name if (!stateObj.TryReadByte(out byteLength)) { return false; } if (byteLength != 0) { if (!stateObj.TryReadString(byteLength, out metaData.udtDatabaseName)) { return false; } } // schema name if (!stateObj.TryReadByte(out byteLength)) { return false; } if (byteLength != 0) { if (!stateObj.TryReadString(byteLength, out metaData.udtSchemaName)) { return false; } } // type name if (!stateObj.TryReadByte(out byteLength)) { return false; } if (byteLength != 0) { if (!stateObj.TryReadString(byteLength, out metaData.udtTypeName)) { return false; } } if (!stateObj.TryReadUInt16(out shortLength)) { return false; } if (shortLength != 0) { if (!stateObj.TryReadString(shortLength, out metaData.udtAssemblyQualifiedName)) { return false; } } return true; }
/// <summary> /// <para> Parses the TDS message to read single CIPHER_INFO entry.</para> /// </summary> internal bool TryReadCipherInfoEntry (TdsParserStateObject stateObj, out SqlTceCipherInfoEntry entry) { byte cekValueCount = 0; entry = new SqlTceCipherInfoEntry(ordinal: 0); // Read the DB ID int dbId; if (!stateObj.TryReadInt32(out dbId)) { return false; } // Read the keyID int keyId; if (!stateObj.TryReadInt32(out keyId)) { return false; } // Read the key version int keyVersion; if (!stateObj.TryReadInt32(out keyVersion)) { return false; } // Read the key MD Version byte[] keyMDVersion = new byte[8]; if (!stateObj.TryReadByteArray(keyMDVersion, 0, 8)) { return false; } // Read the value count if (!stateObj.TryReadByte (out cekValueCount)) { return false; } for (int i = 0; i < cekValueCount; i++) { // Read individual CEK values byte[] encryptedCek; string keyPath; string keyStoreName; byte algorithmLength; string algorithmName; ushort shortValue; byte byteValue; int length; // Read the length of encrypted CEK if (!stateObj.TryReadUInt16 (out shortValue)) { return false; } length = shortValue; encryptedCek = new byte[length]; // Read the actual encrypted CEK if (!stateObj.TryReadByteArray (encryptedCek, 0, length)) { return false; } // Read the length of key store name if (!stateObj.TryReadByte (out byteValue)) { return false; } length = byteValue; // And read the key store name now if (!stateObj.TryReadString(length, out keyStoreName)) { return false; } // Read the length of key Path if (!stateObj.TryReadUInt16 (out shortValue)) { return false; } length = shortValue; // Read the key path string if (!stateObj.TryReadString(length, out keyPath)) { return false; } // Read the length of the string carrying the encryption algo if (!stateObj.TryReadByte(out algorithmLength)) { return false; } length = (int)algorithmLength; // Read the string carrying the encryption algo (eg. RSA_PKCS_OAEP) if (!stateObj.TryReadString(length, out algorithmName)) { return false; } // Add this encrypted CEK blob to our list of encrypted values for the CEK entry.Add(encryptedCek, databaseId: dbId, cekId: keyId, cekVersion: keyVersion, cekMdVersion: keyMDVersion, keyPath: keyPath, keyStoreName: keyStoreName, algorithmName: algorithmName); } return true; }
internal bool TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) { Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!"); metaData = null; _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns, null); int[] indexMap = new int[cColumns]; if (!stateObj.TryReadUInt16(out altMetaDataSet.id)) { return false; } byte byCols; if (!stateObj.TryReadByte(out byCols)) { return false; } while (byCols > 0) { if (!stateObj.TrySkipBytes(2)) { // ignore ColNum ... return false; } byCols--; } // pass 1, read the meta data off the wire for (int i = 0; i < cColumns; i++) { // internal meta data class _SqlMetaData col = altMetaDataSet[i]; if (!stateObj.TryReadByte(out col.op)) { return false; } if (!stateObj.TryReadUInt16(out col.operand)) { return false; } // TCE is not applicable to AltMetadata. if (!TryCommonProcessMetaData(stateObj, col, null, fColMD: false, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Disabled)) { return false; } if (ADP.IsEmpty(col.column)) { // create column name from op switch (col.op) { case TdsEnums.AOPAVG: col.column = "avg"; break; case TdsEnums.AOPCNT: col.column = "cnt"; break; case TdsEnums.AOPCNTB: col.column = "cntb"; break; case TdsEnums.AOPMAX: col.column = "max"; break; case TdsEnums.AOPMIN: col.column = "min"; break; case TdsEnums.AOPSUM: col.column = "sum"; break; case TdsEnums.AOPANY: col.column = "any"; break; case TdsEnums.AOPNOOP: col.column = "noop"; break; case TdsEnums.AOPSTDEV: col.column = "stdev"; break; case TdsEnums.AOPSTDEVP: col.column = "stdevp"; break; case TdsEnums.AOPVAR: col.column = "var"; break; case TdsEnums.AOPVARP: col.column = "varp"; break; } } indexMap[i] = i; } altMetaDataSet.indexMap = indexMap; altMetaDataSet.visibleColumns = cColumns; metaData = altMetaDataSet; return true; }
internal bool TryProcessTceCryptoMetadata (TdsParserStateObject stateObj, SqlMetaDataPriv col, SqlTceCipherInfoTable? cipherTable, SqlCommandColumnEncryptionSetting columnEncryptionSetting, bool isReturnValue) { Debug.Assert(isReturnValue == (cipherTable == null), "Ciphertable is not set iff this is a return value"); // Read the ordinal into cipher table ushort index = 0; UInt32 userType; // For return values there is not cipher table and no ordinal. if (cipherTable.HasValue) { if (!stateObj.TryReadUInt16(out index)) { return false; } // validate the index (ordinal passed) if (index >= cipherTable.Value.Size) { Bid.Trace("<sc.TdsParser.TryProcessTceCryptoMetadata|TCE> Incorrect ordinal received %d, max tab size: %d\n", index, cipherTable.Value.Size); throw SQL.ParsingErrorValue(ParsingErrorState.TceInvalidOrdinalIntoCipherInfoTable, index); } } // Read the user type if (!stateObj.TryReadUInt32(out userType)) { return false; } // Read the base TypeInfo col.baseTI = new SqlMetaDataPriv(); if (!TryProcessTypeInfo(stateObj, col.baseTI, userType)) { return false; } // Read the cipher algorithm Id byte cipherAlgorithmId; if (!stateObj.TryReadByte(out cipherAlgorithmId)) { return false; } string cipherAlgorithmName = null; if (TdsEnums.CustomCipherAlgorithmId == cipherAlgorithmId) { // Custom encryption algorithm, read the name byte nameSize; if (!stateObj.TryReadByte(out nameSize)) { return false; } if (!stateObj.TryReadString(nameSize, out cipherAlgorithmName)) { return false; } } // Read Encryption Type. byte encryptionType; if (!stateObj.TryReadByte(out encryptionType)) { return false; } // Read Normalization Rule Version. byte normalizationRuleVersion; if (!stateObj.TryReadByte(out normalizationRuleVersion)) { return false; } Debug.Assert(col.cipherMD == null, "col.cipherMD should be null in TryProcessTceCryptoMetadata."); // Check if TCE is enable and if it is set the crypto MD for the column. // TCE is enabled if the command is set to enabled or to resultset only and this is not a return value // or if it is set to use connection setting and the connection has TCE enabled. if ((columnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled || (columnEncryptionSetting == SqlCommandColumnEncryptionSetting.ResultSetOnly && !isReturnValue)) || (columnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && _connHandler != null && _connHandler.ConnectionOptions != null && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled)) { col.cipherMD = new SqlCipherMetadata(cipherTable.HasValue ? (SqlTceCipherInfoEntry?)cipherTable.Value[index] : null, index, cipherAlgorithmId: cipherAlgorithmId, cipherAlgorithmName: cipherAlgorithmName, encryptionType: encryptionType, normalizationRuleVersion: normalizationRuleVersion); } else { // If TCE is disabled mark the MD as not encrypted. col.isEncrypted = false; } return true; }
// assumes that the current position is at the start of an altrow! internal bool TryGetAltRowId(TdsParserStateObject stateObj, out int id) { byte token; if (!stateObj.TryReadByte(out token)) { // skip over ALTROW token id = 0; return false; } Debug.Assert((token == TdsEnums.SQLALTROW), ""); // Start a fresh row - disable NBC since Alt Rows are never compressed if (!stateObj.TryStartNewRow(isNullCompressed: false)) { id = 0; return false; } ushort shortId; if (!stateObj.TryReadUInt16(out shortId)) { id = 0; return false; } id = shortId; return true; }
// Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, out bool dataReady) { Debug.Assert((SniContext.Undefined != stateObj.SniContext) && // SniContext must not be Undefined ((stateObj._attentionSent) || ((SniContext.Snix_Execute != stateObj.SniContext) && (SniContext.Snix_SendRows != stateObj.SniContext))), // SniContext should not be Execute or SendRows unless attention was sent (and, therefore, we are looking for an ACK) String.Format("Unexpected SniContext on call to TryRun; SniContext={0}", stateObj.SniContext)); if (TdsParserState.Broken == State || TdsParserState.Closed == State) { dataReady = true; return true; // Just in case this is called in a loop, expecting data to be returned. } dataReady = false; do { // If there is data ready, but we didn't exit the loop, then something is wrong Debug.Assert(!dataReady, "dataReady not expected - did we forget to skip the row?"); if (stateObj._internalTimeout) { runBehavior = RunBehavior.Attention; } if (TdsParserState.Broken == State || TdsParserState.Closed == State) break; // jump out of the loop if the state is already broken or closed. if (!stateObj._accumulateInfoEvents && (stateObj._pendingInfoEvents != null)) { if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) { SqlConnection connection = null; if (_connHandler != null) connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref // We are omitting checks for error.Class in the code below (see processing of INFO) since we know (and assert) that error class // error.Class < TdsEnums.MIN_ERROR_CLASS for info message. // Also we know that TdsEnums.MIN_ERROR_CLASS<TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS if ((connection != null) && connection.FireInfoMessageEventOnUserErrors) { foreach (SqlError error in stateObj._pendingInfoEvents) FireInfoMessageEvent(connection, stateObj, error); } else foreach (SqlError error in stateObj._pendingInfoEvents) stateObj.AddWarning(error); } stateObj._pendingInfoEvents = null; } byte token; if (!stateObj.TryReadByte(out token)) { return false; } if (!IsValidTdsToken(token)) { Debug.Assert(false, String.Format((IFormatProvider)null, "unexpected token; token = {0,-2:X2}", token)); _state = TdsParserState.Broken; _connHandler.BreakConnection(); throw SQL.ParsingError(); } int tokenLength; if (!TryGetTokenLength(token, stateObj, out tokenLength)) { return false; } switch (token) { case TdsEnums.SQLERROR: case TdsEnums.SQLINFO: { if (token == TdsEnums.SQLERROR) { stateObj._errorTokenReceived = true; // Keep track of the fact error token was received - for Done processing. } SqlError error; if (!TryProcessError(token, stateObj, out error)) { return false; } if (token == TdsEnums.SQLINFO && stateObj._accumulateInfoEvents) { Debug.Assert(error.Class < TdsEnums.MIN_ERROR_CLASS, "INFO with class > TdsEnums.MIN_ERROR_CLASS"); if (stateObj._pendingInfoEvents == null) stateObj._pendingInfoEvents = new List<SqlError>(); stateObj._pendingInfoEvents.Add(error); stateObj._syncOverAsync = true; break; } if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) { // If FireInfoMessageEventOnUserErrors is true, we have to fire event without waiting. // Otherwise we can go ahead and add it to errors/warnings collection. SqlConnection connection = null; if (_connHandler != null) connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref if ((connection != null) && (connection.FireInfoMessageEventOnUserErrors == true) && (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS)) { // Fire SqlInfoMessage here FireInfoMessageEvent(connection, stateObj, error); } else { // insert error/info into the appropriate exception - warning if info, exception if error if (error.Class < TdsEnums.MIN_ERROR_CLASS) { stateObj.AddWarning(error); } else if (error.Class < TdsEnums.FATAL_ERROR_CLASS) { // Continue results processing for all non-fatal errors (<20) stateObj.AddError(error); // Add it to collection - but do NOT change run behavior UNLESS // we are in an ExecuteReader call - at which time we will be throwing // anyways so we need to consume all errors. This is not the case // if we have already given out a reader. If we have already given out // a reader we need to throw the error but not halt further processing. We used to // halt processing. if (null != dataStream) { if (!dataStream.IsInitialized) { runBehavior = RunBehavior.UntilDone; } } } else { stateObj.AddError(error); // Else we have a fatal error and we need to change the behavior // since we want the complete error information in the exception. // Besides - no further results will be received. runBehavior = RunBehavior.UntilDone; } } } else if (error.Class >= TdsEnums.FATAL_ERROR_CLASS) { stateObj.AddError(error); } break; } case TdsEnums.SQLCOLINFO: { if (null != dataStream) { _SqlMetaDataSet metaDataSet; if (!TryProcessColInfo(dataStream.MetaData, dataStream, stateObj, out metaDataSet)) { return false; } if (!dataStream.TrySetMetaData(metaDataSet, false)) { return false; } dataStream.BrowseModeInfoConsumed = true; } else { // no dataStream if (!stateObj.TrySkipBytes(tokenLength)) { return false; } } break; } case TdsEnums.SQLDONE: case TdsEnums.SQLDONEPROC: case TdsEnums.SQLDONEINPROC: { // RunBehavior can be modified if (!TryProcessDone(cmdHandler, dataStream, ref runBehavior, stateObj)) { return false; } if ((token == TdsEnums.SQLDONEPROC) && (cmdHandler != null)) { cmdHandler.OnDoneProc(); } break; } case TdsEnums.SQLORDER: { // don't do anything with the order token so read off the pipe if (!stateObj.TrySkipBytes(tokenLength)) { return false; } break; } case TdsEnums.SQLALTMETADATA: { stateObj.CloneCleanupAltMetaDataSetArray(); if (stateObj._cleanupAltMetaDataSetArray == null) { // create object on demand (lazy creation) stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection(); } _SqlMetaDataSet cleanupAltMetaDataSet; if (!TryProcessAltMetaData(tokenLength, stateObj, out cleanupAltMetaDataSet)) { return false; } stateObj._cleanupAltMetaDataSetArray.SetAltMetaData(cleanupAltMetaDataSet); if (null != dataStream) { byte metadataConsumedByte; if (!stateObj.TryPeekByte(out metadataConsumedByte)) { return false; } if (!dataStream.TrySetAltMetaDataSet(cleanupAltMetaDataSet, (TdsEnums.SQLALTMETADATA != metadataConsumedByte))) { return false; } } break; } case TdsEnums.SQLALTROW: { if (!stateObj.TryStartNewRow(isNullCompressed: false)) { // altrows are not currently null compressed return false; } // read will call run until dataReady. Must not read any data if returnimmetiately set if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) { ushort altRowId; if (!stateObj.TryReadUInt16(out altRowId)) { // get altRowId return false; } if (!TrySkipRow(stateObj._cleanupAltMetaDataSetArray.GetAltMetaData(altRowId), stateObj)) { // skip altRow return false; } } else { dataReady = true; } break; } case TdsEnums.SQLENVCHANGE: { // ENVCHANGE must be processed synchronously (since it can modify the state of many objects) stateObj._syncOverAsync = true; SqlEnvChange[] env; if (!TryProcessEnvChange(tokenLength, stateObj, out env)) { return false; } for (int ii = 0; ii < env.Length; ii++) { if (env[ii] != null && !this.Connection.IgnoreEnvChange) { switch (env[ii].type) { case TdsEnums.ENV_BEGINTRAN: // When we get notification from the server of a new // transaction, we move any pending transaction over to // the current transaction, then we store the token in it. // if there isn't a pending transaction, then it's either // a TSQL transaction or a distributed transaction. Debug.Assert(null == _currentTransaction, "non-null current transaction with an ENV Change"); _currentTransaction = _pendingTransaction; _pendingTransaction = null; if (null != _currentTransaction) { _currentTransaction.TransactionId = env[ii].newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. } else { TransactionType transactionType = TransactionType.LocalFromTSQL; _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue); } if (null != _statistics && !_statisticsIsInTransaction) { _statistics.SafeIncrement(ref _statistics._transactions); } _statisticsIsInTransaction = true; break; case TdsEnums.ENV_COMMITTRAN: // SQLHOT 483 // Must clear the retain id if the server-side transaction ends by anything other // than rollback. goto case TdsEnums.ENV_ROLLBACKTRAN; case TdsEnums.ENV_ROLLBACKTRAN: // When we get notification of a completed transaction // we null out the current transaction. if (null != _currentTransaction) { #if DEBUG // Check null for case where Begin and Rollback obtained in the same message. if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) { Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!"); } #endif if (TdsEnums.ENV_COMMITTRAN == env[ii].type) { _currentTransaction.Completed(TransactionState.Committed); } else if (TdsEnums.ENV_ROLLBACKTRAN == env[ii].type) { // Hold onto transaction id if distributed tran is rolled back. This must // be sent to the server on subsequent executions even though the transaction // is considered to be rolled back. _currentTransaction.Completed(TransactionState.Aborted); } else { _currentTransaction.Completed(TransactionState.Unknown); } _currentTransaction = null; } _statisticsIsInTransaction = false; break; case TdsEnums.ENV_ENLISTDTC: case TdsEnums.ENV_DEFECTDTC: case TdsEnums.ENV_TRANSACTIONENDED: Debug.Assert(false, "Should have thrown if DTC token encountered"); break; default: _connHandler.OnEnvChange(env[ii]); break; } } } break; } case TdsEnums.SQLLOGINACK: { SqlLoginAck ack; if (!TryProcessLoginAck(stateObj, out ack)) { return false; } _connHandler.OnLoginAck(ack); break; } case TdsEnums.SQLFEATUREEXTACK: { if (!TryProcessFeatureExtAck(stateObj)) { return false; } break; } case TdsEnums.SQLSESSIONSTATE: { if (!TryProcessSessionState(stateObj, tokenLength, _connHandler._currentSessionData)) { return false; } break; } case TdsEnums.SQLCOLMETADATA: { if (tokenLength != TdsEnums.VARNULL) { _SqlMetaDataSet metadata; if (!TryProcessMetaData(tokenLength, stateObj, out metadata)) { return false; } stateObj._cleanupMetaData = metadata; } else { if (cmdHandler != null) { stateObj._cleanupMetaData = cmdHandler.MetaData; } } if (null != dataStream) { byte peekedToken; if (!stateObj.TryPeekByte(out peekedToken)) { // temporarily cache next byte return false; } if (!dataStream.TrySetMetaData(stateObj._cleanupMetaData, (TdsEnums.SQLTABNAME == peekedToken || TdsEnums.SQLCOLINFO == peekedToken))) { return false; } } else if (null != bulkCopyHandler) { bulkCopyHandler.SetMetaData(stateObj._cleanupMetaData); } break; } case TdsEnums.SQLROW: case TdsEnums.SQLNBCROW: { Debug.Assert(stateObj._cleanupMetaData != null, "Reading a row, but the metadata is null"); if (token == TdsEnums.SQLNBCROW) { if (!stateObj.TryStartNewRow(isNullCompressed: true, nullBitmapColumnsCount: stateObj._cleanupMetaData.Length)) { return false; } } else { if (!stateObj.TryStartNewRow(isNullCompressed: false)) { return false; } } if (null != bulkCopyHandler) { if (!TryProcessRow(stateObj._cleanupMetaData, bulkCopyHandler.CreateRowBuffer(), bulkCopyHandler.CreateIndexMap(), stateObj)) { return false; } } else if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) { if (!TrySkipRow(stateObj._cleanupMetaData, stateObj)) { // skip rows return false; } } else { dataReady = true; } if (_statistics != null) { _statistics.WaitForDoneAfterRow = true; } break; } case TdsEnums.SQLRETURNSTATUS: int status; if (!stateObj.TryReadInt32(out status)) { return false; } if (cmdHandler != null) { cmdHandler.OnReturnStatus(status); } break; case TdsEnums.SQLRETURNVALUE: { SqlReturnValue returnValue; if (!TryProcessReturnValue(tokenLength, stateObj, out returnValue)) { return false; } if (cmdHandler != null) { cmdHandler.OnReturnValue(returnValue); } break; } case TdsEnums.SQLSSPI: { // token length is length of SSPI data - call ProcessSSPI with it Debug.Assert(stateObj._syncOverAsync, "ProcessSSPI does not support retry, do not attempt asynchronously"); stateObj._syncOverAsync = true; ProcessSSPI(tokenLength); break; } case TdsEnums.SQLTABNAME: { { if (!stateObj.TrySkipBytes(tokenLength)) { return false; } } break; } default: Debug.Assert(false, "Unhandled token: " + token.ToString(CultureInfo.InvariantCulture)); break; } Debug.Assert(stateObj._pendingData || !dataReady, "dataReady is set, but there is no pending data"); } // Loop while data pending & runbehavior not return immediately, OR // if in attention case, loop while no more pending data & attention has not yet been // received. while ((stateObj._pendingData && (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior))) || (!stateObj._pendingData && stateObj._attentionSent && !stateObj._attentionReceived)); #if DEBUG if ((stateObj._pendingData) && (!dataReady)) { byte token; if (!stateObj.TryPeekByte(out token)) { return false; } Debug.Assert(IsValidTdsToken(token), string.Format("DataReady is false, but next token is not valid: {0,-2:X2}", token)); } #endif if (!stateObj._pendingData) { if (null != CurrentTransaction) { CurrentTransaction.Activate(); } } // if we recieved an attention (but this thread didn't send it) then // we throw an Operation Cancelled error if (stateObj._attentionReceived) { // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close // Spin until SendAttention has cleared _attentionSending, this prevents a race condition between receiving the attention ACK and setting _attentionSent SpinWait.SpinUntil(() => !stateObj._attentionSending); Debug.Assert(stateObj._attentionSent, "Attention ACK has been received without attention sent"); if (stateObj._attentionSent) { // Reset attention state. stateObj._attentionSent = false; stateObj._attentionReceived = false; if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior) && !stateObj._internalTimeout) { // Add attention error to collection - if not RunBehavior.Clean! stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0)); } } } if (stateObj.HasErrorOrWarning) { ThrowExceptionAndWarning(stateObj); } 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; }
// // returns the token length of the token or tds type // Returns -1 for partially length prefixed (plp) types for metadata info. // DOES NOT handle plp data streams correctly!!! // Plp data streams length information should be obtained from GetDataLength // internal bool TryGetTokenLength(byte token, TdsParserStateObject stateObj, out int tokenLength) { Debug.Assert(token != 0, "0 length token!"); switch (token) { // rules about SQLLenMask no longer apply to new tokens (as of 7.4) case TdsEnums.SQLFEATUREEXTACK: tokenLength = -1; return true; case TdsEnums.SQLSESSIONSTATE: return stateObj.TryReadInt32(out tokenLength); } { if (token == TdsEnums.SQLUDT) { // special case for UDTs tokenLength = -1; // Should we return -1 or not call GetTokenLength for UDTs? return true; } else if (token == TdsEnums.SQLRETURNVALUE) { tokenLength = -1; // In Yukon, the RETURNVALUE token stream no longer has length return true; } else if (token == TdsEnums.SQLXMLTYPE) { ushort value; if (!stateObj.TryReadUInt16(out value)) { tokenLength = 0; return false; } tokenLength = (int)value; Debug.Assert(tokenLength == TdsEnums.SQL_USHORTVARMAXLEN, "Invalid token stream for xml datatype"); return true; } } switch (token & TdsEnums.SQLLenMask) { case TdsEnums.SQLFixedLen: tokenLength = ((0x01 << ((token & 0x0c) >> 2))) & 0xff; return true; case TdsEnums.SQLZeroLen: tokenLength = 0; return true; case TdsEnums.SQLVarLen: case TdsEnums.SQLVarCnt: if (0 != (token & 0x80)) { ushort value; if (!stateObj.TryReadUInt16(out value)) { tokenLength = 0; return false; } tokenLength = value; return true; } else if (0 == (token & 0x0c)) { if (!stateObj.TryReadInt32(out tokenLength)) { return false; } return true; } else { byte value; if (!stateObj.TryReadByte(out value)) { tokenLength = 0; return false; } tokenLength = value; return true; } default: Debug.Assert(false, "Unknown token length!"); tokenLength = 0; return true; } }
private bool TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col) { byte byteLen; UInt32 userType; // read user type - 4 bytes Yukon, 2 backwards if (IsYukonOrNewer) { if (!stateObj.TryReadUInt32(out userType)) { return false; } } else { ushort userTypeShort; if (!stateObj.TryReadUInt16(out userTypeShort)) { return false; } userType = userTypeShort; } // read flags and set appropriate flags in structure byte flags; if (!stateObj.TryReadByte(out flags)) { return false; } col.updatability = (byte)((flags & TdsEnums.Updatability) >> 2); col.isNullable = (TdsEnums.Nullable == (flags & TdsEnums.Nullable)); col.isIdentity = (TdsEnums.Identity == (flags & TdsEnums.Identity)); // read second byte of column metadata flags if (!stateObj.TryReadByte(out flags)) { return false; } col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet)); byte tdsType; if (!stateObj.TryReadByte(out tdsType)) { return false; } if (tdsType == TdsEnums.SQLXMLTYPE) col.length = TdsEnums.SQL_USHORTVARMAXLEN; //Use the same length as other plp datatypes else if (IsVarTimeTds(tdsType)) col.length = 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN else if (tdsType == TdsEnums.SQLDATE) { col.length = 3; } else { if (!TryGetTokenLength(tdsType, stateObj, out col.length)) { return false; } } col.metaType = MetaType.GetSqlDataType(tdsType, userType, col.length); col.type = col.metaType.SqlDbType; // If sphinx, do not change to nullable type if (_isShiloh) col.tdsType = (col.isNullable ? col.metaType.NullableType : col.metaType.TDSType); else col.tdsType = tdsType; if (_isYukon) { if (TdsEnums.SQLUDT == tdsType) { if (!TryProcessUDTMetaData((SqlMetaDataPriv) col, stateObj)) { return false; } } if (col.length == TdsEnums.SQL_USHORTVARMAXLEN) { Debug.Assert(tdsType == TdsEnums.SQLXMLTYPE || tdsType == TdsEnums.SQLBIGVARCHAR || tdsType == TdsEnums.SQLBIGVARBINARY || tdsType == TdsEnums.SQLNVARCHAR || tdsType == TdsEnums.SQLUDT, "Invalid streaming datatype"); col.metaType = MetaType.GetMaxMetaTypeFromMetaType(col.metaType); Debug.Assert(col.metaType.IsLong, "Max datatype not IsLong"); col.length = Int32.MaxValue; if (tdsType == TdsEnums.SQLXMLTYPE) { byte schemapresent; if (!stateObj.TryReadByte(out schemapresent)) { return false; } if ((schemapresent & 1) != 0) { if (!stateObj.TryReadByte(out byteLen)) { return false; } if (byteLen != 0) { if (!stateObj.TryReadString(byteLen, out col.xmlSchemaCollectionDatabase)) { return false; } } if (!stateObj.TryReadByte(out byteLen)) { return false; } if (byteLen != 0) { if (!stateObj.TryReadString(byteLen, out col.xmlSchemaCollectionOwningSchema)) { return false; } } short shortLen; if (!stateObj.TryReadInt16(out shortLen)) { return false; } if (byteLen != 0) { if (!stateObj.TryReadString(shortLen, out col.xmlSchemaCollectionName)) { return false; } } } } } } if (col.type == SqlDbType.Decimal) { if (!stateObj.TryReadByte(out col.precision)) { return false; } if (!stateObj.TryReadByte(out col.scale)) { return false; } } if (col.metaType.IsVarTime) { if (!stateObj.TryReadByte(out col.scale)) { return false; } Debug.Assert(0 <= col.scale && col.scale <= 7); // calculate actual column length here // switch (col.metaType.SqlDbType) { case SqlDbType.Time: col.length = MetaType.GetTimeSizeFromScale(col.scale); break; case SqlDbType.DateTime2: // Date in number of days (3 bytes) + time col.length = 3 + MetaType.GetTimeSizeFromScale(col.scale); break; case SqlDbType.DateTimeOffset: // Date in days (3 bytes) + offset in minutes (2 bytes) + time col.length = 5 + MetaType.GetTimeSizeFromScale(col.scale); break; default: Debug.Assert(false, "Unknown VariableTime type!"); break; } } // read the collation for 7.x servers if (_isShiloh && col.metaType.IsCharType && (tdsType != TdsEnums.SQLXMLTYPE)) { if (!TryProcessCollation(stateObj, out col.collation)) { return false; } int codePage = GetCodePage(col.collation, stateObj); if (codePage == _defaultCodePage) { col.codePage = _defaultCodePage; col.encoding = _defaultEncoding; } else { col.codePage = codePage; col.encoding = System.Text.Encoding.GetEncoding(col.codePage); } } if (col.metaType.IsLong && !col.metaType.IsPlp) { if (_isYukon) { int unusedLen = 0xFFFF; //We ignore this value if (!TryProcessOneTable(stateObj, ref unusedLen, out col.multiPartTableName)) { return false; } } else { ushort shortLen; if (!stateObj.TryReadUInt16(out shortLen)) { return false; } string tableName; if (!stateObj.TryReadString(shortLen, out tableName)) { return false; } // with Sql2000 this is returned as an unquoted mix of catalog.owner.table // all of which may contain "." and unable to parse correctly from the string alone // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios col.multiPartTableName = new MultiPartTableName(tableName); } } if (!stateObj.TryReadByte(out byteLen)) { return false; } if (!stateObj.TryReadString(byteLen, out col.column)) { return false; } // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings. // We only need to fire this event when we actually have a meta data stream with 0 or more rows. stateObj._receivedColMetaData = true; return true; }