Example #1
0
        //
        // 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;
            }
        }
Example #2
0
        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;
        }
Example #3
0
        // @devnote: length should be size of decimal without the sign
        // @devnote: sign should have already been read off the wire
        private bool TryReadDecimalBits(int length, TdsParserStateObject stateObj, out int[] bits)
        {
            bits = stateObj._decimalBits; // used alloc'd array if we have one already
            int i;

            if (null == bits)
                bits = new int[4];
            else
            {
                for (i = 0; i < bits.Length; i++)
                    bits[i] = 0;
            }

            Debug.Assert((length > 0) &&
                         (length <= TdsEnums.MAX_NUMERIC_LEN - 1) &&
                         (length % 4 == 0), "decimal should have 4, 8, 12, or 16 bytes of data");

            int decLength = length >> 2;

            for (i = 0; i < decLength; i++)
            {
                // up to 16 bytes of data following the sign byte
                if (!stateObj.TryReadInt32(out bits[i]))
                {
                    return false;
                }
            }

            return true;
        }
Example #4
0
        private bool TryProcessSessionState(TdsParserStateObject stateObj, int length, SessionData sdata)
        {
            if (length < 5)
            {
                throw SQL.ParsingError();
            }
            UInt32 seqNum;
            if (!stateObj.TryReadUInt32(out seqNum))
            {
                return false;
            }
            if (seqNum == UInt32.MaxValue)
            {
                _connHandler.DoNotPoolThisConnection();
            }
            byte status;
            if (!stateObj.TryReadByte(out status))
            {
                return false;
            }
            if (status > 1)
            {
                throw SQL.ParsingError();
            }
            bool recoverable = status != 0;
            length -= 5;
            while (length > 0)
            {
                byte stateId;
                if (!stateObj.TryReadByte(out stateId))
                {
                    return false;
                }
                int stateLen;
                byte stateLenByte;
                if (!stateObj.TryReadByte(out stateLenByte))
                {
                    return false;
                }
                if (stateLenByte < 0xFF)
                {
                    stateLen = stateLenByte;
                }
                else
                {
                    if (!stateObj.TryReadInt32(out stateLen))
                    {
                        return false;
                    }
                }
                byte[] buffer = null;
                lock (sdata._delta)
                {
                    if (sdata._delta[stateId] == null)
                    {
                        buffer = new byte[stateLen];
                        sdata._delta[stateId] = new SessionStateRecord { _version = seqNum, _dataLength = stateLen, _data = buffer, _recoverable = recoverable };
                        sdata._deltaDirty = true;
                        if (!recoverable)
                        {
                            checked { sdata._unrecoverableStatesCount++; }
                        }
                    }
                    else
                    {
                        if (sdata._delta[stateId]._version <= seqNum)
                        {
                            SessionStateRecord sv = sdata._delta[stateId];
                            sv._version = seqNum;
                            sv._dataLength = stateLen;
                            if (sv._recoverable != recoverable)
                            {
                                if (recoverable)
                                {
                                    Debug.Assert(sdata._unrecoverableStatesCount > 0, "Unrecoverable states count >0");
                                    sdata._unrecoverableStatesCount--;
                                }
                                else
                                {
                                    checked { sdata._unrecoverableStatesCount++; }
                                }
                                sv._recoverable = recoverable;
                            }
                            buffer = sv._data;
                            if (buffer.Length < stateLen)
                            {
                                buffer = new byte[stateLen];
                                sv._data = buffer;
                            }
                        }
                    }
                }
                if (buffer != null)
                {
                    if (!stateObj.TryReadByteArray(buffer, 0, stateLen))
                    {
                        return false;
                    }
                }
                else
                {
                    if (!stateObj.TrySkipBytes(stateLen))
                        return false;
                }

                if (stateLenByte < 0xFF)
                {
                    length -= 2 + stateLen;
                }
                else
                {
                    length -= 6 + stateLen;
                }
            }
            sdata.AssertUnrecoverableStateCountIsCorrect();

            return true;
        }
Example #5
0
        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;
        }
Example #6
0
        // 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;
        }
Example #7
0
        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;
        }
Example #8
0
        /// <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;
        }
Example #9
0
        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;
            }

            if (_isYukon) {
                long longCount;
                if (!stateObj.TryReadInt64(out longCount)) {
                    return false;
                }
                count = (int) longCount;
            }
            else {
                if (!stateObj.TryReadInt32(out count)) {
                    return false;
                }
                // 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 4 bytes
                // But don't try to read beyond the TDS stream in this case, because it generates errors if login failed.
                if ( _state == TdsParserState.OpenNotLoggedIn) {
                    // Login incomplete, if we are reading from Yukon we need to read another int
                    if (stateObj._inBytesRead > stateObj._inBytesUsed) {
                        byte b;
                        if (!stateObj.TryPeekByte(out b)) {
                            return false;
                        }
                        if (b == 0) {
                            // This is an invalid token value
                            if (!stateObj.TryReadInt32(out count)) {
                                return false;
                            }
                        }
                    }
                }
            }

            // 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) {
                    if (cmd.IsDescribeParameterEncryptionRPCCurrentlyInProgress) {
                        // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
                        cmd.RowsAffectedByDescribeParameterEncryption = count;
                    }
                    else {
                        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) { // SQL BU DT 269516
                    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.
            // MDAC #93896.  Also, per Ashwin, 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) { // SQL BU DT 269516
                    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;
        }
Example #10
0
        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
                        Debug.Assert(!_isShiloh, "Received ENV_CHARSET on non 7.0 server!");
                        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:
                    case TdsEnums.ENV_ENLISTDTC:
                    case TdsEnums.ENV_DEFECTDTC:
                    case TdsEnums.ENV_TRANSACTIONENDED:
                        Debug.Assert(_isYukon, "Received new ENVCHANGE transaction/DTC token on pre 9.0 server!");

                        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_PROMOTETRANSACTION:
                        Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!");

                        if (!stateObj.TryReadInt32(out env.newLength)) { // new value has 4 byte length
                            return false;
                        }
                        env.newBinValue = new byte[env.newLength];
                        if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) { // read new value with 4 byte length
                            return false;
                        }

                        if (!stateObj.TryReadByte(out byteLength)) {
                            return false;
                        }
                        env.oldLength = byteLength;
                        Debug.Assert(0 == env.oldLength, "old length should be zero");

                        // env.length includes 1 byte for type token
                        env.length = 5 + env.newLength;
                        break;

                    case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS:
                    case TdsEnums.ENV_SPRESETCONNECTIONACK:
                        // 
                        Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!");
                        if (!TryReadTwoBinaryFields(env, stateObj)) {
                            return false;
                        }
                        break;
                        
                    case TdsEnums.ENV_USERINSTANCE:
                        Debug.Assert(!_isYukon, "Received ENV_USERINSTANCE on non 9.0 server!");
                        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;                        

                    default:
                        Debug.Assert(false, "Unknown environment change token: " + env.type);
                        break;
                }
                processedLength += env.length;
            }

            sqlEnvChange = envarray;
            return true;
        }