TdsParser.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / Data / System / Data / SqlClient / TdsParser.cs / 8 / TdsParser.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
// [....] 
//----------------------------------------------------------------------------- 

namespace System.Data.SqlClient { 
    using System;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Data.ProviderBase; 
    using System.Data.Sql;
    using System.Data.SqlTypes; 
    using System.Diagnostics; 
    using System.Globalization;
    using System.Runtime.CompilerServices; 
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    using System.Xml; 

    using MSS=Microsoft.SqlServer.Server; 
 
    // The TdsParser Object controls reading/writing to the netlib, parsing the tds,
    // and surfacing objects to the user. 
    sealed internal class TdsParser {
        private static int _objectTypeCount; // Bid counter
        internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 
        internal int ObjectID {
            get { 
                return _objectID; 
            }
        } 

#if DEBUG
        static public LocalDataStoreSlot ReliabilitySlot = Thread.AllocateDataSlot();       // contains whether we've setup for catching reliability work
#else 
        static public LocalDataStoreSlot ReliabilitySlot = null;                            // contains whether we've setup for catching reliability work
#endif //DEBUG 
 
        // Default state object for parser
        internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI. 

        // Also, default logical stateObj and connection for MARS over SNI.
        internal TdsParserStateObject _pMarsPhysicalConObj = null; // With MARS enabled, cached physical stateObj and connection.
 
        // Must keep this around - especially for callbacks on pre-MARS
        // ReadAsync which will return if physical connection broken! 
        // 
        // Per Instance TDS Parser variables
        // 

        // State variables
        internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
 
        private string _server = "";                            // name of server that the parser connects to
 
        internal volatile bool _fResetConnection = false;                 // flag to denote whether we are needing to call sp_reset 
        internal volatile bool _fPreserveTransaction = false;             // flag to denote whether we need to preserve the transaction when reseting
 
        private SqlCollation   _defaultCollation;                         // default collation from the server

        private int _defaultCodePage;
 
        private int _defaultLCID;
 
        internal Encoding _defaultEncoding = null;                  // for sql character data 

        private static EncryptionOptions    _sniSupportedEncryptionOption = SNILoadHandle.SingletonInstance.Options; 

        private EncryptionOptions           _encryptionOption             = _sniSupportedEncryptionOption;

        private SqlInternalTransaction      _currentTransaction; 
        private SqlInternalTransaction      _pendingTransaction;    // pending transaction for Yukon and beyond.
        // SQLHOT 483 
        //  need to hold on to the transaction id if distributed transaction merely rolls back without defecting. 
        private long                       _retainedTransactionId        = SqlInternalTransaction.NullTransactionId;
 
        // This counter is used for the entire connection to track the open result count for all
        // operations not under a transaction.
        private int _nonTransactedOpenResultCount = 0;
 
        // Connection reference
        private SqlInternalConnectionTds    _connHandler; 
 
        // Async/Mars variables
        private bool _fAsync = false; 

        private bool _fMARS = false;

        internal volatile bool _fAwaitingPreLogin = false; 

        internal volatile bool _fPreLoginErrorOccurred = false; 
 
        internal AutoResetEvent         _resetConnectionEvent = null;  // Used to serialize executes and call reset on first execute only.
 
        private TdsParserSessionPool _sessionPool = null;  // initialized only when we're a MARS parser.


        // Version variables 
        private bool _isShiloh = false; // set to true if we connect to a 8.0 server (SQL 2000) or later
 
        private bool _isShilohSP1 = false; // set to true if speaking to Shiloh SP1 or later 

        private bool _isYukon = false; // set to true if speaking to Yukon or later 

        private bool _isKatmai = false;

#if WINFSFunctionality 
        private bool _isWinFS = false; // set to true if speaking to WinFS or later
#endif 
 
        private byte[] _sniServerUserName = null;
 
        //

        // local exceptions to cache warnings and errors
        private SqlErrorCollection _errors; 

        private SqlErrorCollection _warnings; 
 
        // Local exceptions to cache warnings and errors for attention handling
        private SqlErrorCollection _attentionErrors; 

        private SqlErrorCollection _attentionWarnings;

        private object _ErrorCollectionLock = new object (); 

        // SqlStatistics 
        private SqlStatistics _statistics = null; 

        private bool _statisticsIsInTransaction = false; 

        //
        // STATIC TDS Parser variables
        // 

        private byte[] datetimeBuffer = new byte[10]; // cache for raw bytes of new Katmai Date/Time type 
 
        // NIC address caching
        private static byte[] s_nicAddress;             // cache the NIC address from the registry 

        // SSPI variables
        private static bool s_fSSPILoaded = false; // bool to indicate whether library has been loaded
 
        private volatile static UInt32 s_maxSSPILength = 0;     // variable to hold max SSPI data size, keep for token from server
 
        // textptr sequence 
        private static readonly byte[] s_longDataHeader = { 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
        private static object s_tdsParserLock = new object();

        // Various other statics
        private const int ATTENTION_TIMEOUT = 5000;  // internal attention timeout, in ticks 

        // XML metadata substitue sequence 
        private static readonly byte[] s_xmlMetadataSubstituteSequence = { 0xe7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }; 

#if NONETWORK 
// DO NOT REMOVE THIS CODE UNLESS YOU TALK WITH [....] FIRST
// WE USE THIS CODE TO CREATE HARD CODED PACKETS FOR PEFORMANCE
// TESTING!!!!
 
private int recv_packet_number=0;
 
// Put the output from the BUILDHARDCODEDPACKETCODE run here, adjust 
// the variable names, and add them to the array of packets below.
 
static private byte[][] recv_packet = { recv_packet_1, recv_packet_2 };

#endif //NONETWORK
 
#if OBJECT_BINDING
        // DataStream prototype 
        private ReadBehavior readBehavior = System.Data.SqlClient.ReadBehavior.AsRow; 
#endif
 
        internal TdsParser(bool MARS, bool fAsynchronous) {
            _fMARS = MARS; // may change during Connect to pre Yukon servers
            _fAsync = fAsynchronous;
            _physicalStateObj = new TdsParserStateObject(this); 
        }
 
        internal bool AsyncOn { 
            get {
                return _fAsync; 
            }
        }

        internal SqlInternalConnectionTds Connection { 
            get {
                return _connHandler; 
            } 
        }
 
        internal SqlInternalTransaction CurrentTransaction {
            get {
                return _currentTransaction;
            } 
            set {
                Debug.Assert(value == _currentTransaction 
                          || null == _currentTransaction 
                          || null == value
                          || (null != _currentTransaction && !_currentTransaction.IsLocal), "attempting to change current transaction?"); 

                // If there is currently a transaction active, we don't want to
                // change it; this can occur when there is a delegated transaction
                // and the user attempts to do an API begin transaction; in these 
                // cases, it's safe to ignore the set.
                if ((null == _currentTransaction && null != value) 
                  ||(null != _currentTransaction && null == value)) { 
                    _currentTransaction = value;
                } 
            }
        }

        internal int DefaultLCID { 
            get {
                return _defaultLCID; 
            } 
        }
 
        internal EncryptionOptions EncryptionOptions {
            get {
                return _encryptionOption;
            } 
            set {
                _encryptionOption = value; 
            } 
        }
 
        internal SqlErrorCollection Errors {
            get {
                lock (_ErrorCollectionLock) {
                    if (null == _errors) { 
                        _errors = new SqlErrorCollection();
                    } 
                    return _errors; 
                }
            } 
        }

        internal bool IsYukonOrNewer {
            get { 
                return _isYukon;
            } 
        } 

        internal bool IsKatmaiOrNewer { 
            get {
                return _isKatmai;
            }
        } 

#if WINFSFunctionality 
        internal bool IsWinFS { 
            get {
                return _isWinFS; 
            }
        }
#endif
 
        internal bool MARSOn {
            get { 
                return _fMARS; 
            }
        } 

        internal SqlInternalTransaction PendingTransaction {
            get {
                return _pendingTransaction; 
            }
            set { 
                Debug.Assert (null != value, "setting a non-null PendingTransaction?"); 
                _pendingTransaction = value;
            } 
        }

#if OBJECT_BINDING
        internal ReadBehavior ReadBehavior { 
            get {
                return readBehavior; 
            } 
            set {
                readBehavior = value; 
            }
        }
#endif
 
        internal string Server {
            get { 
                return _server; 
            }
        } 

        internal TdsParserState State {
            get {
                return _state; 
            }
            set { 
                _state = value; 
            }
        } 

        internal SqlStatistics Statistics {
            get {
                return _statistics; 
            }
            set { 
                _statistics = value; 
            }
        } 

        internal SqlErrorCollection Warnings {
            get {
                lock (_ErrorCollectionLock) { 
                    if (null == _warnings) {
                        _warnings = new SqlErrorCollection(); 
                    } 
                    return _warnings;
                } 
            }
        }

        internal int IncrementNonTransactedOpenResultCount() { 
            // IMPORTANT - this increments the connection wide open result count for all
            // operations not under a transaction!  Do not call if you intend to modify the 
            // count for a transaction! 
            Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
            int result = Interlocked.Increment(ref _nonTransactedOpenResultCount); 
            return result;
        }

        internal void DecrementNonTransactedOpenResultCount() { 
            // IMPORTANT - this decrements the connection wide open result count for all
            // operations not under a transaction!  Do not call if you intend to modify the 
            // count for a transaction! 
            Interlocked.Decrement(ref _nonTransactedOpenResultCount);
            Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state"); 
        }

        internal void ProcessPendingAck(TdsParserStateObject stateObj) {
            if (stateObj._attentionSent) { 
                ProcessAttention(stateObj);
            } 
        } 

        internal void Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, bool encrypt, bool trustServerCert, bool integratedSecurity, SqlConnection owningObject) { 
            if (_state != TdsParserState.Closed) {
                Debug.Assert(false, "TdsParser.Connect called on non-closed connection!");
                return;
            } 

            _connHandler = connHandler; 
 
            UInt32 sniStatus = SNILoadHandle.SingletonInstance.SNIStatus;
            if (sniStatus != TdsEnums.SNI_SUCCESS) { 
                Errors.Add(ProcessSNIError(_physicalStateObj));
                _physicalStateObj.Dispose();
                ThrowExceptionAndWarning(_physicalStateObj);
                Debug.Assert(false, "SNI returned status != success, but no error thrown?"); 
            }
 
            if (integratedSecurity) { 
                LoadSSPILibrary();
                // now allocate proper length of buffer 
                _sniServerUserName = new byte[s_maxSSPILength];
                Bid.Trace(" SSPI authentication\n");
            }
            else { 
                Bid.Trace(" SQL authentication\n");
            } 
 
            byte[] instanceName = null;
 
            _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire,
                        out instanceName, integratedSecurity, _sniServerUserName, false, _fAsync);

            if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { 
                Errors.Add(ProcessSNIError(_physicalStateObj));
 
                // Since connect failed, free the unmanaged connection memory. 
                // HOWEVER - only free this after the netlib error was processed - if you
                // don't, the memory for the connection object might not be accurate and thus 
                // a bad error could be returned (as it was when it was freed to early for me).
                _physicalStateObj.Dispose();
                Bid.Trace(" Login failure\n");
                ThrowExceptionAndWarning(_physicalStateObj); 
                Debug.Assert(false, "SNI returned status != success, but no error thrown?");
            } 
 
            _server = serverInfo.ResolvedServerName;
 
            if (null != connHandler.PoolGroupProviderInfo) {
                // If we are pooling, check to see if we were processing an
                // alias which has changed, which means we need to clean out
                // the pool. See Webdata 104293. 
                connHandler.PoolGroupProviderInfo.AliasCheck(serverInfo.ResolvedServerName);
            } 
            _state = TdsParserState.OpenNotLoggedIn; 
            _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfullWrite; // SQL BU DT 376766
            _physicalStateObj.TimeoutTime = timerExpire; 

            bool marsCapable = false;

            // 
            SendPreLoginHandshake(instanceName, encrypt);
 
            _physicalStateObj.SniContext = SniContext.Snix_PreLogin; 

            PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, out marsCapable); 

            // 1) Sphinx does not support pre-login handshakes, so the server will kill any
            // connection on which it receives a handshake on after open.
            // 2) Must verify instance name in pre-login handshake.  If we receive instance 
            // error (we did not connect to correct instance), we need to close the connection
            // and re-connect and in the process flush the netlib cache for this.  Stale 
            // cache should be the primary reason for this type of failure. 
            if (status == PreLoginHandshakeStatus.SphinxFailure) {
                _fMARS = false; // Turn off MARS, since we know it's not supported by 7.0! 

                // Release reference to Packet - so we don't incorrectly re-use with different connection.
                _physicalStateObj._sniPacket = null;
                _physicalStateObj.SniContext=SniContext.Snix_Connect; 
                _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, _sniServerUserName, false, _fAsync);
 
                if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { 
                    Errors.Add(ProcessSNIError(_physicalStateObj));
                    Bid.Trace(" Login failure\n"); 
                    ThrowExceptionAndWarning(_physicalStateObj);
                }
            }
            else if (status == PreLoginHandshakeStatus.InstanceFailure) { 
                _physicalStateObj.Dispose(); // Close previous connection
 
                // On Instance failure re-connect and flush SNI named instance cache. 
                _physicalStateObj.SniContext=SniContext.Snix_Connect;
                _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, _sniServerUserName, true, _fAsync); 

                if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) {
                    Errors.Add(ProcessSNIError(_physicalStateObj));
                    Bid.Trace(" Login failure\n"); 
                    ThrowExceptionAndWarning(_physicalStateObj);
                } 
 
                SendPreLoginHandshake(instanceName, encrypt);
                status = ConsumePreLoginHandshake(encrypt, trustServerCert, out marsCapable); 

                // Don't need to check for Sphinx failure, since we've already consumed
                // one pre-login packet and know we are connecting to Shiloh.
                if (status == PreLoginHandshakeStatus.InstanceFailure) { 
                    Bid.Trace(" Login failure\n");
                    throw SQL.InstanceFailure(); 
                } 
            }
 
            if (_fMARS && marsCapable) {
                // if user explictly disables mars or mars not supported, don't create the session pool
                _sessionPool = new TdsParserSessionPool(this);
            } 
            else {
                _fMARS = false; 
            } 
            return;
        } 

        internal void RemoveEncryption() {
            Debug.Assert(_encryptionOption == EncryptionOptions.LOGIN, "Invalid encryption option state");
 
            UInt32 error = 0;
 
            // Remove SSL (Encryption) SNI provider since we only wanted to encrypt login. 
            error = SNINativeMethodWrapper.SNIRemoveProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV);
            if (error != TdsEnums.SNI_SUCCESS) { 
                Errors.Add(ProcessSNIError(_physicalStateObj));
                ThrowExceptionAndWarning(_physicalStateObj);
            }
            // create a new packet encryption changes the internal packet size Bug# 228403 
            try {}   // EmptyTry/Finally to avoid FXCop violation
            finally { 
                _physicalStateObj._sniPacket.Dispose(); 
                _physicalStateObj._sniPacket = new SNIPacket(_physicalStateObj.Handle);
            } 
        }

        internal void EnableMars(string server) {
            if (_fMARS) { 
                // Cache physical stateObj and connection.
                _pMarsPhysicalConObj = _physicalStateObj; 
 
                UInt32 error = 0;
                UInt32 info = 0; 

                // Add SMUX (MARS) SNI provider.
                error = SNINativeMethodWrapper.SNIAddProvider(_pMarsPhysicalConObj.Handle, SNINativeMethodWrapper.ProviderEnum.SMUX_PROV, ref info);
 
                if (error != TdsEnums.SNI_SUCCESS) {
                    Errors.Add(ProcessSNIError(_physicalStateObj)); 
                    ThrowExceptionAndWarning(_physicalStateObj); 
                }
 
                // HACK HACK HACK - for Async only
                // Have to post read to intialize MARS - will get callback on this when connection goes
                // down or is closed.
 
                IntPtr temp = IntPtr.Zero;
 
                if (_fAsync) { 
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try {} finally { 
                        _pMarsPhysicalConObj.IncrementPendingCallbacks();

                        error = SNINativeMethodWrapper.SNIReadAsync(_pMarsPhysicalConObj.Handle, ref temp);
 
                        if (temp != IntPtr.Zero) {
                            // Be sure to release packet, otherwise it will be leaked by native. 
                            SNINativeMethodWrapper.SNIPacketRelease(temp); 
                        }
                    } 
                    Debug.Assert(IntPtr.Zero == temp, "unexpected syncReadPacket without corresponding SNIPacketRelease");
                    if (TdsEnums.SNI_SUCCESS_IO_PENDING != error) {
                        Debug.Assert(TdsEnums.SNI_SUCCESS != error, "Unexpected successfull read async on physical connection before enabling MARS!");
                        Errors.Add(ProcessSNIError(_physicalStateObj)); 
                        ThrowExceptionAndWarning(_physicalStateObj);
                    } 
                } 

                _physicalStateObj = CreateSession(); // Create and open default MARS stateObj and connection. 
            }
        }

        internal TdsParserStateObject CreateSession() { 
            TdsParserStateObject session = new TdsParserStateObject(this, (SNIHandle)_pMarsPhysicalConObj.Handle, _fAsync);
            if (Bid.AdvancedOn) { 
                Bid.Trace(" %d# created session %d\n", ObjectID, session.ObjectID); 
            }
            return session; 
        }

        internal TdsParserStateObject GetSession(object owner) {
            TdsParserStateObject session = null; 

            // 
 
            if (MARSOn) {
                session = _sessionPool.GetSession(owner); 

                Debug.Assert(!session._pendingData, "pending data on a pooled MARS session");
                if (Bid.AdvancedOn) {
                    Bid.Trace(" %d# getting session %d from pool\n", ObjectID, session.ObjectID); 
                }
            } 
            else { 
                session = _physicalStateObj;
                if (Bid.AdvancedOn) { 
                    Bid.Trace(" %d# getting physical session %d\n", ObjectID, session.ObjectID);
                }
            }
            Debug.Assert(session._outputPacketNumber==1, "The packet number is expected to be 1"); 
            return session;
        } 
 
        internal void PutSession(TdsParserStateObject session) {
            if (MARSOn) { 
                _sessionPool.PutSession(session);
            }
        }
 
        internal SNIHandle GetBestEffortCleanupTarget() {
            if (null != _physicalStateObj) { 
                return _physicalStateObj.Handle; 
            }
 
            return null;
        }

        private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) { 
            // PreLoginHandshake buffer consists of:
            // 1) Standard header, with type = MT_PRELOGIN 
            // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length) 
            // 3) Consecutive data blocks for each option
 
            // NOTE: packet data needs to be big endian - not the standard little endian used by
            // the rest of the parser.

            _physicalStateObj._outputMessageType = TdsEnums.MT_PRELOGIN; 

            // Initialize option offset into payload buffer 
            // 5 bytes for each option (1 byte length, 2 byte offset, 2 byte payload length) 
            int offset = (int)PreLoginOptions.NUMOPT * 5 + 1;
 
            byte[] payload = new byte[(int)PreLoginOptions.NUMOPT * 5 + TdsEnums.MAX_PRELOGIN_PAYLOAD_LENGTH];
            int payloadLength = 0;

            // 

 
            for (int option = (int)PreLoginOptions.VERSION; option < (int)PreLoginOptions.NUMOPT; option++) { 
                int optionDataSize = 0;
 
                // Fill in the option
                WriteByte((byte)option, _physicalStateObj);

                // Fill in the offset of the option data 
                WriteByte((byte)(offset & 0xff00), _physicalStateObj); // send upper order byte
                WriteByte((byte)(offset & 0x00ff), _physicalStateObj); // send lower order byte 
 
                switch (option) {
                    case (int)PreLoginOptions.VERSION: 
                        // For now, send 9.00.000 (Yukon RTM)
                        // Apparently, it doesn't matter what I send over. :)
                        // The server does not yet use it.
                        payload[payloadLength++] = 0x10; 
                        payload[payloadLength++] = 0x00;
                        payload[payloadLength++] = 0x00; 
                        payload[payloadLength++] = 0x00; 
                        payload[payloadLength++] = 0;
                        payload[payloadLength++] = 0; 
                        offset += 6;
                        optionDataSize = 6;
                        break;
 
                    case (int)PreLoginOptions.ENCRYPT:
                        if (_encryptionOption == EncryptionOptions.NOT_SUP) { 
                            // If OS doesn't support encryption, inform server not supported. 
                            payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
                        } 
                        else {
                            // Else, inform server of user request.
                            if (encrypt) {
                                payload[payloadLength] = (byte)EncryptionOptions.ON; 
                                _encryptionOption = EncryptionOptions.ON;
                            } 
                            else { 
                                payload[payloadLength] = (byte)EncryptionOptions.OFF;
                                _encryptionOption = EncryptionOptions.OFF; 
                            }
                        }

                        payloadLength += 1; 
                        offset += 1;
                        optionDataSize = 1; 
                        break; 

                    case (int)PreLoginOptions.INSTANCE: 
                        int i = 0;

                        while (instanceName[i] != 0) {
                            payload[payloadLength] = instanceName[i]; 
                            payloadLength++;
                            i++; 
                        } 

                        payload[payloadLength] = 0; // null terminate 
                        payloadLength++;
                        i++;

                        offset += i; 
                        optionDataSize = i;
                        break; 
 
                    case (int)PreLoginOptions.THREADID:
                        Int32 threadID = ADP.GetCurrentThreadId(); 

                        payload[payloadLength++] = (byte)(0xff000000 & threadID);
                        payload[payloadLength++] = (byte)(0x00ff0000 & threadID);
                        payload[payloadLength++] = (byte)(0x0000ff00 & threadID); 
                        payload[payloadLength++] = (byte)(0x000000ff & threadID);
                        offset += 4; 
                        optionDataSize = 4; 
                        break;
 
                    case (int)PreLoginOptions.MARS:
                        payload[payloadLength++] = (byte)(_fMARS ? 1 : 0);
                        offset += 1;
                        optionDataSize += 1; 
                        break;
 
                    default: 
                        Debug.Assert(false, "UNKNOWN option in SendPreLoginHandshake");
                        break; 
                }

                // Write data length
                WriteByte((byte)(optionDataSize & 0xff00), _physicalStateObj); 
                WriteByte((byte)(optionDataSize & 0x00ff), _physicalStateObj);
            } 
 
            // Write out last option - to let server know the second part of packet completed
            WriteByte((byte)PreLoginOptions.LASTOPT, _physicalStateObj); 

            // Write out payload
            WriteByteArray(payload, payloadLength, 0, _physicalStateObj);
 
            // Flush packet
            _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); 
        } 

        private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trustServerCert, out bool marsCapable) { 
            // int PRELOGIN_TIMEOUT  = 10;
            marsCapable = _fMARS; // Assign default value

            bool isYukonOrLater = false; 

            // Set bool for callback async read thread not to throw, but to set error flag 
            _fAwaitingPreLogin = true; 

            // 

            _physicalStateObj.ReadNetworkPacket();

            _fAwaitingPreLogin = false; 

            if (_physicalStateObj._inBytesRead == 0 || _fPreLoginErrorOccurred) { 
                if (encrypt) { 
                    // If encryption was required by client, but not connecting to
                    // 8.0 clients and beyond, fail to connect.  This is what netlibs do. 
                    // Pre-8.0 servers do not support encryption.
                    Errors.Add(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
                    _physicalStateObj.Dispose();
                    ThrowExceptionAndWarning(_physicalStateObj); 
                }
 
                // Sending pre-login to a 7.0 server will cause the server to kill 
                // the connection!!! Error and reconnect without sending prelogin packet.
                return PreLoginHandshakeStatus.SphinxFailure; 
            }

            // SEC
            byte[] payload = new byte[_physicalStateObj._inBytesRead - _physicalStateObj._inBytesUsed - _physicalStateObj._inputHeaderLen]; 

            _physicalStateObj.ReadByteArray(payload, 0, payload.Length); 
 
            if (payload[0] == 0xaa) {
                // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which 
                // is not supported.  SQL BU DT 296425
                throw SQL.InvalidSQLServerVersionUnknown();
            }
 
            int offset = 0;
            int payloadOffset = 0; 
            int payloadLength = 0; 
            int option = payload[offset++];
 
            while (option != (byte)PreLoginOptions.LASTOPT) {
                switch (option) {
                    case (int)PreLoginOptions.VERSION:
                        payloadOffset = payload[offset++] << 8 | payload[offset++]; 
                        payloadLength = payload[offset++] << 8 | payload[offset++];
 
                        byte majorVersion = payload[payloadOffset]; 
                        byte minorVersion = payload[payloadOffset + 1];
                        int level = (payload[payloadOffset + 2] << 8) | 
                                             payload[payloadOffset + 3];

                        isYukonOrLater = majorVersion >= 9;
                        if (!isYukonOrLater) { 
                            marsCapable = false;            // If pre-Yukon, MARS not supported.
                        } 
 
                        break;
 
                    case (int)PreLoginOptions.ENCRYPT:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++];
 
                        EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset];
 
                        /* internal enum EncryptionOptions { 
                            OFF,
                            ON, 
                            NOT_SUP,
                            REQ,
                            LOGIN
                        } */ 

                        switch (_encryptionOption) { 
                            case (EncryptionOptions.ON): 
                                if (serverOption == EncryptionOptions.NOT_SUP) {
                                    Errors.Add(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); 
                                    _physicalStateObj.Dispose();
                                    ThrowExceptionAndWarning(_physicalStateObj);
                                }
 
                                break;
 
                            case (EncryptionOptions.OFF): 
                                if (serverOption == EncryptionOptions.OFF) {
                                    // Only encrypt login. 
                                    _encryptionOption = EncryptionOptions.LOGIN;
                                }
                                else if (serverOption == EncryptionOptions.REQ) {
                                    // Encrypt all. 
                                    _encryptionOption = EncryptionOptions.ON;
                                } 
 
                                break;
 
                            case (EncryptionOptions.NOT_SUP):
                                if (serverOption == EncryptionOptions.REQ) {
                                    Errors.Add(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
                                    _physicalStateObj.Dispose(); 
                                    ThrowExceptionAndWarning(_physicalStateObj);
                                } 
 
                                break;
 
                            default:
                                Debug.Assert(false, "Invalid client encryption option detected");
                                break;
                        } 

                        if (_encryptionOption == EncryptionOptions.ON || 
                            _encryptionOption == EncryptionOptions.LOGIN) { 
                            UInt32 error = 0;
                            UInt32 info = ((encrypt && !trustServerCert) ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) 
                                | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);

                            // Add SSL (Encryption) SNI provider.
                            error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref info); 

                            if (error != TdsEnums.SNI_SUCCESS) { 
                                Errors.Add(ProcessSNIError(_physicalStateObj)); 
                                ThrowExceptionAndWarning(_physicalStateObj);
                            } 
        			// create a new packet encryption changes the internal packet size Bug# 228403
                        	try {} // EmptyTry/Finally to avoid FXCop violation
                            finally {
                                  _physicalStateObj._sniPacket.Dispose(); 
                                  _physicalStateObj._sniPacket = new SNIPacket(_physicalStateObj.Handle);
                             } 
                        } 

                        break; 

                    case (int)PreLoginOptions.INSTANCE:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++]; 

                        byte ERROR_INST = 0x1; 
                        byte instanceResult = payload[payloadOffset]; 

                        if (instanceResult == ERROR_INST) { 
                            // Check if server says ERROR_INST. That either means the cached info
                            // we used to connect is not valid or we connected to a named instance
                            // listening on default params.
                            return PreLoginHandshakeStatus.InstanceFailure; 
                        }
 
                        break; 

                    case (int)PreLoginOptions.THREADID: 
                        // DO NOTHING FOR THREADID
                        offset += 4;
                        break;
 
                    case (int)PreLoginOptions.MARS:
                        payloadOffset = payload[offset++] << 8 | payload[offset++]; 
                        payloadLength = payload[offset++] << 8 | payload[offset++]; 

                        marsCapable = (payload[payloadOffset] == 0 ? false : true); 

                        Debug.Assert(payload[payloadOffset] == 0 || payload[payloadOffset] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
                        break;
 
                    default:
                        Debug.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option); 
 
                        // DO NOTHING FOR THESE UNKNOWN OPTIONS
                        offset += 4; 

                        break;
                }
 
                if (offset < payload.Length) {
                    option = payload[offset++]; 
                } 
                else {
                    break; 
                }
            }

            return PreLoginHandshakeStatus.Successful; 
        }
 
        internal void Deactivate(bool connectionIsDoomed) { 
            // Called when the connection that owns us is deactivated.
 
            if (Bid.AdvancedOn) {
                Bid.Trace(" %d# deactivating\n", ObjectID);
            }
 
            if (Bid.StateDumpOn) {
                Bid.Trace(" %d#, %s\n", ObjectID, TraceString()); 
            } 

            if (MARSOn) { 
                _sessionPool.Deactivate();
            }

            Debug.Assert(connectionIsDoomed || null == _pendingTransaction, "pending transaction at disconnect?"); 

            if (!connectionIsDoomed && null != _physicalStateObj) { 
                if (_physicalStateObj._pendingData) { 
                    _physicalStateObj.CleanWire();
                } 

                if (_physicalStateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
                    _physicalStateObj.DecrementOpenResultCount();
                } 
            }
 
            // Any active, non-distributed transaction must be rolled back.  We 
            // need to wait for distributed transactions to be completed by the
            // transaction manager -- we don't want to automatically roll them 
            // back.
            //
            // Note that when there is a transaction delegated to this connection,
            // we will defer the deactivation of this connection until the 
            // transaction manager completes the transaction.
            SqlInternalTransaction currentTransaction = CurrentTransaction; 
 
            if (null != currentTransaction && currentTransaction.HasParentTransaction) {
                currentTransaction.CloseFromConnection(); 
                Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
            }

            Statistics = null; // must come after CleanWire or we won't count the stuff that happens there... 
        }
 
        // Used to close the connection and then free the memory allocated for the netlib connection. 
        internal void Disconnect() {
            if (null != _sessionPool) { 
                // MARSOn may be true, but _sessionPool not yet created
                _sessionPool.Dispose();
            }
 
            // Can close the connection if its open or broken
            if (_state != TdsParserState.Closed) { 
                //benign assert - the user could close the connection before consuming all the data 
                //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
 
                _state = TdsParserState.Closed;
                _physicalStateObj.SniContext = SniContext.Snix_Close;

                if (_fMARS) { 
                    try {
                        _physicalStateObj.Dispose(); 
 
                        if (null != _pMarsPhysicalConObj) {
                            // Not allocated until MARS is actually enabled in SNI. 
                            _pMarsPhysicalConObj.Dispose();
                        }
                    }
                    finally { 
                        _pMarsPhysicalConObj = null;
                    } 
                } 
                else {
                    _physicalStateObj.Dispose(); 
                }
#if DEBUG
                _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
#endif 
            }
        } 
 
        // Fires a single InfoMessageEvent
        private void FireInfoMessageEvent(TdsParserStateObject stateObj, SqlError error) { 

            string serverVersion = null;

            if (_state == TdsParserState.OpenLoggedIn) { 
                serverVersion = _connHandler.ServerVersion;
            } 
 
            SqlErrorCollection sqlErs = new SqlErrorCollection();
 
            sqlErs.Add(error);

            SqlException exc = SqlException.CreateException(sqlErs, serverVersion);
 
            _connHandler.Connection.OnInfoMessage(new SqlInfoMessageEventArgs(exc));
            return; 
        } 

        internal void DisconnectTransaction(SqlInternalTransaction internalTransaction) { 
            Debug.Assert(_currentTransaction != null && _currentTransaction == internalTransaction, "disconnecting different transaction");

            if (_currentTransaction != null && _currentTransaction == internalTransaction) {
                _currentTransaction = null; 
            }
        } 
 
        internal void RollbackOrphanedAPITransactions() {
            // Any active, non-distributed transaction must be rolled back. 
            SqlInternalTransaction currentTransaction = CurrentTransaction;

            if (null != currentTransaction && currentTransaction.HasParentTransaction && currentTransaction.IsOrphaned) {
                currentTransaction.CloseFromConnection(); 
                Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
            } 
        } 

 
        internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj) {
            // Lock the error collection
            // so the error collections are kept in a known state.
            // the error collections are modified here and in ProcessAttention. 
            // which can come in at any time
            lock (_ErrorCollectionLock) { 
                // This function should only be called when there was an error or warning.  If there aren't any 
                // errors, the handler will be called for the warning(s).  If there was an error, the warning(s) will
                // be copied to the end of the error collection so that the user may see all the errors and also the 
                // warnings that occurred.
                // can be deleted)
                Debug.Assert(_warnings != null || _errors != null, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
                Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!"); 
                SqlErrorCollection temp = null;  // temp variable to store that which is being thrown - so that local copies
 
                bool breakConnection = AddSqlErrorToCollection (ref temp, ref _errors); 
                breakConnection |= AddSqlErrorToCollection (ref temp,ref  _attentionErrors);
                breakConnection |= AddSqlErrorToCollection (ref temp, ref _warnings); 
                breakConnection |= AddSqlErrorToCollection (ref temp,ref _attentionWarnings);
                if (breakConnection) {
                     _state = TdsParserState.Broken;
                } 

                Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection"); 
                if (temp != null && temp.Count > 0) { 
                    // Construct the exception now that we've collected all the errors
                    string serverVersion = null; 
                    if (_state == TdsParserState.OpenLoggedIn) {
                        serverVersion = _connHandler.ServerVersion;
                    }
                    SqlException exception = SqlException.CreateException(temp, serverVersion); 
                    // the following handler will throw an exception or generate a warning event
                    _connHandler.OnError(exception, breakConnection); 
                } 
            }
        } 

 	private bool AddSqlErrorToCollection (ref SqlErrorCollection temp,ref SqlErrorCollection InputCollection) {
            if (null == InputCollection) {
                return false; 
            }
            bool broken = false; 
            if (null == temp) { 
                temp = new SqlErrorCollection ();
            } 
            for (int x = 0; x < InputCollection.Count; ++x) {
                SqlError error = InputCollection[x];
                temp.Add(error);
                if (error.Class >= TdsEnums.FATAL_ERROR_CLASS) { 
                    broken = true;                   			
                } 
            } 
            InputCollection = null;
            return (broken && TdsParserState.Closed != _state); 
        }

        internal static void ClearPoolCallback(object state) {
            Debug.Assert(null != state, "Unexpected null arg!"); 

            if (null != state) { 
                DbConnectionPoolGroup poolGroup = (DbConnectionPoolGroup) state; 
                poolGroup.Clear();
            } 
        }

        internal SqlError ProcessSNIError(TdsParserStateObject stateObj) {
#if DEBUG 
            Debug.Assert(SniContext.Undefined!=stateObj.DebugOnlyCopyOfSniContext, "SniContext must not be None");
#endif 
            SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error(); 
            SNINativeMethodWrapper.SNIGetLastError(sniError);
 
            if (_state == TdsParserState.OpenLoggedIn) {
                // Automatic pool cleaning logic upon server failure -
                // If a fatal netlib error occurs and the parser is in the OpenLoggedIn state (not
                // at login or pre-login time) then a fatal failure occurred that we were not informed 
                // of by the server.  As a result, clear out the PoolGroup that corresponds to this
                // parser and internal connection (same connection string).  This will refresh the pool 
                // and prevent other connections from failing in the same manner and forcing the user 
                // to go through all bad connections in the pool.
                if (null != _connHandler.Pool) { // If pooled, then just do it! 
                    Bid.Trace(" %d# clearing pool(ProcessSNIError)\n", ObjectID);
                    if (_fAsync) {
                        // SQL BU DT 399871 - do not call clear pool on same thread!
                        ThreadPool.QueueUserWorkItem(new WaitCallback(ClearPoolCallback), _connHandler.Pool.PoolGroup); 
                    }
                    else { 
                        _connHandler.Pool.PoolGroup.Clear(); 
                    }
                } 
            }
            // error.errorMessage is null terminated with garbage beyond that, since fixed length
            string errorMessage;
            int MessageLength = Array.IndexOf(sniError.errorMessage, '\0'); 
            if (MessageLength == -1) {
                errorMessage = String.Empty;  // If we don't see the expected null return nothing. 
            } else { 
                errorMessage = new String(sniError.errorMessage, 0, MessageLength);
            } 

            //  Format SNI errors and add Context Information
            //
            //  General syntax is: 
            //  
            //  (provider:, error:  - ) 
            // 
            // errorMessage | sniError |
            // ------------------------------------------- 
            // ==null       | x        | must never happen
            // !=null       | != 0     | retrieve corresponding errorMessage from resources
            // !=null       | == 0     | replace text left of errorMessage
            // 

            Debug.Assert(!ADP.IsEmpty(errorMessage),"Empty error message received from SNI"); 
 
            string sqlContextInfo = Res.GetString(Enum.GetName(typeof(SniContext), stateObj.SniContext));
 
            string providerRid = String.Format((IFormatProvider)null,"SNI_PN{0}", (int)sniError.provider);
            string providerName = Res.GetString(providerRid);
            Debug.Assert(!ADP.IsEmpty(providerName), String.Format((IFormatProvider)null,"invalid providerResourceId '{0}'", providerRid));
 
            if (sniError.sniError == 0) {
                // Provider error. The message from provider is preceeded with non-localizable info from SNI 
                // strip provider info from SNI 
                //
                int iColon = errorMessage.IndexOf(':'); 
                Debug.Assert(0<=iColon, "':' character missing in sni errorMessage");
                Debug.Assert(errorMessage.Length>iColon+1 && errorMessage[iColon+1]==' ', "Expecting a space after the ':' character");

                // extract the message excluding the colon and trailing cr/lf chars 
                if (0<=iColon) {
                    int len = errorMessage.Length; 
                    len -=2;    // exclude "\r\n" sequence 
                    iColon+=2;  // skip over ": " sequence
                    len-=iColon; 
                    /*
                        The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
                        Fix Bug 370686, if the message is recieved on a Win9x OS, the error message will not contain MESSAGE TEXT
                        per Bug: 269574.  If we get a errormessage with no message text, just return the entire message otherwise 
                        return just the message text.
                    */ 
                    if (len > 0) { 
                        errorMessage = errorMessage.Substring(iColon, len);
                    } 
                }
            }
            else {
                // SNI error. Replace the entire message 
                //
                string errorMessageId = String.Format((IFormatProvider)null,"SNI_ERROR_{0}",(int)sniError.sniError); 
                errorMessage = Res.GetString(errorMessageId); 
            }
            errorMessage = String.Format((IFormatProvider)null, "{0} (provider: {1}, error: {2} - {3})", 
                sqlContextInfo, providerName, (int)sniError.sniError, errorMessage);

            return new SqlError((int)sniError.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
                                _server, errorMessage, sniError.function, (int)sniError.lineNumber); 
        }
 
        internal void CheckResetConnection(TdsParserStateObject stateObj) { 
            if (_fResetConnection && !stateObj._fResetConnectionSent) {
                Debug.Assert(stateObj._outputPacketNumber == 1 || stateObj._outputPacketNumber == 2, "In ResetConnection logic unexpectedly!"); 
                try {
                    if (_fAsync && _fMARS && !stateObj._fResetEventOwned) {
                        // If using Async & MARS and we do not own ResetEvent - grab it.  We need to not grab lock here
                        // for case where multiple packets are sent to server from one execute. 
                        stateObj._fResetEventOwned = _resetConnectionEvent.WaitOne(TdsParserStaticMethods.GetTimeoutMilliseconds(stateObj.TimeoutTime), false);
 
                        if (stateObj._fResetEventOwned) { 
                            if (stateObj.TimeoutHasExpired) {
                                // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining. 
                                stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
                                stateObj.TimeoutTime = 0;
                            }
                        } 

                        if (!stateObj._fResetEventOwned) { 
                            // We timed out waiting for ResetEvent.  Throw timeout exception and reset 
                            // the buffer.  Nothing else to do since we did not actually send anything
                            // to the server. 
                            stateObj.ResetBuffer();
                            Errors.Add(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.Timeout(), "", 0));
                            ThrowExceptionAndWarning(stateObj);
                        } 
                    }
 
                    if (_fResetConnection) { 
                        // Check again to see if we need to send reset.
 
                        Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state for sending reset connection");
                        Debug.Assert(_isShiloh, "TdsParser.cs: Error!  _fResetConnection true when not going against Shiloh or later!");

                        if (_fPreserveTransaction) { 
                            // if we are reseting, set bit in header by or'ing with other value
                            stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION); 
                        } 
                        else {
                            // if we are reseting, set bit in header by or'ing with other value 
                            stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION);
                        }

                        if (!(_fAsync && _fMARS)) { 
                            _fResetConnection = false; // If not MARS, can turn off flag now.
                            _fPreserveTransaction = false; 
                        } 
                        else {
                            stateObj._fResetConnectionSent = true; // Otherwise set flag so we don't resend on multiple packet execute. 
                        }
                    }
                    else if (_fAsync && _fMARS && stateObj._fResetEventOwned) {
                        Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection"); 

                        // Otherwise if Yukon and we grabbed the event, free it.  Another execute grabbed the event and 
                        // took care of sending the reset. 
                        stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
                        Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); 
                    }
                }
                catch (Exception) {
                    if (_fAsync && _fMARS && stateObj._fResetEventOwned) { 
                        // If exception thrown, and we are on Yukon and own the event, release it!
                        stateObj._fResetConnectionSent = false; 
                        stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); 
                        Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!");
                    } 

                    throw;
                }
            } 
#if DEBUG
            else { 
                Debug.Assert(!_fResetConnection || 
                             (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned),
                             "Unexpected state on else ResetConnection block in WritePacket"); 
            }
#endif
        }
 

        // Takes in a single byte and writes it to the buffer.  If the buffer is full, it is flushed 
        // and then the buffer is re-initialized in flush() and then the byte is put in the buffer. 
        internal void WriteByte(byte b, TdsParserStateObject stateObj) {
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to WriteByte");  // you need to setup for a thread abort somewhere before you call this method 

            Debug.Assert(stateObj._outBytesUsed <= stateObj._outBuff.Length, "ERROR - TDSParser: _outBytesUsed > _outBuff.Length");

            // check to make sure we haven't used the full amount of space available in the buffer, if so, flush it 
            if (stateObj._outBytesUsed == stateObj._outBuff.Length)
                stateObj.WritePacket(TdsEnums.SOFTFLUSH); 
 
            // set byte in buffer and increment the counter for number of bytes used in the out buffer
            stateObj._outBuff[stateObj._outBytesUsed++] = b; 
        }

        //
        // Takes a byte array and writes it to the buffer. 
        //
        internal void WriteByteArray(Byte[] b, int len, int offsetBuffer, TdsParserStateObject stateObj) { 
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to WriteByteArray");  // you need to setup for a thread abort somewhere before you call this method 

            // Do we have to send out in packet size chunks, or can we rely on netlib layer to break it up? 
            // would prefer to to do something like:
            //
            // if (len > what we have room for || len > out buf)
            //   flush buffer 
            //   UnsafeNativeMethods.Write(b)
            // 
 
            int offset = offsetBuffer;
 
            Debug.Assert(b.Length >= len, "Invalid length sent to WriteByteArray()!");

            // loop through and write the entire array
            while (len > 0) { 
                if ((stateObj._outBytesUsed + len) > stateObj._outBuff.Length) {
                    // If the remainder of the string won't fit into the buffer, then we have to put 
                    // whatever we can into the buffer, and flush that so we can then put more into 
                    // the buffer on the next loop of the while.
 
                    int remainder = stateObj._outBuff.Length - stateObj._outBytesUsed;

                    // write the remainder
                    Buffer.BlockCopy(b, offset, stateObj._outBuff, stateObj._outBytesUsed, remainder); 

                    // handle counters 
                    offset += remainder; 
                    stateObj._outBytesUsed += remainder;
 
                    // Flush the buffer if full.
                    if (stateObj._outBytesUsed == stateObj._outBuff.Length) {
                        stateObj.WritePacket(TdsEnums.SOFTFLUSH);
                    } 

                    len -= remainder; 
                } 
                else { //((stateObj._outBytesUsed + len) <= stateObj._outBuff.Length )
                    // Else the remainder of the string will fit into the buffer, so copy it into the 
                    // buffer and then break out of the loop.

                    Buffer.BlockCopy(b, offset, stateObj._outBuff, stateObj._outBytesUsed, len);
 
                    // handle out buffer bytes used counter
                    stateObj._outBytesUsed += len; 
                    break; 
                }
            } 
        }

        //
        // Takes a 16 bit short and writes it. 
        //
        internal void WriteShort(int v, TdsParserStateObject stateObj) { 
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to WriteShort");  // you need to setup for a thread abort somewhere before you call this method 

            if ((stateObj._outBytesUsed + 2) > stateObj._outBuff.Length) { 
                // if all of the short doesn't fit into the buffer

                WriteByte((byte)(v & 0xff), stateObj);
                WriteByte((byte)((v >> 8) & 0xff), stateObj); 
            }
            else { 
                // all of the short fits into the buffer 

                stateObj._outBuff[stateObj._outBytesUsed++] = (byte)(v & 0xFF); 
                stateObj._outBuff[stateObj._outBytesUsed++] = (byte)((v >> 8) & 0xFF);
            }
        }
 
        internal void WriteUnsignedShort(ushort us, TdsParserStateObject stateObj) {
            WriteShort((short)us, stateObj); 
        } 

        // 
        // Takes a long and writes out an unsigned int
        //
        internal void WriteUnsignedInt(uint i, TdsParserStateObject stateObj) {
            WriteByteArray(BitConverter.GetBytes(i), 4, 0, stateObj); 
        }
 
        // 
        // Takes an int and writes it as an int.
        // 
        internal void WriteInt(int v, TdsParserStateObject stateObj) {
            WriteByteArray(BitConverter.GetBytes(v), 4, 0, stateObj);
        }
 
        //
        // Takes a float and writes it as a 32 bit float. 
        // 
        internal void WriteFloat(float v, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(v); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        //
        // Takes a long and writes it as a long. 
        // 
        internal void WriteLong(long v, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(v); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        //
        // Takes a ulong and writes it as a ulong. 
        // 
        internal void WriteUnsignedLong(ulong uv, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(uv); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        //
        // Takes a double and writes it as a 64 bit double. 
        // 
        internal void WriteDouble(double v, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(v); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        // Reads bytes from the buffer but doesn't return them, in effect simply deleting them.
        // Does not handle plp fields, need to use SkipPlpBytesValue for those. 
        internal void SkipLongBytes(ulong num, TdsParserStateObject stateObj) { 
            int cbSkip = 0;
 
            while (num > 0) {
                cbSkip = (num > Int32.MaxValue ? Int32.MaxValue : (int)num);
                stateObj.ReadByteArray(null, 0, cbSkip);
                num -= (ulong)cbSkip; 
            }
        } 
 
        // Reads bytes from the buffer but doesn't return them, in effect simply deleting them.
        public void SkipBytes(int num, TdsParserStateObject stateObj) { 
            stateObj.ReadByteArray(null, 0, num);
        }

        internal void PrepareResetConnection(bool preserveTransaction) { 
            // Set flag to reset connection upon next use - only for use on shiloh!
            _fResetConnection = true; 
            _fPreserveTransaction = preserveTransaction; 
        }
 
        // Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces
        internal bool Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) {
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to Run");  // you need to setup for a thread abort somewhere before you call this method
            Debug.Assert(   SniContext.Undefined != stateObj.SniContext 
                        &&  (SniContext.Snix_Execute != stateObj.SniContext || stateObj._attentionSent)
                        &&  SniContext.Snix_SendRows != stateObj.SniContext, 
                        String.Format((IFormatProvider)null, "Unexpected SniContext on call to GetValueInternal; SniContext={0}", stateObj.SniContext)); 

            if (TdsParserState.Broken == State || TdsParserState.Closed == State ) { 
                return true; // Just in case this is called in a loop, expecting data to be returned.
            }
            bool dataReady = false;
 
            do {
                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.

                byte token = stateObj.ReadByte(); 
                if (false == (
                             token == TdsEnums.SQLERROR || 
                             token == TdsEnums.SQLINFO || 
                             token == TdsEnums.SQLLOGINACK ||
                             token == TdsEnums.SQLENVCHANGE || 
                             token == TdsEnums.SQLRETURNVALUE ||
                             token == TdsEnums.SQLRETURNSTATUS ||
                             token == TdsEnums.SQLCOLNAME ||
                             token == TdsEnums.SQLCOLFMT || 
                             token == TdsEnums.SQLCOLMETADATA ||
                             token == TdsEnums.SQLALTMETADATA || 
                             token == TdsEnums.SQLTABNAME || 
                             token == TdsEnums.SQLCOLINFO ||
                             token == TdsEnums.SQLORDER || 
                             token == TdsEnums.SQLALTROW ||
                             token == TdsEnums.SQLROW ||
                             token == TdsEnums.SQLDONE ||
                             token == TdsEnums.SQLDONEPROC || 
                             token == TdsEnums.SQLDONEINPROC ||
                             token == TdsEnums.SQLROWCRC || 
                             token == TdsEnums.SQLSECLEVEL || 
                             token == TdsEnums.SQLCONTROL ||
                             token == TdsEnums.SQLPROCID || 
                             token == TdsEnums.SQLOFFSET ||
                             token == TdsEnums.SQLSSPI)) {
                    Debug.Assert(false, String.Format((IFormatProvider)null, "unexpected token; token = {0,-2:X2}", token));
                    _state = TdsParserState.Broken; 
                    _connHandler.BreakConnection();
                    throw SQL.ParsingError(); // MDAC 82443 
                } 

                int tokenLength = GetTokenLength(token, stateObj); 

                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 = ProcessError(token, stateObj);

                    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.
                        if ((_connHandler != null) && (_connHandler.Connection != null) && 
                            (_connHandler.Connection.FireInfoMessageEventOnUserErrors == true) && 
                            (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS)) {
                            // Fire SqlInfoMessage here 
                            FireInfoMessageEvent(stateObj, error);
                        }
                        else {
                            // insert error/info into the appropriate exception - warning if info, exception if error 
                            if (error.Class < TdsEnums.MIN_ERROR_CLASS) {
                                Warnings.Add(error); 
                            } 
                            else if (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS) {
                                Errors.Add(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 and that was a bug preventing the user from 
                                // processing subsequent results.
 
                                if (null != dataStream) { // Webdata 104560
                                    if (!dataStream.IsInitialized) {
                                        runBehavior = RunBehavior.UntilDone;
                                    } 
                                }
                            } 
                            else { 
                                Errors.Add(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) {
                        Errors.Add(error); 
                    }
                    break;
                }
 
                case TdsEnums.SQLCOLINFO:
                { 
                    if (null != dataStream) { 
                        _SqlMetaDataSet metaDataSet = ProcessColInfo(dataStream.MetaData, dataStream, stateObj);
                        dataStream.SetMetaData (metaDataSet, false); 
                        dataStream.BrowseModeInfoConsumed = true;
                    }
                    else { // no dataStream
                        SkipBytes(tokenLength, stateObj); 
                    }
                    break; 
                } 

                case TdsEnums.SQLDONE: 
                case TdsEnums.SQLDONEPROC:
                case TdsEnums.SQLDONEINPROC:
                {
                    // RunBehavior can be modified - see SQL BU DT 269516 & 290090 
                    ProcessDone(cmdHandler, dataStream, ref runBehavior, stateObj);
                    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 
                    SkipBytes(tokenLength, stateObj); 
                    break;
                } 

                case TdsEnums.SQLALTMETADATA:
                {
                    if (stateObj._cleanupAltMetaDataSetArray == null) {  // create object on demand (lazy creation) 
                        stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection();
                    } 
 
                    _SqlMetaDataSet cleanupAltMetaDataSet = ProcessAltMetaData(tokenLength, stateObj);
 
                    stateObj._cleanupAltMetaDataSetArray.Add(cleanupAltMetaDataSet);
                    if (null != dataStream) {
                        dataStream.SetAltMetaDataSet(cleanupAltMetaDataSet, (TdsEnums.SQLALTMETADATA != stateObj.PeekByte()));
                    } 

                    break; 
                } 

                case TdsEnums.SQLALTROW: 
                {
                    // read will call run until dataReady. Must not read any data if returnimmetiately set
                    if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) {
                        int altRowId = stateObj.ReadUInt16();             // get altRowId 

                        SkipRow(stateObj._cleanupAltMetaDataSetArray[altRowId], stateObj);  // skip altRow 
                    } 

                    dataReady = true; 
                    break;
                }

                case TdsEnums.SQLENVCHANGE: 
                {
                    SqlEnvChange[] env = ProcessEnvChange(tokenLength, stateObj); 
 
                    for (int ii = 0; ii < env.Length; ii++) {
                        if (env[ii] != null) { 
                            switch (env[ii].type) {
                            case TdsEnums.ENV_BEGINTRAN:
                            case TdsEnums.ENV_ENLISTDTC:
                                // 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 = (TdsEnums.ENV_BEGINTRAN == env[ii].type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed;
                                    _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue); 
                                }
                                if (null != _statistics && !_statisticsIsInTransaction) {
                                    _statistics.SafeIncrement(ref _statistics._transactions);
                                } 
                                _statisticsIsInTransaction = true;
                                _retainedTransactionId = SqlInternalTransaction.NullTransactionId; 
                                break; 
                            case TdsEnums.ENV_DEFECTDTC:
                            case TdsEnums.ENV_TRANSACTIONENDED: 
                            case TdsEnums.ENV_COMMITTRAN:
                                // SQLHOT 483
                                //  Must clear the retain id if the server-side transaction ends by anything other
                                //  than rollback. 
                                _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
                                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. 
                                        if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) { 
                                            _retainedTransactionId = env[ii].oldLongValue;
                                        } 
                                        _currentTransaction.Completed(TransactionState.Aborted);
                                    }
                                    else {
                                        // 
                                        _currentTransaction.Completed(TransactionState.Unknown);
                                    } 
                                    _currentTransaction = null; 
                                }
                                _statisticsIsInTransaction = false; 
                                break;
                            default:
                                _connHandler.OnEnvChange(env[ii]);
                                break; 
                            }
                        } 
                    } 
                    break;
                } 
                case TdsEnums.SQLLOGINACK:
                {
                    SqlLoginAck ack = ProcessLoginAck(stateObj);
 
                    _connHandler.OnLoginAck(ack);
                    break; 
                } 
                case TdsEnums.SQLCOLMETADATA:
                { 
                    if (tokenLength != TdsEnums.VARNULL) {
                        stateObj._cleanupMetaData = ProcessMetaData(tokenLength, stateObj);
                    }
                    else { 
                        if (cmdHandler != null) {
                            stateObj._cleanupMetaData = cmdHandler.MetaData; 
                        } 
                    }
 
                    if (null != dataStream) {
                        byte peekedToken = stateObj.PeekByte(); // temporarily cache next byte

                        dataStream.SetMetaData(stateObj._cleanupMetaData, (TdsEnums.SQLTABNAME == peekedToken || TdsEnums.SQLCOLINFO == peekedToken)); 
                    }
                    else if (null != bulkCopyHandler) { 
                        bulkCopyHandler.SetMetaData(stateObj._cleanupMetaData); 
                    }
                    break; 
                }
                case TdsEnums.SQLROW:
                {
                    if (null != bulkCopyHandler) { 
                        //
                        ProcessRow(stateObj._cleanupMetaData, bulkCopyHandler.CreateRowBuffer(), bulkCopyHandler.CreateIndexMap(), stateObj); 
                    } 
                    else if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior))
                        SkipRow(stateObj._cleanupMetaData, stateObj); // skip rows 

                    if (_statistics != null) {
                        _statistics.WaitForDoneAfterRow = true;
                    } 
                    dataReady = true;
                    break; 
                } 
                case TdsEnums.SQLRETURNSTATUS:
                    int status = stateObj.ReadInt32(); 
                    if (cmdHandler != null) {
                        cmdHandler.OnReturnStatus(status);
                    }
                    break; 
                case TdsEnums.SQLRETURNVALUE:
                { 
                    SqlReturnValue returnValue = ProcessReturnValue(tokenLength, stateObj); 
                    if (cmdHandler != null) {
                        cmdHandler.OnReturnValue(returnValue); 
                    }
                    break;
                }
                case TdsEnums.SQLSSPI: 
                {
                    // token length is length of SSPI data - call ProcessSSPI with it 
                    ProcessSSPI(tokenLength); 
                    break;
                } 
                case TdsEnums.SQLTABNAME:
                {
                    if (null != dataStream) {
                            dataStream.TableNames = ProcessTableName(tokenLength, stateObj); 
                    }
                    else { 
                        SkipBytes(tokenLength, stateObj); 
                    }
                    break; 
                }

                default:
                    Debug.Assert(false, "Unhandled token:  " + token.ToString(CultureInfo.InvariantCulture)); 
                    break;
            } 
            } 
                // 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 (!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._attentionSent && stateObj._attentionReceived) {
                // 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!
                    Errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0));
                } 
            }
 
            if (_errors != null || _warnings != null) { 
                ThrowExceptionAndWarning(stateObj);
            } 
            return dataReady;
        }

        private SqlEnvChange[] ProcessEnvChange(int tokenLength, TdsParserStateObject stateObj) { 
            // There could be multiple environment change messages following this token.
            int processedLength = 0; 
            int nvalues = 0; 
            SqlEnvChange[] envarray = new SqlEnvChange[3];  // Why is this hardcoded to 3?
 
            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(); 

                env.type = stateObj.ReadByte(); 
                envarray[nvalues] = env; 
                nvalues++;
 
                switch (env.type) {
                    case TdsEnums.ENV_DATABASE:
                    case TdsEnums.ENV_LANG:
                        ReadTwoStringFields(env, stateObj); 
                        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!");
                        ReadTwoStringFields(env, stateObj);
                        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
                        ReadTwoStringFields(env, stateObj);
                        // 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._sniPacket.Dispose();

                            // 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"); 

                            _physicalStateObj._sniPacket = new SNIPacket(_physicalStateObj.Handle); 
                        }

                        break;
 
                    case TdsEnums.ENV_LOCALEID:
                        // 
 
                        ReadTwoStringFields(env, stateObj);
                        _defaultLCID = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); 
                        break;

                    case TdsEnums.ENV_COMPFLAGS:
                        ReadTwoStringFields(env, stateObj); 
                        break;
 
                    case TdsEnums.ENV_COLLATION: 
                        Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!");
                        env.newLength = stateObj.ReadByte(); 
                        if (env.newLength == 5) {
                            env.newCollation = ProcessCollation(stateObj);

                            // 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;
                        }
 
                        env.oldLength = stateObj.ReadByte();
                        Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!"); 
                        if (env.oldLength == 5) 
                            env.oldCollation = ProcessCollation(stateObj);
 
                        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!");

                        env.newLength = stateObj.ReadByte();
                        Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!"); 

                        if (env.newLength > 0) { 
                            env.newLongValue = stateObj.ReadInt64(); 
                            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.
                        }
 
                        env.oldLength = stateObj.ReadByte();
                        Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!"); 
 
                        if (env.oldLength > 0) {
                            env.oldLongValue = stateObj.ReadInt64(); 
                            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 
                        ReadTwoStringFields(env, stateObj);
                        break; 
 
                    case TdsEnums.ENV_PROMOTETRANSACTION:
                        Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!"); 

                        env.newLength = stateObj.ReadInt32();                          // new value has 4 byte length
                        env.newBinValue = new byte[env.newLength];
                        stateObj.ReadByteArray(env.newBinValue, 0, env.newLength); // read new value with 4 byte length 

                        env.oldLength = stateObj.ReadByte(); 
                        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!"); 
                        ReadTwoBinaryFields(env, stateObj);
                        break; 

                    case TdsEnums.ENV_USERINSTANCE:
                        Debug.Assert(!_isYukon, "Received ENV_USERINSTANCE on non 9.0 server!");
                        ReadTwoStringFields(env, stateObj); 
                        break;
 
                    default: 
                        Debug.Assert(false, "Unknown environment change token: " + env.type);
                        break; 
                }
                processedLength += env.length;
            }
 
            return envarray;
        } 
 
        private void ReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject stateObj) {
            // Used by ProcessEnvChangeToken 
            env.newLength = stateObj.ReadByte();
            env.newBinValue = new byte[env.newLength];
            stateObj.ReadByteArray(env.newBinValue, 0, env.newLength);
            env.oldLength = stateObj.ReadByte(); 
            env.oldBinValue = new byte[env.oldLength];
            stateObj.ReadByteArray(env.oldBinValue, 0, env.oldLength); 
 
            // env.length includes 1 byte type token
            env.length = 3 + env.newLength + env.oldLength; 
        }

        private void ReadTwoStringFields(SqlEnvChange env, TdsParserStateObject stateObj) {
            // Used by ProcessEnvChangeToken 
            env.newLength = stateObj.ReadByte();
            env.newValue = stateObj.ReadString(env.newLength); 
            env.oldLength = stateObj.ReadByte(); 
            env.oldValue = stateObj.ReadString(env.oldLength);
 
            // env.length includes 1 byte type token
            env.length = 3 + env.newLength * 2 + env.oldLength * 2;
        }
 
        private void ProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavior run, TdsParserStateObject stateObj) {
            ushort curCmd; 
            ushort status; 
            int count;
 
            // status
            // command
            // rowcount (valid only if DONE_COUNT bit is set)
 
            status = (ushort)stateObj.ReadUInt16();
            curCmd = (ushort)stateObj.ReadUInt16(); 
            if (_isYukon) { 
                count = (int) stateObj.ReadInt64();
            } 
            else {
                count = stateObj.ReadInt32();
                // 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 = stateObj.PeekByte(); 
                        if (b == 0) {
                            // This is an invalid token value
                            count = stateObj.ReadInt32();
                        } 
                    }
                } 
            } 

            // 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)) && _errors == null && 
                  stateObj._errorTokenReceived == false && (RunBehavior.Clean != (RunBehavior.Clean & run))) { 
                Errors.Add(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))) {
                Errors.Add(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();
            } 
        } 

        private void ProcessSqlStatistics(ushort curCmd, ushort status, int count) { 
            // SqlStatistics bookkeeping stuff
            //
            if (null != _statistics) {
                // any done after row(s) counts as a resultset 
                if (_statistics.WaitForDoneAfterRow) {
                    _statistics.SafeIncrement(ref _statistics._sumResultSets); 
                    _statistics.WaitForDoneAfterRow = false; 
                }
 
                // clear row count DONE_COUNT flag is not set
                if (!(TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) {
                    count = 0;
                } 

                switch (curCmd) { 
                    case TdsEnums.INSERT: 
                    case TdsEnums.DELETE:
                    case TdsEnums.UPDATE: 
                    case TdsEnums.MERGE:
                        _statistics.SafeIncrement(ref _statistics._iduCount);
                        _statistics.SafeAdd(ref _statistics._iduRows, count);
                        if (!_statisticsIsInTransaction) { 
                            _statistics.SafeIncrement(ref _statistics._transactions);
                        } 
 
                        break;
 
                    case TdsEnums.SELECT:
                        _statistics.SafeIncrement(ref _statistics._selectCount);
                        _statistics.SafeAdd(ref _statistics._selectRows, count);
                        break; 

                    case TdsEnums.BEGINXACT: 
                        if (!_statisticsIsInTransaction) { 
                            _statistics.SafeIncrement(ref _statistics._transactions);
                        } 
                        _statisticsIsInTransaction = true;
                        break;

                    case TdsEnums.OPENCURSOR: 
                        _statistics.SafeIncrement(ref _statistics._cursorOpens);
                        break; 
 
                    case TdsEnums.ABORT:
                        _statisticsIsInTransaction = false; 
                        break;

                    case TdsEnums.ENDXACT:
                        _statisticsIsInTransaction = false; 
                        break;
                } // switch 
            } 
            else {
                switch (curCmd) { 
                    case TdsEnums.BEGINXACT:
                        _statisticsIsInTransaction = true;
                        break;
 
                    case TdsEnums.ABORT:
                    case TdsEnums.ENDXACT: 
                        _statisticsIsInTransaction = false; 
                        break;
                } 
            }
        }

        private SqlLoginAck ProcessLoginAck(TdsParserStateObject stateObj) { 
            SqlLoginAck a = new SqlLoginAck();
 
            // read past interface type and version 
            SkipBytes(1, stateObj);
 
            byte[] b = new byte[TdsEnums.VERSION_SIZE];
            stateObj.ReadByteArray(b, 0, b.Length);
            UInt32 tdsVersion = (UInt32)((((((b[0]<<8)|b[1])<<8)|b[2])<<8)|b[3]); // bytes are in motorola order (high byte first)
            UInt32 majorMinor = tdsVersion & 0xff00ffff; 
            UInt32 increment  = (tdsVersion >> 16) & 0xff;
 
            // Server responds: 
            // 0x07000000 -> Sphinx         // Notice server response format is different for bwd compat
            // 0x07010000 -> Shiloh RTM     // Notice server response format is different for bwd compat 
            // 0x71000001 -> Shiloh SP1
            // 0x72xx0002 -> Yukon RTM
            // 0x73xx0003 -> WinFS RTM
            // information provided by S. Ashwin 

            switch (majorMinor) { 
                case TdsEnums.SPHINXORSHILOH_MAJOR<<24|TdsEnums.DEFAULT_MINOR:    // Sphinx & Shiloh RTM 
                    // note that sphinx and shiloh_rtm can only be distinguished by the increment
                    switch (increment) { 
                        case TdsEnums.SHILOH_INCREMENT:
                            _isShiloh = true;
                            break;
                        case TdsEnums.SPHINX_INCREMENT: 
                            // no flag will be set
                            break; 
                        default: 
                            throw SQL.InvalidTDSVersion();
                    } 
                    break;
                case TdsEnums.SHILOHSP1_MAJOR<<24|TdsEnums.SHILOHSP1_MINOR: // Shiloh SP1
                    if (increment != TdsEnums.SHILOHSP1_INCREMENT) { throw SQL.InvalidTDSVersion(); }
                    _isShilohSP1 = true; 
                    break;
                case TdsEnums.YUKON_MAJOR<<24|TdsEnums.YUKON_RTM_MINOR:     // Yukon 
                    if (increment != TdsEnums.YUKON_INCREMENT) { throw SQL.InvalidTDSVersion(); } 
                    _isYukon = true;
                    break; 
                case TdsEnums.KATMAI_MAJOR<<24|TdsEnums.KATMAI_MINOR:
                    if (increment != TdsEnums.KATMAI_INCREMENT) { throw SQL.InvalidTDSVersion(); }
                    _isKatmai = true;
                    break; 
#if WINFSFunctionality
                case TdsEnums.WINFS_MAJOR<<24|TdsEnums.WINFS_RTM_MINOR:     // WinFS 
                    if (increment != TdsEnums.WINFS_INCREMENT) { throw SQL.InvalidTDSVersion(); } 
                    _isWinFS = true;
                    break; 
#endif
                default:
                    throw SQL.InvalidTDSVersion();
            } 

#if WINFSFunctionality 
            _isYukon        |= _isWinFS;            // the highest supported version 
#endif
            _isYukon        |= _isKatmai; 
            _isShilohSP1    |= _isYukon;            // includes all lower versions
            _isShiloh       |= _isShilohSP1;        //

            a.isVersion8 = _isShiloh; 

            stateObj._outBytesUsed = stateObj._outputHeaderLen; 
            byte len = stateObj.ReadByte(); 

            a.programName = stateObj.ReadString(len); 
            a.majorVersion = stateObj.ReadByte();
            a.minorVersion = stateObj.ReadByte();
            a.buildNum = (short)((stateObj.ReadByte() << 8) + stateObj.ReadByte());
 
            Debug.Assert(_state == TdsParserState.OpenNotLoggedIn, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn");
            _state = TdsParserState.OpenLoggedIn; 
 
            if (_isYukon) {
                if (_fAsync && _fMARS) { 
                    _resetConnectionEvent = new AutoResetEvent(true);
                }
            }
 
            // Fail if SSE UserInstance and we have not received this info.
            if ( _connHandler.ConnectionOptions.UserInstance && 
                ADP.IsEmpty(_connHandler.InstanceName)) { 
                Errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, Server, SQLMessage.UserInstanceFailure(), "", 0));
                ThrowExceptionAndWarning(_physicalStateObj); 
            }

            return a;
        } 

        internal SqlError ProcessError(byte token, TdsParserStateObject stateObj) { 
            int len; 
            int number = stateObj.ReadInt32();
            byte state = stateObj.ReadByte(); 
            byte errorClass = stateObj.ReadByte();

            Debug.Assert(((errorClass >= TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLERROR) ||
                          ((errorClass < TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLINFO), "class and token don't match!"); 

            len = stateObj.ReadUInt16(); 
 
            string message = stateObj.ReadString(len);
 
            len = (int)stateObj.ReadByte();

            string server;
 
            // MDAC bug #49307 - server sometimes does not send over server field! In those cases
            // we will use our locally cached value. 
            if (len == 0) 
                server = _server;
            else 
                server = stateObj.ReadString(len);

            len = (int)stateObj.ReadByte();
 
            string procedure = stateObj.ReadString(len);
 
             int line; 
             if (_isYukon) {
                line = stateObj.ReadInt32(); 
            }
            else {
                line = stateObj.ReadUInt16();
                // 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 = stateObj.PeekByte();
                    if (b == 0) { 
                        // This is an invalid token value
                        line = (line << 16) + stateObj.ReadUInt16();
                    }
                } 
            }
 
            return new SqlError(number, state, errorClass, _server, message, procedure, line); 
        }
 

        internal SqlReturnValue ProcessReturnValue(int length, TdsParserStateObject stateObj) {
            SqlReturnValue rec = new SqlReturnValue();
            rec.length = length;        // In Yukon this length is -1 
            if (_isYukon) {
                rec.parmIndex = stateObj.ReadUInt16(); 
            } 
            byte len = stateObj.ReadByte();      // Length of parameter name
 
            if (len > 0)
                rec.parameter = stateObj.ReadString(len);

            // read status and ignore 
            stateObj.ReadByte();
 
            UInt32 userType; 

            // read user type - 4 bytes Yukon, 2 backwards 
            if (IsYukonOrNewer) {
                userType = stateObj.ReadUInt32();
            }
            else { 
                userType = stateObj.ReadUInt16();
            } 
 
            // read off the flags
            stateObj.ReadUInt16(); 

            // read the type
            byte tdsType = stateObj.ReadByte();
 
            // 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 {
                tdsLen = GetTokenLength(tdsType, stateObj); 
            }

            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) {
                rec.precision = stateObj.ReadByte(); 
                rec.scale = stateObj.ReadByte(); 
            }
 
            if (rec.metaType.IsVarTime) {
                rec.scale = stateObj.ReadByte();
            }
 
            if (tdsType == TdsEnums.SQLUDT) {
                ProcessUDTMetaData((SqlMetaDataPriv) rec, stateObj); 
            } 

            if (rec.type == SqlDbType.Xml) { 
                // Read schema info
                byte schemapresent = stateObj.ReadByte();

                if ((schemapresent & 1) != 0) { 
                    len = stateObj.ReadByte();
                    if (len != 0) 
                        rec.xmlSchemaCollectionDatabase = stateObj.ReadString(len); 

                    len = stateObj.ReadByte(); 
                    if (len != 0)
                        rec.xmlSchemaCollectionOwningSchema = stateObj.ReadString(len);

                    short slen = stateObj.ReadInt16(); 
                    if (slen != 0)
                        rec.xmlSchemaCollectionName = stateObj.ReadString(slen); 
 
                }
            } 
            else if (_isShiloh && rec.metaType.IsCharType) {
                // read the collation for 8.x servers
                rec.collation = ProcessCollation(stateObj);
 
                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 = ProcessColumnHeader(rec, stateObj, out isNull);

            // 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 { 
                ReadSqlValue(rec.value, rec, intlen, stateObj);
            } 

            return rec;
        }
 
        internal SqlCollation ProcessCollation(TdsParserStateObject stateObj) {
            SqlCollation collation = new SqlCollation(); 
 
            collation.info = stateObj.ReadUInt32();
            collation.sortId = stateObj.ReadByte(); 
            return collation;
        }

        internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) { 
            int codePage = 0;
 
            if (0 != collation.sortId) { 
                codePage = TdsEnums.CODE_PAGE_FROM_SORT_ID[collation.sortId];
                Debug.Assert(0 != codePage, "GetCodePage accessed codepage array and produced 0!, sortID =" + ((Byte)(collation.sortId)).ToString((IFormatProvider)null)); 
            }
            else {
                int cultureId = collation.LCID;
                bool success = false; 

                try { 
                    codePage = CultureInfo.GetCultureInfo(cultureId).TextInfo.ANSICodePage; 

                    // SqlHot 50001398: CodePage can be zero, but we should defer such errors until 
                    //  we actually MUST use the code page (i.e. don't error if no ANSI data is sent).
                    success = true;
                }
                catch (ArgumentException e) { 
                    ADP.TraceExceptionWithoutRethrow(e);
                } 
 
                // If we failed, it is quite possible this is because certain culture id's
                // were removed in Win2k and beyond, however Sql Server still supports them. 
                // There is a workaround for the culture id's listed below, which is to mask
                // off the sort id (the leading 1). If that fails, or we have a culture id
                // other than the special cases below, we throw an error and throw away the
                // rest of the results. For additional info, see MDAC 65963. 

                // SqlHot 50001398: Sometimes GetCultureInfo will return CodePage 0 instead of throwing. 
                //  treat this as an error also, and switch into the special-case logic. 
                if (!success || codePage == 0) {
                    CultureInfo ci = null; 
                    switch (cultureId) {
                        case 0x10404: // Chinese - ----
                        case 0x10804: // Chinese - People's Republic of China
                        case 0x10c04: // Chinese - Hong Kong SAR 
                        case 0x11004: // Chinese - Singapore
                        case 0x11404: // Chinese - Macau SAR 
                        case 0x10411: // Japanese - Japan 
                        case 0x10412: // Korean - Korea
                            // If one of the following special cases, mask out sortId and 
                            // retry.
                            cultureId = cultureId & 0x03fff;

                            try { 
                                ci = new CultureInfo(cultureId);
                                success = true; 
                            } 
                            catch (ArgumentException e) {
                                ADP.TraceExceptionWithoutRethrow(e); 
                            }
                            break;
                        case 0x827:     // Non-supported Lithuanian code page, map it to supported Lithuanian.
                            try { 
                                ci = new CultureInfo(0x427);
                                success = true; 
                            } 
                            catch (ArgumentException e) {
                                ADP.TraceExceptionWithoutRethrow(e); 
                            }
                            break;
                        default:
                            break; 
                    }
 
                    // I don't believe we should still be in failure case, but just in case. 
                    if (!success) {
                        ThrowUnsupportedCollationEncountered(stateObj); 
                    }

                    if (null != ci) {
                        codePage = ci.TextInfo.ANSICodePage; 
                    }
                } 
            } 

            return codePage; 
        }

        internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj) {
            Errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.CultureIdError(), "", 0)); 

            if (null != stateObj) { 
                stateObj.CleanWire(); 

                stateObj._pendingData = false; 
            }

            ThrowExceptionAndWarning(stateObj);
        } 

 
 
        internal _SqlMetaDataSet ProcessAltMetaData(int cColumns, TdsParserStateObject stateObj) {
            Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!"); 

            _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns);
            int[] indexMap = new int[cColumns];
 
            altMetaDataSet.id = stateObj.ReadUInt16();
 
            int byCols = stateObj.ReadByte(); 

            while (byCols > 0) { 
                SkipBytes(2, stateObj);   // ignore ColNum ...
                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];
 
                col.op = stateObj.ReadByte();
                col.operand = stateObj.ReadUInt16();

                CommonProcessMetaData(stateObj, col); 

                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;
 
            return altMetaDataSet;
        }

 
        internal _SqlMetaDataSet ProcessMetaData(int cColumns, TdsParserStateObject stateObj) {
            Debug.Assert(cColumns > 0, "should have at least 1 column in metadata!"); 
 
            _SqlMetaDataSet metaData = new _SqlMetaDataSet(cColumns);
            for (int i = 0; i < cColumns; i++) { 
                CommonProcessMetaData(stateObj, metaData[i]);
            }

            return metaData; 
        }
 
        private bool IsVarTimeTds(byte tdsType) { 
            return tdsType == TdsEnums.SQLTIME || tdsType == TdsEnums.SQLDATETIME2 || tdsType == TdsEnums.SQLDATETIMEOFFSET;
        } 

        private void CommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col) {
            int    len      = 0;
            UInt32 userType; 

            // read user type - 4 bytes Yukon, 2 backwards 
            if (IsYukonOrNewer) { 
                userType = stateObj.ReadUInt32();
            } 
            else {
                userType = stateObj.ReadUInt16();
            }
 
            // read flags and set appropriate flags in structure
            byte flags = stateObj.ReadByte(); 
 
            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
            stateObj.ReadByte(); 

            col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet)); 
 
            byte tdsType = stateObj.ReadByte();
 
            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 {
                col.length = GetTokenLength(tdsType, stateObj); 
            }

            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 WINFSFunctionality
                    if (_isWinFS) { 
                        col.metaType = MetaType.GetUdtMetaType(true); 
                    }
#endif 

                    ProcessUDTMetaData((SqlMetaDataPriv) col, stateObj);
                }
 
                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 = stateObj.ReadByte(); 

                        if ((schemapresent & 1) != 0) { 
                            len = stateObj.ReadByte();
                            if (len != 0)
                                col.xmlSchemaCollectionDatabase = stateObj.ReadString(len);
 
                            len = stateObj.ReadByte();
                            if (len != 0) 
                                col.xmlSchemaCollectionOwningSchema = stateObj.ReadString(len); 

                            len = stateObj.ReadInt16(); 
                            if (len != 0)
                                col.xmlSchemaCollectionName = stateObj.ReadString(len);
                        }
                    } 
                }
            } 
 
            if (col.type == SqlDbType.Decimal) {
                col.precision = stateObj.ReadByte(); 
                col.scale = stateObj.ReadByte();
            }

            if (col.metaType.IsVarTime) { 
                col.scale = stateObj.ReadByte();
 
                // 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)) {
                col.collation = ProcessCollation(stateObj); 

                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
                    col.multiPartTableName = ProcessOneTable(stateObj, ref unusedLen);
                } else { 
                    len = stateObj.ReadUInt16();
                    string tableName = stateObj.ReadString(len); 
                    // 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);
                }
            } 

            len = stateObj.ReadByte(); 
            col.column = stateObj.ReadString(len); 

            // 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;
        }
 
        private void ProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObject stateObj) {
            int length; 
 
            metaData.length = stateObj.ReadUInt16(); // max byte size
 
            // database name
            length = stateObj.ReadByte();
            if (length != 0) {
                metaData.udtDatabaseName = stateObj.ReadString(length); 
            }
 
            // schema name 
            length = stateObj.ReadByte();
            if (length != 0) { 
                metaData.udtSchemaName = stateObj.ReadString(length);
            }

            // type name 
            length = stateObj.ReadByte();
            if (length != 0) { 
                metaData.udtTypeName = stateObj.ReadString(length); 
            }
 
            length = stateObj.ReadUInt16();
            if (length != 0) {
                metaData.udtAssemblyQualifiedName = stateObj.ReadString(length);
            } 
        }
 
        private void WriteUDTMetaData(object value, string database, string schema, string type, 
                                      TdsParserStateObject stateObj) {
            // database 
            if (ADP.IsEmpty(database)) {
                WriteByte(0, stateObj);
            }
            else { 
                WriteByte((byte) database.Length, stateObj);
                WriteString(database, stateObj); 
            } 

            // schema 
            if (ADP.IsEmpty(schema)) {
                WriteByte(0, stateObj);
            }
            else { 
                WriteByte((byte) schema.Length, stateObj);
                WriteString(schema, stateObj); 
            } 

            // type 
            if (ADP.IsEmpty(type)) {
                WriteByte(0, stateObj);
            }
            else { 
                WriteByte((byte) type.Length, stateObj);
                WriteString(type, stateObj); 
            } 
        }
 
        internal MultiPartTableName[] ProcessTableName(int length, TdsParserStateObject stateObj) {
            int tablesAdded = 0;

            MultiPartTableName[] tables = new MultiPartTableName[1]; 
            MultiPartTableName mpt;
            while (length > 0) { 
                // 

 
                mpt = ProcessOneTable(stateObj, ref length);
                if (tablesAdded == 0) {
                    tables[tablesAdded] = mpt;
                } 
                else {
                    MultiPartTableName[] newTables = new MultiPartTableName[tables.Length + 1]; 
                    Array.Copy(tables, 0, newTables, 0, tables.Length); 
                    newTables[tables.Length] = mpt;
                    tables = newTables; 
                }

                tablesAdded++;
            } 
            return tables;
        } 
 
        private MultiPartTableName ProcessOneTable(TdsParserStateObject stateObj, ref int length ) {
            ushort tableLen; 
            MultiPartTableName mpt;

            if (_isShilohSP1) {
 
                    mpt = new MultiPartTableName();
                    byte nParts; 
 
                    // Find out how many parts in the TDS stream
                    nParts = stateObj.ReadByte(); 
                    length--;
                    if (nParts == 4) {
                        tableLen = stateObj.ReadUInt16();
                        length -= 2; 
                        mpt.ServerName = stateObj.ReadString(tableLen);
                        nParts--; 
                        length -= (tableLen * 2); // wide bytes 
                    }
                    if (nParts == 3) { 
                        tableLen = stateObj.ReadUInt16();
                        length -= 2;
                        mpt.CatalogName = stateObj.ReadString(tableLen);
                        length -= (tableLen * 2); // wide bytes 
                        nParts--;
                    } 
                    if (nParts == 2) { 
                        tableLen = stateObj.ReadUInt16();
                        length -= 2; 
                        mpt.SchemaName = stateObj.ReadString(tableLen);
                        length -= (tableLen * 2); // wide bytes
                        nParts--;
                    } 
                    if (nParts == 1) {
                        tableLen = stateObj.ReadUInt16(); 
                        length -= 2; 
                        mpt.TableName = stateObj.ReadString(tableLen);
                        length -= (tableLen * 2); // wide bytes 
                        nParts--;
                    }
                    Debug.Assert(nParts == 0 , "ProcessTableName:Unidentified parts in the table name token stream!");
 
            }
            else { 
                tableLen = stateObj.ReadUInt16(); 
                length -= 2;
                string tableName = stateObj.ReadString(tableLen); 
                length -= (tableLen * 2); // wide bytes
                mpt = new MultiPartTableName(MultipartIdentifier.ParseMultipartIdentifier(tableName, "[\"", "]\"", Res.SQL_TDSParserTableName, false));
            }
            return mpt; 
        }
 
        // augments current metadata with table and key information 
        private _SqlMetaDataSet ProcessColInfo(_SqlMetaDataSet columns, SqlDataReader reader, TdsParserStateObject stateObj) {
            Debug.Assert(columns != null && columns.Length > 0, "no metadata available!"); 

            for (int i = 0; i < columns.Length; i++) {
                _SqlMetaData col = columns[i];
 
                stateObj.ReadByte();    // colnum, ignore
                col.tableNum = stateObj.ReadByte(); 
 
                // interpret status
                byte status = stateObj.ReadByte(); 

                col.isDifferentName = (TdsEnums.SQLDifferentName == (status & TdsEnums.SQLDifferentName));
                col.isExpression = (TdsEnums.SQLExpression == (status & TdsEnums.SQLExpression));
                col.isKey = (TdsEnums.SQLKey == (status & TdsEnums.SQLKey)); 
                col.isHidden = (TdsEnums.SQLHidden == (status & TdsEnums.SQLHidden));
 
                // read off the base table name if it is different than the select list column name 
                if (col.isDifferentName) {
                    byte len = stateObj.ReadByte(); 

                    col.baseColumn = stateObj.ReadString(len);
                }
 
                // Fixup column name - only if result of a table - that is if it was not the result of
                // an expression. 
                if ((reader.TableNames != null) && (col.tableNum > 0)) { 
                    Debug.Assert(reader.TableNames.Length >= col.tableNum, "invalid tableNames array!");
                    col.multiPartTableName = reader.TableNames[col.tableNum - 1]; 
                }

                // MDAC 60109: expressions are readonly
                if (col.isExpression) { 
                    col.updatability = 0;
                } 
            } 

            // set the metadata so that the stream knows some metadata info has changed 
            return columns;
        }

        // takes care of any per data header information: 
        // for long columns, reads off textptrs, reads length, check nullability
        // for other columns, reads length, checks nullability 
        // returns length and nullability 
        internal ulong ProcessColumnHeader(SqlMetaDataPriv col, TdsParserStateObject stateObj, out bool isNull) {
            if (col.metaType.IsLong && !col.metaType.IsPlp) { 
                //
                // we don't care about TextPtrs, simply go after the data after it
                //
                byte textPtrLen = stateObj.ReadByte(); 

                if (0 != textPtrLen) { 
                    // read past text pointer 
                    SkipBytes(textPtrLen, stateObj);
 
                    // read past timestamp
                    SkipBytes(TdsEnums.TEXT_TIME_STAMP_LEN, stateObj);
                    isNull = false;
                    return GetDataLength(col, stateObj); 
                }
                else { 
                    isNull = true; 
                    return 0;
 
                }
            }
            else {
                // non-blob columns 
                ulong longlen = GetDataLength(col, stateObj);
                isNull = IsNull(col.metaType, longlen); 
                return (isNull ? 0 : longlen); 
            }
        } 

        // assumes that the current position is at the start of an altrow!
        internal int GetAltRowId(TdsParserStateObject stateObj) {
            byte token = stateObj.ReadByte();    // skip over ALTROW token 

            Debug.Assert((token == TdsEnums.SQLALTROW), ""); 
            return (int)stateObj.ReadUInt16(); 
        }
 
        // Used internally by BulkCopy only
        private void ProcessRow(_SqlMetaDataSet columns, object[] buffer, int[] map, TdsParserStateObject stateObj) {
            SqlBuffer data = new SqlBuffer();
 
            for (int i = 0; i < columns.Length; i++) {
                _SqlMetaData md = columns[i]; 
                Debug.Assert(md != null, "_SqlMetaData should not be null for column " + i.ToString(CultureInfo.InvariantCulture)); 

                bool isNull; 
                ulong len = ProcessColumnHeader(md, stateObj, out isNull);

                if (isNull) {
                    GetNullSqlValue(data, md); 
                    buffer[map[i]] = data.SqlValue;
                } 
                else { 
                    // We only read up to 2Gb. Throw if data is larger. Very large data
                    // should be read in chunks in sequential read mode 
                    // For Plp columns, we may have gotten only the length of the first chunk
                    ReadSqlValue(data, md, md.metaType.IsPlp ? (Int32.MaxValue) : (int)len, stateObj);
                    buffer[map[i]] = data.SqlValue;
                    if (stateObj._longlen != 0) { 
                        throw new SqlTruncateException(Res.GetString(Res.SqlMisc_TruncationMaxDataMessage));
                    } 
                } 
                data.Clear();
            } 
        }

        internal object GetNullSqlValue(SqlBuffer nullVal, SqlMetaDataPriv md) {
            switch (md.type) { 
                case SqlDbType.Real:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Single); 
                    break; 

                case SqlDbType.Float: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Double);
                    break;

                case SqlDbType.Udt: 
                case SqlDbType.Binary:
                case SqlDbType.VarBinary: 
                case SqlDbType.Image: 
                    nullVal.SqlBinary = SqlBinary.Null;
                    break; 

                case SqlDbType.UniqueIdentifier:
                    nullVal.SqlGuid = SqlGuid.Null;
                    break; 

                case SqlDbType.Bit: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Boolean); 
                    break;
 
                case SqlDbType.TinyInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Byte);
                    break;
 
                case SqlDbType.SmallInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int16); 
                    break; 

                case SqlDbType.Int: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int32);
                    break;

                case SqlDbType.BigInt: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int64);
                    break; 
 
                case SqlDbType.Char:
                case SqlDbType.VarChar: 
                case SqlDbType.NChar:
                case SqlDbType.NVarChar:
                case SqlDbType.Text:
                case SqlDbType.NText: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.String);
                    break; 
 
                case SqlDbType.Decimal:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Decimal); 
                    break;

                case SqlDbType.DateTime:
                case SqlDbType.SmallDateTime: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime);
                    break; 
 
                case SqlDbType.Money:
                case SqlDbType.SmallMoney: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Money);
                    break;

                case SqlDbType.Variant: 
                    // DBNull.Value will have to work here
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Empty); 
                    break; 

                case SqlDbType.Xml: 
                    nullVal.SqlCachedBuffer = SqlCachedBuffer.Null;
                    break;

                case SqlDbType.Date: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Date);
                    break; 
 
                case SqlDbType.Time:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Time); 
                    break;

                case SqlDbType.DateTime2:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime2); 
                    break;
 
                case SqlDbType.DateTimeOffset: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTimeOffset);
                    break; 

                default:
                    Debug.Assert(false, "unknown null sqlType!" + md.type.ToString());
                    break; 
            }
 
            return nullVal; 
        }
 
        internal void SkipRow(_SqlMetaDataSet columns, TdsParserStateObject stateObj) {
            SkipRow(columns, 0, stateObj);
        }
 
        internal void SkipRow(_SqlMetaDataSet columns, int startCol, TdsParserStateObject stateObj) {
            for (int i = startCol; i < columns.Length; i++) { 
                _SqlMetaData md = columns[i]; 

                if (md.metaType.IsLong && !md.metaType.IsPlp) { 
                    byte textPtrLen = stateObj.ReadByte();

                    if (0 != textPtrLen)
                        SkipBytes(textPtrLen + TdsEnums.TEXT_TIME_STAMP_LEN, stateObj); 
                    else
                        continue; 
                } 

                SkipValue(md, stateObj); 
            }
        }

        internal void SkipValue(SqlMetaDataPriv md, TdsParserStateObject stateObj) { 
            if (md.metaType.IsPlp) {
                SkipPlpValue(UInt64.MaxValue, stateObj); 
            } 
            else {
                int length = GetTokenLength(md.tdsType, stateObj); 

                // if false, no value to skip - it's null
                if (!IsNull(md.metaType, (ulong)length))
                    SkipBytes(length, stateObj); 
            }
 
            return; 
        }
 
        private bool IsNull(MetaType mt, ulong length) {
            // null bin and char types have a length of -1 to represent null
            if (mt.IsPlp) {
                return (TdsEnums.SQL_PLP_NULL == length); 
            }
 
            // HOTFIX #50000415: for image/text, 0xFFFF is the length, not representing null 
            if ((TdsEnums.VARNULL == length) && !mt.IsLong) {
                return true; 
            }

            // other types have a length of 0 to represent null
            return ((TdsEnums.FIXEDNULL == length) && !mt.IsCharType && !mt.IsBinType); 
        }
 
        private void ReadSqlStringValue(SqlBuffer value, byte type, int length, Encoding encoding, bool isPlp, TdsParserStateObject stateObj) { 
            switch (type) {
                case TdsEnums.SQLCHAR: 
                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLVARCHAR:
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT: 
                    // If bigvarchar(max), we only read the first chunk here,
                    // expecting the caller to read the rest 
                    if (encoding == null) { 
                        // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
                        // 7.0 has no support for multiple code pages in data - single code page support only 
                        encoding = _defaultEncoding;
                    }
                    value.SetToString(stateObj.ReadStringWithEncoding(length, encoding, isPlp));
                    break; 

                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR: 
                case TdsEnums.SQLNTEXT:
                { 
                        String s = null;

                        if (isPlp) {
                            char[] cc = null; 

                            length = ReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj); 
                            if (length > 0) { 
                                s = new String(cc, 0, length);
                            } 
                            else {
                                s = ADP.StrEmpty;
                            }
                        } 
                        else {
                            s = stateObj.ReadString(length >> 1); 
                        } 

                        value.SetToString(s); 
                        break;
                    }

                default: 
                    Debug.Assert(false, "Unknown tds type for SqlString!" + type.ToString(CultureInfo.InvariantCulture));
                    break; 
            } 
        }
 
        internal void ReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, TdsParserStateObject stateObj) {
            Debug.Assert(md.metaType.IsPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!");
            if (md.metaType.IsPlp) {
                // We must read the column value completely, no matter what length is passed in 
                length = Int32.MaxValue;
            } 
            switch (md.tdsType) { 
                case TdsEnums.SQLDECIMALN:
                case TdsEnums.SQLNUMERICN: 
                    ReadSqlDecimal(value, length, md.precision, md.scale, stateObj);
                    break;

                case TdsEnums.SQLUDT: 
                case TdsEnums.SQLBINARY:
                case TdsEnums.SQLBIGBINARY: 
                case TdsEnums.SQLBIGVARBINARY: 
                case TdsEnums.SQLVARBINARY:
                case TdsEnums.SQLIMAGE: 
                    byte[] b = null;

                    // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
                    if (md.metaType.IsPlp) { 
                        // If we are given -1 for length, then we read the entire value,
                        // otherwise only the requested amount, usually first chunk. 
                        stateObj.ReadPlpBytes(ref b, 0, length); 
                    }
                    else { 
                        //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
                        b = new byte[length];
                        stateObj.ReadByteArray(b, 0, length);
                    } 

                    value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array 
 
                    break;
 
                case TdsEnums.SQLCHAR:
                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLVARCHAR:
                case TdsEnums.SQLBIGVARCHAR: 
                case TdsEnums.SQLTEXT:
                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR: 
                case TdsEnums.SQLNTEXT:
                    ReadSqlStringValue(value, md.tdsType, length, md.encoding, md.metaType.IsPlp, stateObj); 
                    break;

                case TdsEnums.SQLXMLTYPE:
                    // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader. 
                    SqlCachedBuffer sqlBuf = new SqlCachedBuffer(md, this, stateObj);
 
                    value.SqlCachedBuffer = sqlBuf; 
                    break;
 
                case TdsEnums.SQLDATE:
                case TdsEnums.SQLTIME:
                case TdsEnums.SQLDATETIME2:
                case TdsEnums.SQLDATETIMEOFFSET: 
                    ReadSqlDateTime(value, md.tdsType, length, md.scale, stateObj);
                    break; 
 
                default:
                    Debug.Assert(!md.metaType.IsPlp, "ReadSqlValue calling ReadSqlValueInternal with plp data"); 
                    ReadSqlValueInternal(value, md.tdsType, md.metaType.TypeId, length, stateObj);
                    break;
            }
 
            Debug.Assert((stateObj._longlen == 0) && (stateObj._longlenleft == 0), "ReadSqlValue did not read plp field completely, longlen =" + stateObj._longlen.ToString((IFormatProvider)null) + ",longlenleft=" + stateObj._longlenleft.ToString((IFormatProvider)null));
        } 
 
        private void ReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj) {
            stateObj.ReadByteArray(datetimeBuffer, 0, length); 

            switch (tdsType) {
                case TdsEnums.SQLDATE:
                    Debug.Assert(length == 3, "invalid length for date type!"); 
                    value.SetToDate(datetimeBuffer);
                    break; 
 
                case TdsEnums.SQLTIME:
                    Debug.Assert(3 <= length && length <= 5, "invalid length for time type!"); 
                    value.SetToTime(datetimeBuffer, length, scale);
                    break;

                case TdsEnums.SQLDATETIME2: 
                    Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!");
                    value.SetToDateTime2(datetimeBuffer, length, scale); 
                    break; 

                case TdsEnums.SQLDATETIMEOFFSET: 
                    Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!");
                    value.SetToDateTimeOffset(datetimeBuffer, length, scale);
                    break;
 
                default:
                    Debug.Assert(false, "ReadSqlDateTime is called with the wrong tdsType"); 
                    break; 
            }
        } 

        internal void ReadSqlValueInternal(SqlBuffer value, byte tdsType, int typeId, int length, TdsParserStateObject stateObj) {
            switch (tdsType) {
                case TdsEnums.SQLBIT: 
                case TdsEnums.SQLBITN:
                    Debug.Assert(length == 1, "invalid length for SqlBoolean type!"); 
                    value.Boolean = (stateObj.ReadByte() != 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!"); 
                    value.Byte = stateObj.ReadByte();
                    break; 

                case TdsEnums.SQLINT2:
                    Debug.Assert(length == 2, "invalid length for SqlInt16 type!");
                    value.Int16 = stateObj.ReadInt16(); 
                    break;
 
                case TdsEnums.SQLINT4: 
                    Debug.Assert(length == 4, "invalid length for SqlInt32 type!");
                    value.Int32 = stateObj.ReadInt32(); 
                    break;

                case TdsEnums.SQLINT8:
                    Debug.Assert(length == 8, "invalid length for SqlInt64 type!"); 
                    value.Int64 = stateObj.ReadInt64();
                    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!");
                    value.Single = stateObj.ReadSingle(); 
                    break;

                case TdsEnums.SQLFLT8:
                    Debug.Assert(length == 8, "invalid length for SqlDouble type!"); 
                    value.Double = stateObj.ReadDouble();
                    break; 
 
                case TdsEnums.SQLMONEYN:
                    if (length == 4) { 
                        goto case TdsEnums.SQLMONEY4;
                    }
                    else {
                        goto case TdsEnums.SQLMONEY; 
                    }
 
                case TdsEnums.SQLMONEY: 
                {
                        int mid = stateObj.ReadInt32(); 
                        uint lo = stateObj.ReadUInt32();
                        long l = (((long)mid) << 0x20) + ((long)lo);

                        value.SetToMoney(l); 
                        break;
                    } 
 
                case TdsEnums.SQLMONEY4:
                    value.SetToMoney(stateObj.ReadInt32()); 
                    break;

                case TdsEnums.SQLDATETIMN:
                    if (length == 4) { 
                        goto case TdsEnums.SQLDATETIM4;
                    } 
                    else { 
                        goto case TdsEnums.SQLDATETIME;
                    } 

                case TdsEnums.SQLDATETIM4:
                    value.SetToDateTime(stateObj.ReadUInt16(), stateObj.ReadUInt16() * SqlDateTime.SQLTicksPerMinute);
                    break; 

                case TdsEnums.SQLDATETIME: 
                    value.SetToDateTime(stateObj.ReadInt32(), (int)stateObj.ReadUInt32()); 
                    break;
 
                case TdsEnums.SQLUNIQUEID:
                {
                    Debug.Assert(length == 16, "invalid length for SqlGuid type!");
 
                    byte[] b = new byte[length];
 
                    stateObj.ReadByteArray(b, 0, length); 
                    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];
                    stateObj.ReadByteArray(b, 0, length);
                    value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array 

                    break; 
                } 

                case TdsEnums.SQLVARIANT: 
                    ReadSqlVariant(value, length, stateObj);
                    break;

                default: 
                    Debug.Assert(false, "Unknown SqlType!" + tdsType.ToString(CultureInfo.InvariantCulture));
                    break; 
            } // switch 
        }
 
        //
        // Read in a SQLVariant
        //
        // SQLVariant looks like: 
        // struct
        // { 
        //      BYTE TypeTag 
        //      BYTE cbPropBytes
        //      BYTE[] Properties 
        //      BYTE[] DataVal
        // }
        internal void ReadSqlVariant(SqlBuffer value, int lenTotal, TdsParserStateObject stateObj) {
            Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variaint in pre-SQL2000 server!"); 
            // get the SQLVariant type
            byte type = stateObj.ReadByte(); 
            int lenMax = 0; // maximum lenData of value inside variant 

            // read cbPropBytes 
            byte cbPropsActual = stateObj.ReadByte();
            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:
                    ReadSqlValueInternal(value, type, 0, lenData, stateObj); 
                    break;

                case TdsEnums.SQLDECIMALN:
                case TdsEnums.SQLNUMERICN: 
                {
                        Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for decimal/numeric type!"); 
 
                        byte precision = stateObj.ReadByte();
                        byte scale = stateObj.ReadByte(); 

                        // skip over unknown properties
                        if (cbPropsActual > cbPropsExpected) {
                            SkipBytes(cbPropsActual - cbPropsExpected, stateObj); 
                        }
 
                        ReadSqlDecimal(value, TdsEnums.MAX_NUMERIC_LEN, precision, scale, stateObj); 
                        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!");
 
                    lenMax = stateObj.ReadUInt16(); 
                    Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarbinary(max) in a sqlvariant");
 
                    // skip over unknown properties
                    if (cbPropsActual > cbPropsExpected) {
                        SkipBytes(cbPropsActual - cbPropsExpected, stateObj);
                    } 

                    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!"); 

                        // 
 
                        ProcessCollation(stateObj);
 
                        lenMax = stateObj.ReadUInt16();
                        Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarchar(max) or nvarchar(max) in a sqlvariant");

                        // skip over unknown properties 
                        if (cbPropsActual > cbPropsExpected) {
                            SkipBytes(cbPropsActual - cbPropsExpected, stateObj); 
                        } 

                        ReadSqlStringValue(value, type, lenData, null, false, stateObj); 
                        break;
                    }
                case TdsEnums.SQLDATE:
                    ReadSqlDateTime(value, type, lenData, 0, stateObj); 
                    break;
 
                case TdsEnums.SQLTIME: 
                case TdsEnums.SQLDATETIME2:
                case TdsEnums.SQLDATETIMEOFFSET: 
                {
                    Debug.Assert(cbPropsExpected == 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!");

                    byte scale = stateObj.ReadByte(); 

                    // skip over unknown properties 
                    if (cbPropsActual > cbPropsExpected) { 
                        SkipBytes(cbPropsActual - cbPropsExpected, stateObj);
                    } 

                    ReadSqlDateTime(value, type, lenData, scale, stateObj);
                    break;
                } 

                default: 
                    Debug.Assert(false, "Unknown tds type in SqlVariant!" + type.ToString(CultureInfo.InvariantCulture)); 
                    break;
            } // switch 
        }

        //
        // Translates a com+ object -> SqlVariant 
        // when the type is ambiguous, we always convert to the bigger type
        // note that we also write out the maxlen and actuallen members (4 bytes each) 
        // in addition to the SQLVariant structure 
        //
        internal void WriteSqlVariantValue(object value, int length, int offset, TdsParserStateObject stateObj) { 
            Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");

            // handle null values
            if (ADP.IsNull(value)) { 
                WriteInt(TdsEnums.FIXEDNULL, stateObj); //maxlen
                WriteInt(TdsEnums.FIXEDNULL, stateObj); //actuallen 
                return; 
            }
 
            MetaType mt = MetaType.GetMetaTypeFromValue(value);

            if (mt.IsAnsiType) {
                length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding); 
            }
 
            // max and actual len are equal to 
            // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
            WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // maxLen 
            WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // actualLen

            // write the SQLVariant header (type and cbPropBytes)
            WriteByte(mt.TDSType, stateObj); 
            WriteByte(mt.PropBytes, stateObj);
 
            // now write the actual PropBytes and data 
            switch (mt.TDSType) {
                case TdsEnums.SQLFLT4: 
                    WriteFloat((Single)value, stateObj);
                    break;

                case TdsEnums.SQLFLT8: 
                    WriteDouble((Double)value, stateObj);
                    break; 
 
                case TdsEnums.SQLINT8:
                    WriteLong((Int64)value, stateObj); 
                    break;

                case TdsEnums.SQLINT4:
                    WriteInt((Int32)value, stateObj); 
                    break;
 
                case TdsEnums.SQLINT2: 
                    WriteShort((Int16)value, stateObj);
                    break; 

                case TdsEnums.SQLINT1:
                    WriteByte((byte)value, stateObj);
                    break; 

                case TdsEnums.SQLBIT: 
                    if ((bool)value == true) 
                        WriteByte(1, stateObj);
                    else 
                        WriteByte(0, stateObj);

                    break;
 
                case TdsEnums.SQLBIGVARBINARY:
                    { 
                        byte[] b = (byte[])value; 

                        WriteShort(length, stateObj); // propbytes: varlen 
                        WriteByteArray(b, length, offset, stateObj);
                        break;
                    }
 
                case TdsEnums.SQLBIGVARCHAR:
                    { 
                        string s = (string)value; 

                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info 
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId
                        WriteShort(length, stateObj); // propbyte: varlen
                        WriteEncodingChar(s, _defaultEncoding, stateObj);
                        break; 
                    }
 
                case TdsEnums.SQLUNIQUEID: 
                    {
                        System.Guid guid = (System.Guid)value; 
                        byte[] b = guid.ToByteArray();

                        Debug.Assert((length == b.Length) && (length == 16), "Invalid length for guid type in com+ object");
                        WriteByteArray(b, length, 0, stateObj); 
                        break;
                    } 
 
                case TdsEnums.SQLNVARCHAR:
                    { 
                        string s = (string)value;

                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId 
                        WriteShort(length, stateObj); // propbyte: varlen
 
                        // string takes cchar, not cbyte so convert 
                        length >>= 1;
                        WriteString(s, length, offset, stateObj); 
                        break;
                    }

                case TdsEnums.SQLDATETIME: 
                    {
                        TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8); 
 
                        WriteInt(dt.days, stateObj);
                        WriteInt(dt.time, stateObj); 
                        break;
                    }

                case TdsEnums.SQLMONEY: 
                    {
                        WriteCurrency((Decimal)value, 8, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLNUMERICN:
                    {
                        WriteByte(mt.Precision, stateObj); //propbytes: precision
                        WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10), stateObj); // propbytes: scale 
                        WriteDecimal((Decimal)value, stateObj);
                        break; 
                    } 

                case TdsEnums.SQLTIME: 
                    WriteByte(mt.Scale, stateObj); //propbytes: scale
                    WriteTime((TimeSpan)value, mt.Scale, length, stateObj);
                    break;
 
                case TdsEnums.SQLDATETIMEOFFSET:
                    WriteByte(mt.Scale, stateObj); //propbytes: scale 
                    WriteDateTimeOffset((DateTimeOffset)value, mt.Scale, length, stateObj); 
                    break;
 
                default:
                    Debug.Assert(false, "unknown tds type for sqlvariant!");
                    break;
            } // switch 
        }
 
// todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider 
// combining these tow methods.
 
        //
        // Translates a com+ object -> SqlVariant
        // when the type is ambiguous, we always convert to the bigger type
        // note that we also write out the maxlen and actuallen members (4 bytes each) 
        // in addition to the SQLVariant structure
        // 
        // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value. 
        // Therefore the sql_variant value must not include the MaxLength. This is the major difference
        // between this method and WriteSqlVariantValue above. 
        //
        internal void WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj) {
            Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
 
            // handle null values
            if ((null == value) || (DBNull.Value == value)) { 
                WriteInt(TdsEnums.FIXEDNULL, stateObj); 
                return;
            } 

            MetaType metatype = MetaType.GetMetaTypeFromValue(value);
            int length = 0;
 
            if (metatype.IsAnsiType) {
                length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding); 
            } 

            switch (metatype.TDSType) { 
                case TdsEnums.SQLFLT4:
                    WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteFloat((Single)value, stateObj);
                    break; 

                case TdsEnums.SQLFLT8: 
                    WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); 
                    WriteDouble((Double)value, stateObj);
                    break; 

                case TdsEnums.SQLINT8:
                    WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteLong((Int64)value, stateObj); 
                    break;
 
                case TdsEnums.SQLINT4: 
                    WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteInt((Int32)value, stateObj); 
                    break;

                case TdsEnums.SQLINT2:
                    WriteSqlVariantHeader(4, metatype.TDSType, metatype.PropBytes, stateObj); 
                    WriteShort((Int16)value, stateObj);
                    break; 
 
                case TdsEnums.SQLINT1:
                    WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj); 
                    WriteByte((byte)value, stateObj);
                    break;

                case TdsEnums.SQLBIT: 
                    WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj);
                    if ((bool)value == true) 
                        WriteByte(1, stateObj); 
                    else
                        WriteByte(0, stateObj); 

                    break;

                case TdsEnums.SQLBIGVARBINARY: 
                    {
                        byte[] b = (byte[])value; 
 
                        length = b.Length;
                        WriteSqlVariantHeader(4 + length, metatype.TDSType, metatype.PropBytes, stateObj); 
                        WriteShort(length, stateObj); // propbytes: varlen
                        WriteByteArray(b, length, 0, stateObj);
                        break;
                    } 

                case TdsEnums.SQLBIGVARCHAR: 
                    { 
                        string s = (string)value;
 
                        length = s.Length;
                        WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId 
                        WriteShort(length, stateObj);
                        WriteEncodingChar(s, _defaultEncoding, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLUNIQUEID:
                    {
                        System.Guid guid = (System.Guid)value;
                        byte[] b = guid.ToByteArray(); 

                        length = b.Length; 
                        Debug.Assert(length == 16, "Invalid length for guid type in com+ object"); 
                        WriteSqlVariantHeader(18, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteByteArray(b, length, 0, stateObj); 
                        break;
                    }

                case TdsEnums.SQLNVARCHAR: 
                    {
                        string s = (string)value; 
 
                        length = s.Length * 2;
                        WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj); 
                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId
                        WriteShort(length, stateObj); // propbyte: varlen
 
                        // string takes cchar, not cbyte so convert
                        length >>= 1; 
                        WriteString(s, length, 0, stateObj); 
                        break;
                    } 

                case TdsEnums.SQLDATETIME:
                    {
                        TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8); 

                        WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); 
                        WriteInt(dt.days, stateObj); 
                        WriteInt(dt.time, stateObj);
                        break; 
                    }

                case TdsEnums.SQLMONEY:
                    { 
                        WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteCurrency((Decimal)value, 8, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLNUMERICN:
                    {
                        WriteSqlVariantHeader(21, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteByte(metatype.Precision, stateObj); //propbytes: precision 
                        WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10), stateObj); // propbytes: scale
                        WriteDecimal((Decimal)value, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLTIME:
                    WriteSqlVariantHeader(8, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteByte(metatype.Scale, stateObj); //propbytes: scale
                    WriteTime((TimeSpan)value, metatype.Scale, 5, stateObj); 
                    break;
 
                case TdsEnums.SQLDATETIMEOFFSET: 
                    WriteSqlVariantHeader(13, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteByte(metatype.Scale, stateObj); //propbytes: scale 
                    WriteDateTimeOffset((DateTimeOffset)value, metatype.Scale, 10, stateObj);
                    break;

                default: 
                    Debug.Assert(false, "unknown tds type for sqlvariant!");
                    break; 
            } // switch 
        }
 
        internal void WriteSqlVariantHeader(int length, byte tdstype, byte propbytes, TdsParserStateObject stateObj) {
            WriteInt(length, stateObj);
            WriteByte(tdstype, stateObj);
            WriteByte(propbytes, stateObj); 
        }
 
        private void WriteSqlMoney(SqlMoney value, int length, TdsParserStateObject stateObj) { 
            //
            int[] bits = Decimal.GetBits(value.Value); 

            // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
            bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
            long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0]; 

            if (isNeg) 
                l = -l; 

            if (length == 4) { 
                Decimal decimalValue = value.Value;

                // validate the value can be represented as a small money
                if (decimalValue < TdsEnums.SQL_SMALL_MONEY_MIN || decimalValue > TdsEnums.SQL_SMALL_MONEY_MAX) { 
                    throw SQL.MoneyOverflow(decimalValue.ToString(CultureInfo.InvariantCulture));
                } 
 
                WriteInt((int)l, stateObj);
            } 
            else {
                WriteInt((int)(l >> 0x20), stateObj);
                WriteInt((int)l, stateObj);
            } 
        }
 
        private void WriteCurrency(Decimal value, int length, TdsParserStateObject stateObj) { 
            SqlMoney m = new SqlMoney(value);
            int[] bits = Decimal.GetBits(m.Value); 

            // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
            bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
            long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0]; 

            if (isNeg) 
                l = -l; 

            if (length == 4) { 
                // validate the value can be represented as a small money
                if (value < TdsEnums.SQL_SMALL_MONEY_MIN || value > TdsEnums.SQL_SMALL_MONEY_MAX) {
                    throw SQL.MoneyOverflow(value.ToString(CultureInfo.InvariantCulture));
                } 

                WriteInt((int)l, stateObj); 
            } 
            else {
                WriteInt((int)(l >> 0x20), stateObj); 
                WriteInt((int)l, stateObj);
            }
        }
 
        private void WriteDate(DateTime value, TdsParserStateObject stateObj) {
            int days = value.Subtract(DateTime.MinValue).Days; 
            WriteByteArray(BitConverter.GetBytes(days), 3, 0, stateObj); 
        }
 
        private void WriteTime(TimeSpan value, byte scale, int length, TdsParserStateObject stateObj) {
            if (0 > value.Ticks || value.Ticks >= TimeSpan.TicksPerDay) {
                throw SQL.TimeOverflow(value.ToString());
            } 
            Int64 time = value.Ticks / TdsEnums.TICKS_FROM_SCALE[scale];
            WriteByteArray(BitConverter.GetBytes(time), length, 0, stateObj); 
        } 

        private void WriteDateTime2(DateTime value, byte scale, int length, TdsParserStateObject stateObj) { 
            Int64 time = value.TimeOfDay.Ticks / TdsEnums.TICKS_FROM_SCALE[scale]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
            WriteByteArray(BitConverter.GetBytes(time), length - 3, 0, stateObj);
            WriteDate(value, stateObj);
        } 

        private void WriteDateTimeOffset(DateTimeOffset value, byte scale, int length, TdsParserStateObject stateObj) { 
            WriteDateTime2(value.UtcDateTime, scale, length - 2, stateObj); 
            Int16 offset = (Int16)value.Offset.TotalMinutes;
            WriteByte((byte)(offset & 0xff), stateObj); 
            WriteByte((byte)((offset >> 8) & 0xff), stateObj);
        }

        private void ReadSqlDecimal(SqlBuffer value, int length, byte precision, byte scale, TdsParserStateObject stateObj) { 
            bool fPositive = (1 == stateObj.ReadByte());
 
            length = checked((int)length-1); 

            int[] bits = ReadDecimalBits(length, stateObj); 

            value.SetToDecimal(precision, scale, fPositive, bits);
        }
 
        // @devnote: length should be size of decimal without the sign
        // @devnote: sign should have already been read off the wire 
        private int[] ReadDecimalBits(int length, TdsParserStateObject stateObj) { 
            int[] 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 
                bits[i] = stateObj.ReadInt32();
            } 

            return bits;
        }
 
        static internal SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale) {
            if (d.Scale != newScale) { 
                return SqlDecimal.AdjustScale(d, newScale - d.Scale, false /* Don't round, truncate.  MDAC 69229 */); 
            }
 
            return d;
        }

        static internal decimal AdjustDecimalScale(decimal value, int newScale) { 
            int oldScale = (Decimal.GetBits(value)[3] & 0x00ff0000) >> 0x10;
 
            if (newScale != oldScale) { 
                SqlDecimal num = new SqlDecimal(value);
 
                num = SqlDecimal.AdjustScale(num, newScale - oldScale, false /* Don't round, truncate.  MDAC 69229 */);
                return num.Value;
            }
 
            return value;
        } 
 
        internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) {
            // sign 
            if (d.IsPositive)
                WriteByte(1, stateObj);
            else
                WriteByte(0, stateObj); 

            // four ints now 
            int[] data = d.Data; 

            WriteInt(data[0], stateObj); 
            WriteInt(data[1], stateObj);
            WriteInt(data[2], stateObj);
            WriteInt(data[3], stateObj);
        } 

        private void WriteDecimal(decimal value, TdsParserStateObject stateObj) { 
            stateObj._decimalBits = Decimal.GetBits(value); 
            Debug.Assert(null != stateObj._decimalBits, "decimalBits should be filled in at TdsExecuteRPC time");
 
            /*
             Returns a binary representation of a Decimal. The return value is an integer
             array with four elements. Elements 0, 1, and 2 contain the low, middle, and
             high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains 
             the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
             unused; bits 16-23 contain a value between 0 and 28, indicating the power of 
             10 to divide the 96-bit integer part by to produce the Decimal value; bits 24- 
             30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
             meaning positive and 1 meaning negative. 

             SQLDECIMAL/SQLNUMERIC has a byte stream of:
             struct {
                 BYTE sign; // 1 if positive, 0 if negative 
                 BYTE data[];
             } 
 
             For TDS 7.0 and above, there are always 17 bytes of data
            */ 

            // write the sign (note that COM and SQL are opposite)
            if (0x80000000 == (stateObj._decimalBits[3] & 0x80000000))
                WriteByte(0, stateObj); 
            else
                WriteByte(1, stateObj); 
 
            WriteInt(stateObj._decimalBits[0], stateObj);
            WriteInt(stateObj._decimalBits[1], stateObj); 
            WriteInt(stateObj._decimalBits[2], stateObj);
            WriteInt(0, stateObj);
        }
 
        private void WriteIdentifier(string s, TdsParserStateObject stateObj) {
            if (null != s) { 
                WriteByte(checked((byte) s.Length), stateObj); 
                WriteString(s, stateObj);
            } 
            else {
                WriteByte((byte)0, stateObj);
            }
        } 

        private void WriteIdentifierWithShortLength(string s, TdsParserStateObject stateObj) { 
            if (null != s) { 
                WriteShort(checked((short)s.Length), stateObj);
                WriteString(s, stateObj); 
            }
            else {
                WriteShort(0, stateObj);
            } 
        }
 
        private void WriteString(string s, TdsParserStateObject stateObj) { 
            WriteString(s, s.Length, 0, stateObj);
        } 

        internal void WriteCharArray(char[] carr, int length, int offset, TdsParserStateObject stateObj) {
            int cBytes = ADP.CharSize * length;
 
            // Perf shortcut: If it fits, write directly to the outBuff
            if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) { 
                CopyCharsToBytes(carr, offset, stateObj._outBuff, stateObj._outBytesUsed, length); 
                stateObj._outBytesUsed += cBytes;
            } 
            else {
                if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
                    stateObj._bTmp = new byte[cBytes];
                } 

                CopyCharsToBytes(carr, offset, stateObj._bTmp, 0, length); 
                WriteByteArray(stateObj._bTmp, cBytes, 0, stateObj); 
            }
        } 

        internal void WriteString(string s, int length, int offset, TdsParserStateObject stateObj) {
            int cBytes = ADP.CharSize * length;
 
            // Perf shortcut: If it fits, write directly to the outBuff
            if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) { 
                CopyStringToBytes(s, offset, stateObj._outBuff, stateObj._outBytesUsed, length); 
                stateObj._outBytesUsed += cBytes;
            } 
            else {
                if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
                    stateObj._bTmp = new byte[cBytes];
                } 

                CopyStringToBytes(s, offset, stateObj._bTmp, 0, length); 
                WriteByteArray(stateObj._bTmp, cBytes, 0, stateObj); 
            }
        } 


        private unsafe static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength) {
            // DEVNOTE: BE EXTREMELY CAREFULL in this method.  Since it pins the buffers and copies the memory 
            //  directly, it bypasses all of the CLR's usual array-bounds checking.  For maintainability, the checks
            //  here should NOT be removed just because some other code will check them ahead of time. 
            if (charLength < 0) { 
                throw ADP.InvalidDataLength(charLength);
            } 

            if (checked(sourceOffset + charLength) > source.Length || sourceOffset < 0) {
                throw ADP.IndexOutOfRange(sourceOffset);
            } 

            // charLength >= 0 & checked conversion implies byteLength >= 0 
            int byteLength = checked(charLength * ADP.CharSize); 

            if (checked(destOffset + byteLength) > dest.Length || destOffset < 0) { 
                throw ADP.IndexOutOfRange(destOffset);
            }

            fixed (char* sourcePtr = source) { 
                char* srcPtr = sourcePtr;  // Can't increment the target of a Fixed statement
                srcPtr += sourceOffset;  // char* increments by sizeof(char) 
                fixed (byte* destinationPtr = dest) { 
                    byte* destPtr = destinationPtr;
                    destPtr += destOffset; 
                    NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength);
                }
            }
        } 

        private unsafe static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength) { 
            // DEVNOTE: BE EXTREMELY CAREFULL in this method.  Since it pins the buffers and copies the memory 
            //  directly, it bypasses all of the CLR's usual array-bounds checking.  For maintainability, the checks
            //  here should NOT be removed just because some other code will check them ahead of time. 
            if (charLength < 0) {
                throw ADP.InvalidDataLength(charLength);
            }
 
            if (sourceOffset + charLength > source.Length || sourceOffset < 0) {
                throw ADP.IndexOutOfRange(sourceOffset); 
            } 

            // charLength >= 0 & checked conversion implies byteLength >= 0 
            int byteLength = checked(charLength * ADP.CharSize);

            if (destOffset + byteLength > dest.Length || destOffset < 0) {
                throw ADP.IndexOutOfRange(destOffset); 
            }
 
            fixed (char* sourcePtr = source) { 
                char* srcPtr = sourcePtr;  // Can't increment the target of a Fixed statement
                srcPtr += sourceOffset;  // char* increments by sizeof(char) 
                fixed (byte* destinationPtr = dest) {
                    byte* destPtr = destinationPtr;
                    destPtr += destOffset;
                    NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength); 
                }
            } 
        } 

        private void WriteEncodingChar(string s, Encoding encoding, TdsParserStateObject stateObj) { 
            WriteEncodingChar(s, s.Length, 0, encoding, stateObj);
        }

        private void WriteEncodingChar(string s, int numChars, int offset, Encoding encoding, TdsParserStateObject stateObj) { 
            char[] charData;
            byte[] byteData; 
 
            // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
            // 7.0 has no support for multiple code pages in data - single code page support only 
            if (encoding == null)
                encoding = _defaultEncoding;

            charData = s.ToCharArray(offset, numChars); 
            byteData = encoding.GetBytes(charData, 0, numChars);
            Debug.Assert(byteData != null, "no data from encoding"); 
            WriteByteArray(byteData, byteData.Length, 0, stateObj); 
        }
 
        internal int GetEncodingCharLength(string value, int numChars, int charOffset, Encoding encoding) {
            //

            if (value == null || value == ADP.StrEmpty) { 
                return 0;
            } 
 
            // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
            // 7.0 has no support for multiple code pages in data - single code page support only 
            if (encoding == null) {
                if (null == _defaultEncoding) {
                    ThrowUnsupportedCollationEncountered(null);
                } 

                encoding = _defaultEncoding; 
            } 

            char[] charData = value.ToCharArray(charOffset, numChars); 

            return encoding.GetByteCount(charData, 0, numChars);
        }
 
        //
        // Returns the data stream length of the data identified by tds type or SqlMetaData returns 
        // Returns either the total size or the size of the first chunk for partially length prefixed types. 
        //
        internal ulong GetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject stateObj) { 
            // Handle Yukon specific tokens
            if (_isYukon && colmeta.metaType.IsPlp) {
                Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
                             colmeta.tdsType == TdsEnums.SQLBIGVARCHAR || 
                             colmeta.tdsType == TdsEnums.SQLBIGVARBINARY ||
                             colmeta.tdsType == TdsEnums.SQLNVARCHAR || 
                             // Large UDTs is WinFS-only 
                             colmeta.tdsType == TdsEnums.SQLUDT,
                             "GetDataLength:Invalid streaming datatype"); 
                return stateObj.ReadPlpLength(true);
            }
            else {
                return (ulong)GetTokenLength(colmeta.tdsType, stateObj); 
            }
        } 
 
        //
        // 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 int GetTokenLength(byte token, TdsParserStateObject stateObj) {
            Debug.Assert(token != 0, "0 length token!"); 
 
            if (_isYukon) {     // Handle Yukon specific exceptions
                if (token == TdsEnums.SQLUDT) { // special case for UDTs 
                    return -1; // Should we return -1 or not call GetTokenLength for UDTs?
                }
                else if (token == TdsEnums.SQLRETURNVALUE) {
                    // In Yukon, the RETURNVALUE token stream no longer has length 
                    return -1;
                } 
                else if (token == TdsEnums.SQLXMLTYPE) { 
                    int tokenLength = (int)stateObj.ReadUInt16();
                    Debug.Assert(tokenLength == TdsEnums.SQL_USHORTVARMAXLEN, "Invalid token stream for xml datatype"); 
                    return tokenLength;
                }
            }
 
            switch (token & TdsEnums.SQLLenMask) {
                case TdsEnums.SQLFixedLen: 
                    return ((0x01 << ((token & 0x0c) >> 2))) & 0xff; 
                case TdsEnums.SQLZeroLen:
                    return 0; 
                case TdsEnums.SQLVarLen:
                case TdsEnums.SQLVarCnt:
                    if (0 != (token & 0x80)) {
                        return stateObj.ReadUInt16(); 
                    }
                    else if (0 == (token & 0x0c)) { 
                        // 
                        return stateObj.ReadInt32();
                    } 
                    else {
                        return stateObj.ReadByte();
                    }
                default: 
                    Debug.Assert(false, "Unknown token length!");
                    return 0; 
            } 
        }
 
        private void ProcessAttention(TdsParserStateObject stateObj) {
            if (_state == TdsParserState.Closed || _state == TdsParserState.Broken){
                return;
            } 
            Debug.Assert(stateObj._attentionSent, "invalid attempt to ProcessAttention, attentionSent == false!");
 
            // Attention processing scenarios: 
            // 1) EOM packet with header ST_AACK bit plus DONE with status DONE_ATTN
            // 2) Packet without ST_AACK header bit but has DONE with status DONE_ATTN 
            // 3) Secondary timeout occurs while reading, break connection

            // Since errors can occur and we need to cancel prior to throwing those errors, we
            // cache away error state and then process TDS for the attention.  We restore those 
            // errors after processing.
 
            // Lock the error collection 
            // so the error collections are kept in a known state.
            // the error collections are modified here and in ThrowExceptionAndWarning. 
            // which can come in at any time
            lock (_ErrorCollectionLock) {
                _attentionErrors = _errors;
                _attentionWarnings = _warnings; 
                _errors =  null;
                _warnings = null; 
 
                try {
                    // Call run loop to process looking for attention ack. 
                    Run(RunBehavior.Attention, null, null, null, stateObj);
                }
                catch (Exception e) {
                    // 
                    if (!ADP.IsCatchableExceptionType(e)) {
                        throw; 
                    } 

                    // If an exception occurs - break the connection. 
                    // Attention error will not be thrown in this case by Run(), but other failures may.
                    ADP.TraceExceptionWithoutRethrow(e);
                    _state = TdsParserState.Broken;
                    _connHandler.BreakConnection(); 

                  throw; 
                } 

                Debug.Assert(_errors == null && _warnings == null, "Unexpected error state on ProcessAttention"); 

                // Restore error state
                _errors = _attentionErrors;
                _warnings = _attentionWarnings; 

                _attentionErrors = null; 
                _attentionWarnings = null; 

                Debug.Assert(!stateObj._attentionSent, "Invalid attentionSent state at end of ProcessAttention"); 
            }
        }

        internal void TdsLogin(SqlLogin rec) { 
            _physicalStateObj.SetTimeoutSeconds(rec.timeout);
 
            Debug.Assert(TdsEnums.MAXLEN_HOSTNAME>=rec.hostName.Length, "_workstationId.Length exceeds the max length for this value"); 
            Debug.Assert(TdsEnums.MAXLEN_USERNAME>=rec.userName.Length, "_userID.Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_PASSWORD>=rec.password.Length, "_password.Length exceeds the max length for this value"); 
            Debug.Assert(TdsEnums.MAXLEN_APPNAME>=rec.applicationName.Length, "_applicationName.Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_SERVERNAME>=rec.serverName.Length, "_dataSource.Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_LANGUAGE>=rec.language.Length, "_currentLanguage .Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_DATABASE>=rec.database.Length, "_initialCatalog.Length exceeds the max length for this value"); 
            Debug.Assert(TdsEnums.MAXLEN_ATTACHDBFILE>=rec.attachDBFilename.Length, "_attachDBFileName.Length exceeds the max length for this value");
 
            // get the password up front to use in sspi logic below 
            byte[] encryptedPassword = null;
            encryptedPassword = TdsParserStaticMethods.EncryptPassword(rec.password); 

            byte[] encryptedChangePassword = null;
            encryptedChangePassword = TdsParserStaticMethods.EncryptPassword(rec.newPassword);
 
            // set the message type
            _physicalStateObj._outputMessageType = TdsEnums.MT_LOGIN7; 
 
            // length in bytes
            int length = TdsEnums.YUKON_LOG_REC_FIXED_LEN; 

            string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME;
            Debug.Assert(TdsEnums.MAXLEN_CLIENTINTERFACE >= clientInterfaceName.Length, "cchCltIntName can specify at most 128 unicode characters. See Tds spec");
 
            // add up variable-len portions (multiply by 2 for byte len of char strings)
            // 
            checked { 
                length += (rec.hostName.Length + rec.applicationName.Length +
                            rec.serverName.Length + clientInterfaceName.Length + 
                            rec.language.Length + rec.database.Length +
                            rec.attachDBFilename.Length) * 2;
            }
 
            // allocate memory for SSPI variables
            byte[] outSSPIBuff = null; 
            UInt32 outSSPILength = 0; 

            // only add lengths of password and username if not using SSPI 
            if (!rec.useSSPI) {
                checked {
                    length += (rec.userName.Length * 2) + encryptedPassword.Length
                    + encryptedChangePassword.Length; 
                }
            } 
            else { 
                if (rec.useSSPI) {
                    // now allocate proper length of buffer, and set length 
                    outSSPIBuff = new byte[s_maxSSPILength];
                    outSSPILength = s_maxSSPILength;

                    // Call helper function for SSPI data and actual length. 
                    // Since we don't have SSPI data from the server, send null for the
                    // byte[] buffer and 0 for the int length. 
                    Debug.Assert(SniContext.Snix_Login==_physicalStateObj.SniContext, String.Format((IFormatProvider)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj.SniContext)); 
                    _physicalStateObj.SniContext = SniContext.Snix_LoginSspi;
                    SSPIData(null, 0, outSSPIBuff, ref outSSPILength); 
                    if (outSSPILength > Int32.MaxValue) {
                        throw SQL.InvalidSSPIPacketSize();  // SqlBu 332503
                    }
                    _physicalStateObj.SniContext=SniContext.Snix_Login; 

                    checked { 
                        length += (Int32)outSSPILength; 
                    }
                } 
            }

            try {
                WriteInt(length, _physicalStateObj); 
#if WINFSFunctionality
                WriteInt((TdsEnums.WINFS_MAJOR << 24) | (TdsEnums.WINFS_INCREMENT<<16) | TdsEnums.WINFS_RTM_MINOR, _physicalStateObj); 
#else 
                WriteInt((TdsEnums.KATMAI_MAJOR << 24) | (TdsEnums.KATMAI_INCREMENT<<16) | TdsEnums.KATMAI_MINOR, _physicalStateObj);
#endif 
                WriteInt(rec.packetSize, _physicalStateObj);
                WriteInt(TdsEnums.CLIENT_PROG_VER, _physicalStateObj);
                WriteInt(TdsParserStaticMethods.GetCurrentProcessId(), _physicalStateObj); //MDAC 84718
                WriteInt(0, _physicalStateObj); // connectionID is unused 

                // Log7Flags (DWORD) 
                WriteByte(TdsEnums.USE_DB_ON << 5 | 
                          TdsEnums.INIT_DB_FATAL << 6 |
                          TdsEnums.SET_LANG_ON << 7, 
                          _physicalStateObj);
                WriteByte((byte)(TdsEnums.INIT_LANG_FATAL |
                          TdsEnums.ODBC_ON << 1 |
                          (rec.useReplication ? (TdsEnums.REPL_ON << 4) : 0) | 
                          (rec.useSSPI ? (TdsEnums.SSPI_ON << 7) : 0)),
                          _physicalStateObj); 
                WriteByte(TdsEnums.SQL_DFLT, _physicalStateObj); // TypeFlags 
                WriteByte((byte) (((byte)(ADP.IsEmpty(rec.newPassword)?0:1)) |
                                  ((byte)(rec.userInstance ? ((byte) 1) << 2 : 0))), 
                          _physicalStateObj); // OptionFlags3

                WriteInt(0, _physicalStateObj);  // ClientTimeZone is not used
                WriteInt(0, _physicalStateObj);  // LCID is unused by server 

                // Start writing offset and length of variable length portions 
                int offset = TdsEnums.YUKON_LOG_REC_FIXED_LEN; 

                // write offset/length pairs 

                // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record
                WriteShort(offset, _physicalStateObj); // host name offset
                WriteShort(rec.hostName.Length, _physicalStateObj); 
                offset += rec.hostName.Length * 2;
 
                // Only send user/password over if not fSSPI...  If both user/password and SSPI are in login 
                // rec, only SSPI is used.  Confirmed same bahavior as in luxor.
                if (rec.useSSPI == false) { 
                    WriteShort(offset, _physicalStateObj);  // userName offset
                    WriteShort(rec.userName.Length, _physicalStateObj);
                    offset += rec.userName.Length * 2;
 
                    // the encrypted password is a byte array - so length computations different than strings
                    WriteShort(offset, _physicalStateObj); // password offset 
                    WriteShort(encryptedPassword.Length / 2, _physicalStateObj); 
                    offset += encryptedPassword.Length;
                } 
                else {
                    // case where user/password data is not used, send over zeros
                    WriteShort(0, _physicalStateObj);  // userName offset
                    WriteShort(0, _physicalStateObj); 
                    WriteShort(0, _physicalStateObj);  // password offset
                    WriteShort(0, _physicalStateObj); 
                } 

                WriteShort(offset, _physicalStateObj); // app name offset 
                WriteShort(rec.applicationName.Length, _physicalStateObj);
                offset += rec.applicationName.Length * 2;

                WriteShort(offset, _physicalStateObj); // server name offset 
                WriteShort(rec.serverName.Length, _physicalStateObj);
                offset += rec.serverName.Length * 2; 
 
                WriteShort(offset, _physicalStateObj); // remote password name offset
                WriteShort(0, _physicalStateObj); 

                WriteShort(offset, _physicalStateObj); // client interface name offset
                WriteShort(clientInterfaceName.Length, _physicalStateObj);
                offset += clientInterfaceName.Length * 2; 

                WriteShort(offset, _physicalStateObj); // language name offset 
                WriteShort(rec.language.Length, _physicalStateObj); 
                offset += rec.language.Length * 2;
 
                WriteShort(offset, _physicalStateObj); // database name offset
                WriteShort(rec.database.Length, _physicalStateObj);
                offset += rec.database.Length * 2;
 
                //
 
                if (null == s_nicAddress) 
                    s_nicAddress = TdsParserStaticMethods.GetNIC();
 
                WriteByteArray(s_nicAddress, s_nicAddress.Length, 0, _physicalStateObj);

                WriteShort(offset, _physicalStateObj); // ibSSPI offset
                if (rec.useSSPI) { 
                    WriteShort((int)outSSPILength, _physicalStateObj);
                    offset += (int)outSSPILength; 
                } 
                else {
                    WriteShort(0, _physicalStateObj); 
                }

                WriteShort(offset, _physicalStateObj); // DB filename offset
                WriteShort(rec.attachDBFilename.Length, _physicalStateObj); 
                offset += rec.attachDBFilename.Length * 2;
 
                WriteShort(offset, _physicalStateObj); // reset password offset 
                WriteShort(encryptedChangePassword.Length / 2, _physicalStateObj);
 
                WriteInt(0, _physicalStateObj);        // reserved for chSSPI

                // write variable length portion
                WriteString(rec.hostName, _physicalStateObj); 

                // if we are using SSPI, do not send over username/password, since we will use SSPI instead 
                // same behavior as Luxor 
                if (!rec.useSSPI) {
                    WriteString(rec.userName, _physicalStateObj); 

                    // Cache offset in packet for tracing.
                    _physicalStateObj._tracePasswordOffset = _physicalStateObj._outBytesUsed;
                    _physicalStateObj._tracePasswordLength = encryptedPassword.Length; 

                    WriteByteArray(encryptedPassword, encryptedPassword.Length, 0, _physicalStateObj); 
                } 

                WriteString(rec.applicationName, _physicalStateObj); 
                WriteString(rec.serverName, _physicalStateObj);

                // Intentially skip, we never use this field
                // WriteString(rec.remotePassword); 
                WriteString(clientInterfaceName, _physicalStateObj);
                WriteString(rec.language, _physicalStateObj); 
                WriteString(rec.database, _physicalStateObj); 

                // send over SSPI data if we are using SSPI 
                if (rec.useSSPI)
                    WriteByteArray(outSSPIBuff, (int)outSSPILength, 0, _physicalStateObj);

                WriteString(rec.attachDBFilename, _physicalStateObj); 
                if (!rec.useSSPI) {
                    // Cache offset in packet for tracing. 
                    _physicalStateObj._traceChangePasswordOffset = _physicalStateObj._outBytesUsed; 
                    _physicalStateObj._traceChangePasswordLength = encryptedChangePassword.Length;
                    WriteByteArray(encryptedChangePassword, encryptedChangePassword.Length, 0, _physicalStateObj); 
                }

            } // try
            catch (Exception e) { 
                //
                if (ADP.IsCatchableExceptionType(e)) { 
                    // be sure to wipe out our buffer if we started sending stuff 
                    _physicalStateObj._outputPacketNumber = 1;  // end of message - reset to 1 - per ramas
                    _physicalStateObj.ResetBuffer(); 
                }

                throw;
            } 

            _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); 
            _physicalStateObj._pendingData = true; 
        }// tdsLogin
 
        private void SSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength) {
            SNISSPIData(receivedBuff, receivedLength, sendBuff, ref sendLength);
        }
 
        private void SNISSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength)
        { 
            if (receivedBuff == null) 
            {
                // we do not have SSPI data coming from server, so send over 0's for pointer and length 
                receivedLength = 0;
            }
            // we need to respond to the server's message with SSPI data
            if(0 != SNINativeMethodWrapper.SNISecGenClientContext(_physicalStateObj.Handle, receivedBuff, receivedLength, sendBuff, ref sendLength, _sniServerUserName)) 
            {
                SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT); 
            } 
        }
 

        private void ProcessSSPI(int receivedLength) {
            SniContext outerContext=_physicalStateObj.SniContext;
            _physicalStateObj.SniContext = SniContext.Snix_ProcessSspi; 
            // allocate received buffer based on length from SSPI message
            byte[] receivedBuff = new byte[receivedLength]; 
 
            // read SSPI data received from server
            _physicalStateObj.ReadByteArray(receivedBuff, 0, receivedLength); 

            // allocate send buffer and initialize length
            byte[] sendBuff = new byte[s_maxSSPILength];
            UInt32 sendLength = s_maxSSPILength; 

            // make call for SSPI data 
            SSPIData(receivedBuff, (UInt32)receivedLength, sendBuff, ref sendLength); 

            // DO NOT SEND LENGTH - TDS DOC INCORRECT!  JUST SEND SSPI DATA! 
            WriteByteArray(sendBuff, (int)sendLength, 0, _physicalStateObj);

            // set message type so server knows its a SSPI response
            _physicalStateObj._outputMessageType = TdsEnums.MT_SSPI; 

            // send to server 
            _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); 
            _physicalStateObj.SniContext=outerContext;
        } 

        private void SSPIError(string error, string procedure) {
            Debug.Assert(!ADP.IsEmpty(procedure), "TdsParser.SSPIError called with an empty or null procedure string");
            Debug.Assert(!ADP.IsEmpty(error), "TdsParser.SSPIError called with an empty or null error string"); 

            Errors.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, _server, error, procedure, 0)); 
            ThrowExceptionAndWarning(_physicalStateObj); 
        }
 
        private void LoadSSPILibrary() {
            // Outer check so we don't acquire lock once once it's loaded.
            if (!s_fSSPILoaded) {
                lock (s_tdsParserLock) { 
                    // re-check inside lock
                    if (!s_fSSPILoaded) { 
                        // use local for ref param to defer setting s_maxSSPILength until we know the call succeeded. 
                        UInt32 maxLength = 0;
                        if (0 != SNINativeMethodWrapper.SNISecInitPackage(ref maxLength)) 
                            SSPIError(SQLMessage.SSPIInitializeError(), TdsEnums.INIT_SSPI_PACKAGE);

                        s_maxSSPILength = maxLength;
                        s_fSSPILoaded = true; 

                    } 
                } 
            }
 
            if (s_maxSSPILength > Int32.MaxValue) {
                throw SQL.InvalidSSPIPacketSize();   // SqlBu 332503
            }
        } 

        internal byte[] GetDTCAddress(int timeout, TdsParserStateObject stateObj) { 
            // If this fails, the server will return a server error - Sameet Agarwal confirmed. 
            // Success: DTCAddress returned.  Failure: SqlError returned.
 
            byte[] dtcAddr = null;

            using (SqlDataReader dtcReader = TdsExecuteTransactionManagerRequest(
                                                        null, 
                                                        TdsEnums.TransactionManagerRequestType.GetDTCAddress,
                                                        null, 
                                                        TdsEnums.TransactionManagerIsolationLevel.Unspecified, 
                                                        timeout, null, stateObj, false)) {
 
                Debug.Assert(SniContext.Snix_Read==stateObj.SniContext, String.Format((IFormatProvider)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj.SniContext));
                if (null != dtcReader && dtcReader.Read()) {
                    Debug.Assert(dtcReader.GetName(0) == "TM Address", "TdsParser: GetDTCAddress did not return 'TM Address'");
 
                    // DTCAddress is of variable size, and does not have a maximum.  So we call GetBytes
                    // to get the length of the dtcAddress, then allocate a byte array of that length, 
                    // then call GetBytes again on that byte[] with the length 
                    long dtcLength = dtcReader.GetBytes(0, 0, null, 0, 0);
 
                    //
                    if (dtcLength <= Int32.MaxValue) {
                        int cb = (int)dtcLength;
 
                        dtcAddr = new byte[cb];
                        dtcReader.GetBytes(0, 0, dtcAddr, 0, cb); 
                    } 
#if DEBUG
                    else { 
                        Debug.Assert(false, "unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes");
                        // if we hit this case we'll just return a null address so that the user
                        // will get a transcaction enlistment error in the upper layers
                    } 
#endif
                } 
            } 
            return dtcAddr;
        } 

        // Propagate the dtc cookie to the server, enlisting the connection.
        internal void PropagateDistributedTransaction(byte[] buffer, int timeout, TdsParserStateObject stateObj) {
            // if this fails, the server will return a server error - Sameet Agarwal confirmed 
            // Success: server will return done token.  Failure: SqlError returned.
 
            TdsExecuteTransactionManagerRequest(buffer, 
                TdsEnums.TransactionManagerRequestType.Propagate, null,
                TdsEnums.TransactionManagerIsolationLevel.Unspecified, timeout, null, stateObj, false); 
        }

        internal SqlDataReader TdsExecuteTransactionManagerRequest(
                    byte[] buffer, 
                    TdsEnums.TransactionManagerRequestType request,
                    string transactionName, 
                    TdsEnums.TransactionManagerIsolationLevel isoLevel, 
                    int timeout,
                    SqlInternalTransaction transaction, 
                    TdsParserStateObject stateObj,
                    bool isDelegateControlRequest) {

            Debug.Assert(this == stateObj.Parser, "different parsers"); 

            if (TdsParserState.Broken == State || TdsParserState.Closed == State) { 
                return null; 
            }
 
            // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
            // delegated transactions often happen while there is an open result
            // set, so we need to handle them by using a different MARS session,
            // otherwise we'll write on the physical state objects while someone 
            // else is using it.  When we don't have MARS enabled, we need to
            // lock the physical state object to syncronize it's use at least 
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they
            // won't stomp on anything. 

            bool mustRelease = false;

            lock (_connHandler) { 
                try {
                    if (_isYukon && !MARSOn) { 
                        Monitor.Enter(_physicalStateObj); 
                        mustRelease = true;
                    } 

                    // This validation step MUST be done after locking the connection to guarantee we don't
                    //  accidentally execute after the transaction has completed on a different thread.
                    if (!isDelegateControlRequest) { 
                        _connHandler.ValidateTransaction();
                    } 
 
                    stateObj._outputMessageType = TdsEnums.MT_TRANS;       // set message type
                    stateObj.SetTimeoutSeconds(timeout); 

                    stateObj.SniContext = SniContext.Snix_Execute;

                    if (_isYukon) { 
                        WriteMarsHeader(stateObj, _currentTransaction);
                    } 
 
                    WriteShort((short)request, stateObj); // write TransactionManager Request type
 
                    bool returnReader = false;

                    switch (request) {
                        case TdsEnums.TransactionManagerRequestType.GetDTCAddress: 
                            WriteShort(0, stateObj);
 
                            returnReader = true; 
                            break;
                        case TdsEnums.TransactionManagerRequestType.Propagate: 
                            if (null != buffer) {
                                WriteShort(buffer.Length, stateObj);
                                WriteByteArray(buffer, buffer.Length, 0, stateObj);
                            } 
                            else {
                                WriteShort(0, stateObj); 
                            } 
                            break;
                        case TdsEnums.TransactionManagerRequestType.Begin: 
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
                            Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!");

                            // Only assign the passed in transaction if it is not equal to the current transaction. 
                            // And, if it is not equal, the current actually should be null.  Anything else
                            // is a unexpected state.  The concern here is mainly for the mixed use of 
                            // T-SQL and API transactions.  See SQL BU DT 345300 for full details and repro. 

                            // Expected states: 
                            // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
                            // passed in on BeginTransaction API call.
                            // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
                            // passed in but equivalent to _currentTransaction. 

                            // #1 will occur on standard BeginTransactionAPI call.  #2 should only occur if 
                            // t-sql transaction started followed by a call to SqlConnection.BeginTransaction. 
                            // Any other state is unknown.
                            if (_currentTransaction != transaction) { 
                                Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point");
                                PendingTransaction = transaction;
                            }
 
                            WriteByte((byte)isoLevel, stateObj);
 
                            WriteByte((byte)(transactionName.Length * 2), stateObj); // Write number of bytes (unicode string). 
                            WriteString(transactionName, stateObj);
                            break; 
                        case TdsEnums.TransactionManagerRequestType.Promote:
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
                            // No payload - except current transaction in header
                            // Promote returns a DTC cookie.  However, the transaction cookie we use for the 
                            // connection does not change after a promote.
                            break; 
                        case TdsEnums.TransactionManagerRequestType.Commit: 
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");
 
                            Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit");
                            WriteByte((byte)0, stateObj); // No xact name

                            WriteByte(0, stateObj);  // No flags 

                            Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); 
                            // WriteByte((byte) 0, stateObj); // IsolationLevel 
                            // WriteByte((byte) 0, stateObj); // No begin xact name
                            break; 
                        case TdsEnums.TransactionManagerRequestType.Rollback:
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");

                            WriteByte((byte)(transactionName.Length * 2), stateObj); // Write number of bytes (unicode string). 
                            WriteString(transactionName, stateObj);
 
                            WriteByte(0, stateObj);  // No flags 

                            Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); 
                            // WriteByte((byte) 0, stateObj); // IsolationLevel
                            // WriteByte((byte) 0, stateObj); // No begin xact name
                            break;
                        case TdsEnums.TransactionManagerRequestType.Save: 
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");
 
                            WriteByte((byte)(transactionName.Length * 2), stateObj); // Write number of bytes (unicode string). 
                            WriteString(transactionName, stateObj);
                            break; 
                        default:
                            Debug.Assert(false, "Unexpected TransactionManagerRequest");
                            break;
                    } 

                    stateObj.WritePacket(TdsEnums.HARDFLUSH); 
                    stateObj._pendingData = true; 

                    SqlDataReader dtcReader = null; 
                    stateObj.SniContext = SniContext.Snix_Read;
                    if (returnReader) {
                        dtcReader = new SqlDataReader(null, CommandBehavior.Default);
                        Debug.Assert(this == stateObj.Parser, "different parser"); 
                        dtcReader.Bind(stateObj);
 
                        // force consumption of metadata 
                        _SqlMetaDataSet metaData = dtcReader.MetaData;
                    } 
                    else {
                        Run(RunBehavior.UntilDone, null, null, null, stateObj);
                    }
 
                    return dtcReader;
                } 
                catch (Exception e) { 
                    //
                    if (!ADP.IsCatchableExceptionType(e)) { 
                        throw;
                    }

                    FailureCleanup(stateObj, e); 

                    throw; 
                } 
                finally {
                    // SQLHotfix 50000518 
                    // make sure we don't leave temporary fields set when leaving this function
                    _pendingTransaction = null;

                    if (mustRelease) { 
                        Monitor.Exit(_physicalStateObj);
                    } 
                } 
            }
        } 

        internal void FailureCleanup(TdsParserStateObject stateObj, Exception e) {
            int old_outputPacketNumber = stateObj._outputPacketNumber;
 
            if (Bid.TraceOn) {
                Bid.Trace(" Exception caught on ExecuteXXX: '%ls' \n", e.ToString()); 
            } 

            if (stateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount if operation failed. 
                stateObj.DecrementOpenResultCount();
            }

            // be sure to wipe out our buffer if we started sending stuff 
            stateObj.ResetBuffer();
            stateObj._outputPacketNumber = 1;  // end of message - reset to 1 - per ramas 
 
            if (old_outputPacketNumber != 1 && _state == TdsParserState.OpenLoggedIn) {
                // If _outputPacketNumber prior to ResetBuffer was not equal to 1, a packet was already 
                // sent to the server and so we need to send an attention and process the attention ack.
                stateObj.SendAttention();
                ProcessAttention(stateObj);
            } 

            Bid.Trace(" Exception rethrown. \n"); 
        } 

        internal void TdsExecuteSQLBatch(string text, int timeout, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj) { 
            if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
                return;
            }
 
            if (stateObj.BcpLock) {
                throw SQL.ConnectionLockedForBcpEvent(); 
            } 

            // SQLBUDT #20010853 - Promote, Commit and Rollback requests for 
            // delegated transactions often happen while there is an open result
            // set, so we need to handle them by using a different MARS session,
            // otherwise we'll write on the physical state objects while someone
            // else is using it.  When we don't have MARS enabled, we need to 
            // lock the physical state object to syncronize it's use at least
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they 
            // won't stomp on anything.
 
            bool mustRelease = false;

            lock (_connHandler) {
                try { 
                    if (_isYukon && !MARSOn) {
                        Monitor.Enter(_physicalStateObj); 
                        mustRelease = true; 
                    }
 
                    // This validation step MUST be done after locking the connection to guarantee we don't
                    //  accidentally execute after the transaction has completed on a different thread.
                    _connHandler.ValidateTransaction();
 
                    stateObj.SetTimeoutSeconds(timeout);
                    stateObj.SniContext = SniContext.Snix_Execute; 
 
                    if (_isYukon) {
                        WriteMarsHeader(stateObj, CurrentTransaction); 
                        if (null != notificationRequest) {
                            WriteQueryNotificationHeader(notificationRequest, stateObj);
                        }
                    } 

                    stateObj._outputMessageType = TdsEnums.MT_SQL; 
 
                    WriteString(text, text.Length, 0, stateObj);
 
                    stateObj.ExecuteFlush();
                    stateObj.SniContext = SniContext.Snix_Read;
                }
                catch (Exception e) { 
                    //Debug.Assert(_state == TdsParserState.Broken, "Caught exception in TdsExecuteSQLBatch but connection was not broken!");
                    // 
                    if (!ADP.IsCatchableExceptionType(e)) { 
                        throw;
                    } 

                    FailureCleanup(stateObj, e);

                    throw; 
                }
                finally { 
                    if (mustRelease) { 
                        Monitor.Exit(_physicalStateObj);
                    } 
                }
            }
        }
 
       internal void TdsExecuteRPC(_SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool isCommandProc) {
            if (TdsParserState.Broken == State || TdsParserState.Closed == State) { 
                return; 
            }
            _SqlRPC rpcext = null; 
            int tempLen;

            // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
            // delegated transactions often happen while there is an open result 
            // set, so we need to handle them by using a different MARS session,
            // otherwise we'll write on the physical state objects while someone 
            // else is using it.  When we don't have MARS enabled, we need to 
            // lock the physical state object to syncronize it's use at least
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they
            // won't stomp on anything.

            bool mustRelease = false; 

            lock (_connHandler) { 
                try { 
                    if (_isYukon && !MARSOn) {
                        Monitor.Enter(_physicalStateObj); 
                        mustRelease = true;
                    }

                    // This validation step MUST be done after locking the connection to guarantee we don't 
                    //  accidentally execute after the transaction has completed on a different thread.
                    _connHandler.ValidateTransaction(); 
 
                    stateObj.SetTimeoutSeconds(timeout);
                    stateObj.SniContext = SniContext.Snix_Execute; 

                    if (_isYukon) {
                        WriteMarsHeader(stateObj, CurrentTransaction);
                        if (null != notificationRequest) { 
                            WriteQueryNotificationHeader(notificationRequest, stateObj);
                        } 
                    } 

                    stateObj._outputMessageType = TdsEnums.MT_RPC; 

                    for (int ii = 0; ii < rpcArray.Length; ii++) {
                        rpcext = rpcArray[ii];
                        if (rpcext.ProcID != 0 && _isShiloh) { 
                            // Perf optimization for Shiloh and later,
                            Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255"); 
                            WriteShort(0xffff, stateObj); 
                            WriteShort((short)(rpcext.ProcID), stateObj);
                        } 
                        else {
                            Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name");
                            tempLen = rpcext.rpcName.Length;
                            WriteShort(tempLen, stateObj); 
                            WriteString(rpcext.rpcName, tempLen, 0, stateObj);
                        } 
 
                        // Options
                        WriteShort((short)rpcext.options, stateObj); 

                        // Stream out parameters
                        SqlParameter[] parameters = rpcext.parameters;
 
                        for (int i = 0; i < parameters.Length; i++) {
                            //                Debug.WriteLine("i:  " + i.ToString(CultureInfo.InvariantCulture)); 
                            // parameters can be unnamed 
                            SqlParameter param = parameters[i];
                            // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters. 
                            if (param == null)
                                break;      // End of parameters for this execute

                            // Validate parameters are not variable length without size and with null value.  MDAC 66522 
#if WINFSFunctionality
                            param.Validate(i, IsWinFS); 
#else 
                            param.Validate(i, isCommandProc);
#endif 

                            // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
                            MetaType mt = param.InternalMetaType;
 
#pragma warning disable 162
#if !UseSmiForTds 
                            if (mt.IsNewKatmaiType) { 
#endif
                                WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj); 
                                continue;
#if !UseSmiForTds
                            }
#endif 

                            if ((!_isShiloh && !mt.Is70Supported) || 
                                (!_isYukon && !mt.Is80Supported) || 
                                (!_isKatmai && !mt.Is90Supported)) {
                                throw ADP.VersionDoesNotSupportDataType(mt.TypeName); 
                            }
                            object value = null;
                            // if we have an output param, set the value to null so we do not send it across to the server
                            if (param.Direction == ParameterDirection.Output) { 
                                bool isCLRType = param.ParamaterIsSqlType;  // We have to forward the TYPE info, we need to know what type we are returning.  Once we null the paramater we will no longer be able to distinguish what type were seeing.
                                param.Value = null; 
                                value = null; 
                                param.ParamaterIsSqlType = isCLRType;
                            } 
                            else {
#if WINFSFunctionality
                             value = param.GetCoercedValue(_isWinFS);
#else 
                                value = param.GetCoercedValue();
#endif 
                            } 
                            // Check for empty or null value before we write out meta info about the actual data (size, scale, etc)
                            bool isSqlVal; 
                            bool isNull = ADP.IsNull(value, out isSqlVal);


                            string parameterName = param.ParameterNameFixed; 
                            WriteParameterName(parameterName, stateObj);
 
                            // Write parameter status 
                            WriteByte(rpcext.paramoptions[i], stateObj);
 
                            //
                            // fixup the types by using the NullableType property of the MetaType class
                            //
                            // following rules should be followed based on feedback from the M-SQL team 
                            // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
                            // 2) always use nullable types (ex: instead of SQLINT use SQLINTN) 
                            // 3) DECIMALN should always be sent as NUMERICN 
                            //
                            WriteByte(mt.NullableType, stateObj); 

                            // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
                            if (mt.TDSType == TdsEnums.SQLVARIANT) {
                                // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant 
                                // param.GetActualSize is not used
#if WINFSFunctionality 
                                WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(_isWinFS), param.Offset, stateObj); 
#else
                                WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj); 
#endif
                                continue;
                            }
 
                            // MaxLen field is only written out for non-fixed length data types
                            // use the greater of the two sizes for maxLen 
                            int actualSize; 
                            int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize();
 
                            //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
                            if (mt.TDSType != TdsEnums.SQLUDT)
                                // getting the actualSize is expensive, cache here and use below
#if WINFSFunctionality 
                                actualSize = param.GetActualSize(_isWinFS);
#else 
                                actualSize = param.GetActualSize(); 
#endif
                            else 
                                actualSize = 0; //get this later

                            int codePageByteSize = 0;
                            int maxsize = 0; 

                            if (mt.IsAnsiType) { 
                                // Avoid the following code block if ANSI but unfilled LazyMat blob 
#if WINFSFunctionality
                                if (!isNull && (!IsWinFS || !param.IsNonFilledLazyMatInstance())) 
#else
                                if (!isNull)
#endif
 { 
                                    string s;
 
                                    if (isSqlVal) { 
                                        if (value is SqlString) {
                                            s = ((SqlString)value).Value; 
                                        }
                                        else {
                                            Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype");
                                            s = new String(((SqlChars)value).Value); 
                                        }
                                    } 
                                    else { 
                                        s = (string)value;
                                    } 

                                    codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding);
                                }
 
                                if (mt.IsPlp) {
                                    WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj); 
                                } 
                                else {
                                    maxsize = (size > codePageByteSize) ? size : codePageByteSize; 
                                    if (maxsize == 0) {
                                        // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                        if (mt.IsNCharType)
                                            maxsize = 2; 
                                        else
                                            maxsize = 1; 
                                    } 

                                    WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj); 
                                }
                            }
                            else {
                                // If type timestamp - treat as fixed type and always send over timestamp length, which is 8. 
                                // For fixed types, we either send null or fixed length for type length.  We want to match that
                                // behavior for timestamps.  However, in the case of null, we still must send 8 because if we 
                                // send null we will not receive a output val.  You can send null for fixed types and still 
                                // receive a output value, but not for variable types.  So, always send 8 for timestamp because
                                // while the user sees it as a fixed type, we are actually representing it as a bigbinary which 
                                // is variable.
                                if (mt.SqlDbType == SqlDbType.Timestamp) {
                                    WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj);
                                } 
                                else if (mt.SqlDbType == SqlDbType.Udt) {
                                    byte[] udtVal = null; 
                                    bool fNull = ADP.IsNull(value); 
                                    Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;
 
                                    Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!");

                                    if (!fNull) {
                                        udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize); 

                                        Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value"); 
                                        size = udtVal.Length; 

                                        //it may be legitimate, but we dont support it yet 
#if WINFSFunctionality
                                        if ((_isWinFS && size < -1) ||
                                           ((size < 0 || size >= UInt16.MaxValue) && !_isWinFS))
#else 
                                        if (size < 0 || (size >= UInt16.MaxValue && maxsize != -1))
#endif 
                                            throw new IndexOutOfRangeException(); 
                                    }
 
                                    //if this is NULL value, write special null value
                                    byte[] lenBytes = BitConverter.GetBytes((Int64)size);

                                    if (ADP.IsEmpty(param.UdtTypeName)) 
                                        throw SQL.MustSetUdtTypeNameForUdtParams();
 
                                    // Split the input name. TypeName is returned as single 3 part name during DeriveParameters. 
                                    // NOTE: ParseUdtTypeName throws if format is incorrect
                                    String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, true /* is UdtTypeName */); 
                                    if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) {
                                        throw ADP.ArgumentOutOfRange("names");
                                    }
                                    if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) { 
                                        throw ADP.ArgumentOutOfRange("names");
                                    } 
                                    if (TdsEnums.MAX_SERVERNAME < names[2].Length) { 
                                        throw ADP.ArgumentOutOfRange("names");
                                    } 

                                    WriteUDTMetaData(value, names[0], names[1], names[2], stateObj);

                                    // 
                                    if (!fNull) {
                                        WriteUnsignedLong((ulong)udtVal.Length, stateObj); // PLP length 
                                        if (udtVal.Length > 0) { // Only write chunk length if its value is greater than 0 
                                            WriteInt(udtVal.Length, stateObj);                  // Chunk length
                                            WriteByteArray(udtVal, udtVal.Length, 0, stateObj); // Value 
                                        }
                                        WriteInt(0, stateObj);                              // Terminator
                                    }
                                    else { 
                                        WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); // PLP Null.
                                    } 
                                    continue; // End of UDT - continue to next parameter. 
                                    //
                                } 
                                else if (mt.IsPlp) {
                                    if (mt.SqlDbType != SqlDbType.Xml)
                                        WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                                } 
                                else if (mt.IsVarTime || mt.SqlDbType == SqlDbType.Date) {
                                    // This block intentionally blank.  It exists to avoid the size being written out in 
                                    //  the default else block below. 
                                }
                                else { 
                                    maxsize = (size > actualSize) ? size : actualSize;
                                    if (maxsize == 0 && IsYukonOrNewer) {
                                        // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                        if (mt.IsNCharType) 
                                            maxsize = 2;
                                        else 
                                            maxsize = 1; 
                                    }
 
                                    WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
                                }
                            }
 
                            // scale and precision are only relevant for numeric and decimal types
                            if (mt.SqlDbType == SqlDbType.Decimal) { 
                                byte precision = param.GetActualPrecision(); 
                                byte scale = param.GetActualScale();
 
                                // bug 49512, make sure the value matches the scale the user enters
                                if (!isNull) {
                                    if (isSqlVal) {
                                        value = AdjustSqlDecimalScale((SqlDecimal)value, scale); 

                                        // If Precision is specified, verify value precision vs param precision 
                                        if (precision != 0) { 
                                            if (precision < ((SqlDecimal)value).Precision) {
                                                throw ADP.ParameterValueOutOfRange((SqlDecimal)value); 
                                            }
                                        }
                                    }
                                    else { 
                                        value = AdjustDecimalScale((Decimal)value, scale);
 
                                        SqlDecimal sqlValue = new SqlDecimal((Decimal)value); 

                                        // If Precision is specified, verify value precision vs param precision 
                                        if (precision != 0) {
                                            if (precision < sqlValue.Precision) {
                                                throw ADP.ParameterValueOutOfRange((Decimal)value);
                                            } 
                                        }
                                    } 
                                } 

                                if (0 == precision) { 
                                    if (_isShiloh)
                                        WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION, stateObj);
                                    else
                                        WriteByte(TdsEnums.SPHINX_DEFAULT_NUMERIC_PRECISION, stateObj); 
                                }
                                else 
                                    WriteByte(precision, stateObj); 

                                WriteByte(scale, stateObj); 
                            }
                            else if (mt.IsVarTime) {
                                WriteByte(param.GetActualScale(), stateObj);
                            } 

                            // write out collation or xml metadata 
 
                            if (_isYukon && (mt.SqlDbType == SqlDbType.Xml)) {
                                if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) || 
                                    ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) ||
                                    ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))) {
                                    WriteByte(1, stateObj);   //Schema present flag
 
                                    if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) {
                                        tempLen = (param.XmlSchemaCollectionDatabase).Length; 
                                        WriteByte((byte)(tempLen), stateObj); 
                                        WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
                                    } 
                                    else {
                                        WriteByte(0, stateObj);       // No dbname
                                    }
 
                                    if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) {
                                        tempLen = (param.XmlSchemaCollectionOwningSchema).Length; 
                                        WriteByte((byte)(tempLen), stateObj); 
                                        WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
                                    } 
                                    else {
                                        WriteByte(0, stateObj);      // no xml schema name
                                    }
                                    if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)) { 
                                        tempLen = (param.XmlSchemaCollectionName).Length;
                                        WriteShort((short)(tempLen), stateObj); 
                                        WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj); 
                                    }
                                    else { 
                                        WriteShort(0, stateObj);       // No xml schema collection name
                                    }

                                } 
                                else {
                                    WriteByte(0, stateObj);       // No schema 
                                } 
                            }
                            else if (_isShiloh && mt.IsCharType) { 
                                // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
                                SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation;
                                Debug.Assert(_defaultCollation != null, "_defaultCollation is null!");
 
                                WriteUnsignedInt(outCollation.info, stateObj);
                                WriteByte(outCollation.sortId, stateObj); 
                            } 

                            if (0 == codePageByteSize) 
                                WriteParameterVarLen(mt, actualSize, isNull, stateObj);
                            else
                                WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj);
 
                            // write the value now
                            if (!isNull) { 
                                if (isSqlVal) { 
                                    WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj);
                                } 
                                else {
                                    // for codePageEncoded types, WriteValue simply expects the number of characters
                                    // For plp types, we also need the encoded byte size
                                    WriteValue(value, mt, param.GetActualScale(), actualSize, codePageByteSize, param.Offset, stateObj); 
                                }
                            } 
                        } // parameter for loop 
#pragma warning restore 162
 
                        // If this is not the last RPC we are sending, add the batch flag
                        if (ii < (rpcArray.Length - 1)) {
                            if (_isYukon) {
                                WriteByte(TdsEnums.YUKON_RPCBATCHFLAG, stateObj); 

                            } 
                            else { 
                                WriteByte(TdsEnums.SHILOH_RPCBATCHFLAG, stateObj);
                            } 
                        }
                    } // rpc for loop

                    stateObj.ExecuteFlush(); 
                    stateObj.SniContext = SniContext.Snix_Read;
                } 
                catch (Exception e) { 
                    //
                    if (!ADP.IsCatchableExceptionType(e)) { 
                        throw;
                    }

                    FailureCleanup(stateObj, e); 

                    throw; 
                } 
                finally {
                    if (mustRelease) { 
                        Monitor.Exit(_physicalStateObj);
                    }
                }
            } 
        }
 
        private void WriteParameterName(string parameterName, TdsParserStateObject stateObj) { 
            // paramLen
            // paramName 
            if (!ADP.IsEmpty(parameterName)) {
                Debug.Assert( parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
                int tempLen = parameterName.Length & 0xff;
                WriteByte((byte)tempLen, stateObj); 
                WriteString(parameterName, tempLen, 0, stateObj);
            } 
            else { 
                WriteByte(0, stateObj);
            } 
        }

        private static readonly IEnumerable __tvpEmptyValue = new List().AsReadOnly();
        private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj) { 
            //
            // Determine Metadata 
            // 
            ParameterPeekAheadValue peekAhead;
            MSS.SmiParameterMetaData metaData = param.MetaDataForSmi(out peekAhead); 

            if (!_isKatmai) {
                MetaType mt = MetaType.GetMetaTypeFromSqlDbType(metaData.SqlDbType, metaData.IsMultiValued);
                throw ADP.VersionDoesNotSupportDataType(mt.TypeName); 
            }
 
            // 
            //  Determine value to send
            // 
            object value;
            MSS.ExtendedClrTypeCode typeCode;

            // if we have an output or default param, set the value to null so we do not send it across to the server 
            if (sendDefault) {
                // Value for TVP default is empty list, not NULL 
                if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued) { 
                    value = __tvpEmptyValue;
                    typeCode = MSS.ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; 
                }
                else {
                    // Need to send null value for default
                    value = null; 
                    typeCode = MSS.ExtendedClrTypeCode.DBNull;
                } 
            } 
            else if (param.Direction == ParameterDirection.Output) {
                bool isCLRType = param.ParamaterIsSqlType;  // We have to forward the TYPE info, we need to know what type we are returning.  Once we null the paramater we will no longer be able to distinguish what type were seeing. 
                param.Value = null;
                value = null;
                typeCode = MSS.ExtendedClrTypeCode.DBNull;
                param.ParamaterIsSqlType = isCLRType; 
            }
            else { 
#if WINFSFunctionality 
                value = param.GetCoercedValue(_isWinFS);
#else 
                value = param.GetCoercedValue();
#endif
                typeCode = MSS.MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(
                                                    metaData.SqlDbType, metaData.IsMultiValued, value, null, MSS.SmiContextFactory.KatmaiVersion); 
            }
 
            if (Bid.AdvancedOn) { 
                Bid.Trace(" %d#, Sending parameter '%ls', default flag=%d, metadata:\n", ObjectID, paramIndex, sendDefault?1:0);
                Bid.PutStr(metaData.TraceString(3)); 
                Bid.Trace("\n");
            }

            // 
            // Write parameter metadata
            // 
            WriteSmiParameterMetaData(metaData, sendDefault, stateObj); 

            // 
            // Now write the value
            //
            TdsParameterSetter paramSetter = new TdsParameterSetter(stateObj, metaData);
            MSS.ValueUtilsSmi.SetCompatibleValueV200( 
                                        new MSS.SmiEventSink_Default(),  // TDS Errors/events dealt with at lower level for now, just need an object for processing
                                        paramSetter, 
                                        0,          // ordinal.  TdsParameterSetter only handles one parameter at a time 
                                        metaData,
                                        value, 
                                        typeCode,
                                        param.Offset,
                                        0 < param.Size ? param.Size : -1,
                                        peekAhead); 
        }
 
        // Writes metadata portion of parameter stream from an SmiParameterMetaData object. 
        private void WriteSmiParameterMetaData(MSS.SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj) {
            // Determine status 
            byte status = 0;
            if (ParameterDirection.Output == metaData.Direction || ParameterDirection.InputOutput == metaData.Direction) {
                status |= TdsEnums.RPC_PARAM_BYREF;
            } 

            if (sendDefault) { 
                status |= TdsEnums.RPC_PARAM_DEFAULT; 
            }
 
            // Write everything out
            WriteParameterName(metaData.Name, stateObj);
            WriteByte(status, stateObj);
            WriteSmiTypeInfo(metaData, stateObj); 
        }
 
        // Write a TypeInfo stream 
        // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR,  SQLNVARCHAR, and SQLBIGVARBINARY
        private void WriteSmiTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { 
            switch(metaData.SqlDbType) {
                case SqlDbType.BigInt:
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Binary: 
                    WriteByte(TdsEnums.SQLBIGBINARY, stateObj); 
                    WriteUnsignedShort(checked((ushort)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.Bit:
                    WriteByte(TdsEnums.SQLBITN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.Char:
                    WriteByte(TdsEnums.SQLBIGCHAR, stateObj); 
                    WriteUnsignedShort(checked((ushort)(metaData.MaxLength)), stateObj); 
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj); 
                    break;
                case SqlDbType.DateTime:
                    WriteByte(TdsEnums.SQLDATETIMN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Decimal: 
                    WriteByte(TdsEnums.SQLNUMERICN, stateObj); 
                    WriteByte(checked((byte)MetaType.MetaDecimal.FixedLength), stateObj);   // SmiMetaData's length and actual wire format's length are different
                    WriteByte(0 == metaData.Precision ? (byte)1 : metaData.Precision, stateObj); 
                    WriteByte(metaData.Scale, stateObj);
                    break;
                case SqlDbType.Float:
                    WriteByte(TdsEnums.SQLFLTN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.Image: 
                    WriteByte(TdsEnums.SQLBIGVARBINARY, stateObj);
                    WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); 
                    break;
                case SqlDbType.Int:
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Money: 
                    WriteByte(TdsEnums.SQLMONEYN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.NChar:
                    WriteByte(TdsEnums.SQLNCHAR, stateObj);
                    WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
                    WriteUnsignedInt(_defaultCollation.info, stateObj); // 
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break; 
                case SqlDbType.NText: 
                    WriteByte(TdsEnums.SQLNVARCHAR, stateObj);
                    WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); 
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break;
                case SqlDbType.NVarChar: 
                    WriteByte(TdsEnums.SQLNVARCHAR, stateObj);
                    if (MSS.SmiMetaData.UnlimitedMaxLengthIndicator == metaData.MaxLength) { 
                        WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); 
                    }
                    else { 
                        WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
                    }
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj); 
                    break;
                case SqlDbType.Real: 
                    WriteByte(TdsEnums.SQLFLTN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.UniqueIdentifier:
                    WriteByte(TdsEnums.SQLUNIQUEID, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.SmallDateTime:
                    WriteByte(TdsEnums.SQLDATETIMN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.SmallInt: 
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break;
                case SqlDbType.SmallMoney: 
                    WriteByte(TdsEnums.SQLMONEYN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break; 
                case SqlDbType.Text:
                    WriteByte(TdsEnums.SQLBIGVARCHAR, stateObj); 
                    WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break; 
                case SqlDbType.Timestamp:
                    WriteByte(TdsEnums.SQLBIGBINARY, stateObj); 
                    WriteShort(checked((int)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.TinyInt: 
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break;
                case SqlDbType.VarBinary: 
                    WriteByte(TdsEnums.SQLBIGVARBINARY, stateObj);
                    WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj); 
                    break; 
                case SqlDbType.VarChar:
                    WriteByte(TdsEnums.SQLBIGVARCHAR, stateObj); 
                    WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj);
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break; 
                case SqlDbType.Variant:
                    WriteByte(TdsEnums.SQLVARIANT, stateObj); 
                    WriteInt(checked((int)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Xml: 
                    WriteByte(TdsEnums.SQLXMLTYPE, stateObj);
                    // Is there a schema
                    if (ADP.IsEmpty(metaData.TypeSpecificNamePart1) && ADP.IsEmpty(metaData.TypeSpecificNamePart2) &&
                            ADP.IsEmpty(metaData.TypeSpecificNamePart3)) { 
                        WriteByte(0, stateObj);  // schema not present
                    } 
                    else { 
                        WriteByte(1, stateObj); // schema present
                        WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); 
                        WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
                        WriteIdentifierWithShortLength(metaData.TypeSpecificNamePart3, stateObj);
                    }
                    break; 
                case SqlDbType.Udt:
                    WriteByte(TdsEnums.SQLUDT, stateObj); 
                    WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); 
                    WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
                    WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj); 
                    break;
                case SqlDbType.Structured:
                    if (metaData.IsMultiValued) {
                        WriteTvpTypeInfo(metaData, stateObj); 
                    }
                    else { 
                        Debug.Assert(false, "SUDTs not yet supported."); 
                    }
                    break; 
                case SqlDbType.Date:
                    WriteByte(TdsEnums.SQLDATE, stateObj);
                    break;
                case SqlDbType.Time: 
                    WriteByte(TdsEnums.SQLTIME, stateObj);
                    WriteByte(metaData.Scale, stateObj); 
                    break; 
                case SqlDbType.DateTime2:
                    WriteByte(TdsEnums.SQLDATETIME2, stateObj); 
                    WriteByte(metaData.Scale, stateObj);
                    break;
                case SqlDbType.DateTimeOffset:
                    WriteByte(TdsEnums.SQLDATETIMEOFFSET, stateObj); 
                    WriteByte(metaData.Scale, stateObj);
                    break; 
                default: 
                    Debug.Assert(false, "Unknown SqlDbType should have been caught earlier!");
                    break; 
            }
        }

        private void WriteTvpTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { 
            Debug.Assert(SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued,
                        "Invalid metadata for TVPs. Type=" + metaData.SqlDbType); 
            // Type token 
            WriteByte((byte) TdsEnums.SQLTABLE, stateObj);
 
            // 3-part name (DB, Schema, TypeName)
            WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
            WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
            WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj); 

            // TVP_COLMETADATA 
            if (0 == metaData.FieldMetaData.Count) { 
                WriteUnsignedShort((ushort)TdsEnums.TVP_NOMETADATA_TOKEN, stateObj);
            } 
            else {
                // COUNT of columns
                WriteUnsignedShort(checked((ushort) metaData.FieldMetaData.Count), stateObj);
 
                // TvpColumnMetaData for each column (look for defaults in this loop
                MSS.SmiDefaultFieldsProperty defaults = (MSS.SmiDefaultFieldsProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.DefaultFields]; 
                for(int i=0; i's per-struct-instantiated memory costs. 
        private class TdsOrderUnique {
            internal short ColumnOrdinal;
            internal byte  Flags;
 
            internal TdsOrderUnique(short ordinal, byte flags) {
                ColumnOrdinal = ordinal; 
                Flags = flags; 
            }
        } 

        private void WriteTvpOrderUnique(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
            // TVP_ORDER_UNIQUE token (uniqueness and sort order)
 
            // Merge order and unique keys into a single token stream
 
            MSS.SmiOrderProperty orderProperty = (MSS.SmiOrderProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.SortOrder]; 
            MSS.SmiUniqueKeyProperty uniqueKeyProperty = (MSS.SmiUniqueKeyProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.UniqueKey];
 
            // Build list from
            List columnList = new List(metaData.FieldMetaData.Count);
            for(int i=0; i timeout) {
                    throw ADP.ArgumentOutOfRange("Timeout");
                } 

                Bid.NotificationsTrace(" NotificationRequest: userData: '%ls', options: '%ls', timeout: '%d'\n", notificationRequest.UserData, notificationRequest.Options, notificationRequest.Timeout); 
 
                // Total Length (uint) (included in MARS header written previously)
 
                // Header Length (uint) (included in size) (already written to output buffer)
                // Header Type (ushort)
                // NotifyID Length (ushort)
                // NotifyID UnicodeStream (unicode text) 
                // SSBDeployment Length (ushort)
                // SSBDeployment UnicodeStream (unicode text) 
                // Timeout (uint) -- optional 
                // WEBDATA 102263: Don't send timeout value if it is 0
 
                // This is equal to sum of comments above.
                int headerLength = 4 + 2 + 2 + (callbackId.Length * 2) + 2 + (service.Length * 2);

                if (timeout > 0) 
                    headerLength += 4;
 
                // Total length = sum of QueryNotification header plus sum of previous headers (MARS header). 
                int totalHeaderLength = headerLength + stateObj._outBytesUsed - TdsEnums.HEADER_LEN;
 
                // override previous headers (MARS) length field
                int save_outBytesUsed = stateObj._outBytesUsed;

                stateObj._outBytesUsed = TdsEnums.HEADER_LEN; 
                WriteInt(totalHeaderLength, stateObj);
                stateObj._outBytesUsed = save_outBytesUsed; 
                WriteInt(headerLength, stateObj); 

                WriteShort(1, stateObj);      // Query notifications Type 

                WriteShort(callbackId.Length * 2, stateObj); // Length in bytes
                WriteString(callbackId, stateObj);
 
                WriteShort(service.Length * 2, stateObj); // Length in bytes
                WriteString(service, stateObj); 
                if (timeout > 0) 
                    WriteInt(timeout, stateObj);
            } 
        }

#if STREAMINGPARAMETERS
        // 
        // Writes the length of the data before the data itself
        //      Handles partially length prefixed types correctly 
        // 
        private void WriteDataLength(SqlMetaDataPriv colmeta, ulong longlen, TdsParserStateObject stateObj) {
            // Handle Yukon specific tokens 
            if ((_isYukon) && (colmeta != null && colmeta.metaType.IsPlp)) {
                Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
                             colmeta.tdsType == TdsEnums.SQLBIGVARCHAR ||
                             colmeta.tdsType == TdsEnums.SQLBIGVARBINARY || 
                             colmeta.tdsType == TdsEnums.SQLNVARCHAR,
                             "GetDataLength:Invalid streaming datatype"); 
 
                WriteUnsignedLong(longlen, stateObj);
            } 
            else {
                Debug.Assert(longlen <= 0xffffffffL, "Bad length for data");
                WriteTokenLength(colmeta.tdsType, (int)longlen, stateObj);
            } 

            return; 
        } 
#endif //STREAMINGPARAMETERS
 
        //
        // Reverse function of GetTokenLength
        //
        private void WriteTokenLength(byte token, int length, TdsParserStateObject stateObj) { 
            int tokenLength = 0;
 
            Debug.Assert(token != 0, "0 length token!"); 

            // For Plp fields, this should only be used when writing to metadata header. 
            // For actual data length, WriteDataLength should be used.
            // For Xml fields, there is no token length field. For MAX fields it is 0xffff.
            if (_isYukon) {     // Handle Yukon specific exceptions
                if (TdsEnums.SQLUDT == token) { 
                    tokenLength = 8;
                } 
                else if (token == TdsEnums.SQLXMLTYPE) { 
                    tokenLength = 8;
                } 
            }

            if (tokenLength == 0) {
                switch (token & TdsEnums.SQLLenMask) { 
                    case TdsEnums.SQLFixedLen:
                        Debug.Assert(length == 0x01 << ((token & 0x0c) >> 2), "length does not match encoded length in token"); 
                        tokenLength = 0; 
                        break;
 
                    case TdsEnums.SQLZeroLen:
                        tokenLength = 0;
                        break;
 
                    case TdsEnums.SQLVarLen:
                    case TdsEnums.SQLVarCnt: 
                        if (0 != (token & 0x80)) 
                            tokenLength = 2;
                        else if (0 == (token & 0x0c)) 

                            //
                            tokenLength = 4;
                        else 
                            tokenLength = 1;
 
                        break; 

                    default: 
                        Debug.Assert(false, "Unknown token length!");
                        break;
                }
 
                switch (tokenLength) {
                    case 1: 
                        WriteByte((byte)length, stateObj); 
                        break;
 
                    case 2:
                        WriteShort(length, stateObj);
                        break;
 
                    case 4:
                        WriteInt(length, stateObj); 
                        break; 

                    case 8: 
                        // In the metadata case we write 0xffff for partial length prefixed types.
                        //  For actual data length preceding data, WriteDataLength should be used.
                        WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                        break; 
                } // end switch
            } 
        } 

        // Returns true if BOM byte mark is needed for an XML value 
        private bool IsBOMNeeded(MetaType type, object value) {
            if (type.NullableType == TdsEnums.SQLXMLTYPE) {
                Type currentType = value.GetType();
 
                if(currentType == typeof(SqlString)) {
                    if (!((SqlString)value).IsNull && ((((SqlString)value).Value).Length > 0)) { 
                        if ((((SqlString)value).Value[0] & 0xff) != 0xff) 
                            return true;
                    } 
                }
                else if ((currentType == typeof(String)) && (((String)value).Length > 0)) {
                    if ((value != null) && (((String)value)[0] & 0xff) != 0xff)
                        return true; 
                }
                else if (currentType == typeof(SqlXml)) { 
                    if (!((SqlXml)value).IsNull) 
                        return true;
                } 
                else if (currentType ==typeof(XmlReader)) {
                    return true;             // Values will eventually converted to unicode string here
                }
            } 
            return false;
        } 
 
        // For MAX types, this method can only write everything in one big chunk. If multiple
        // chunk writes needed, please use WritePlpBytes/WritePlpChars 
        private void WriteSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) {
            Debug.Assert(((type.NullableType == TdsEnums.SQLXMLTYPE) ||
                   (value is INullable && !((INullable)value).IsNull)),
                   "unexpected null SqlType!"); 

            // parameters are always sent over as BIG or N types 
            switch (type.NullableType) { 
                case TdsEnums.SQLFLTN:
                    if (type.FixedLength == 4) 
                        WriteFloat(((SqlSingle)value).Value, stateObj);
                    else {
                        Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
                        WriteDouble(((SqlDouble)value).Value, stateObj); 
                    }
 
                    break; 

                case TdsEnums.SQLBIGBINARY: 
                case TdsEnums.SQLBIGVARBINARY:
                case TdsEnums.SQLIMAGE:
                    {
                        if (type.IsPlp) { 
                            WriteInt(actualLength, stateObj);               // chunk length
                        } 
 
                        if (value is SqlBinary) {
                            WriteByteArray(((SqlBinary)value).Value, actualLength, offset, stateObj); 
                        }
                        else {
                            Debug.Assert(value is SqlBytes);
                            WriteByteArray(((SqlBytes)value).Value, actualLength, offset, stateObj); 
                        }
                        break; 
                    } 

                case TdsEnums.SQLUNIQUEID: 
                    {
                        byte[] b = ((SqlGuid)value).ToByteArray();

                        Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object"); 
                        WriteByteArray(b, actualLength, 0, stateObj);
                        break; 
                    } 

                case TdsEnums.SQLBITN: 
                    {
                        Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
                        if (((SqlBoolean)value).Value == true)
                            WriteByte(1, stateObj); 
                        else
                            WriteByte(0, stateObj); 
 
                        break;
                    } 

                case TdsEnums.SQLINTN:
                    if (type.FixedLength == 1)
                        WriteByte(((SqlByte)value).Value, stateObj); 
                    else
                    if (type.FixedLength == 2) 
                        WriteShort(((SqlInt16)value).Value, stateObj); 
                    else
                        if (type.FixedLength == 4) 
                        WriteInt(((SqlInt32)value).Value, stateObj);
                    else {
                        Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
                        WriteLong(((SqlInt64)value).Value, stateObj); 
                    }
 
                    break; 

                case TdsEnums.SQLBIGCHAR: 
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT:
                    if (type.IsPlp) {
                        WriteInt(codePageByteSize, stateObj);               // chunk length 
                    }
                    if (value is SqlChars) { 
                        String sch = new String(((SqlChars)value).Value); 

                        WriteEncodingChar(sch, actualLength, offset, _defaultEncoding, stateObj); 
                    }
                    else {
                        Debug.Assert(value is SqlString);
                        WriteEncodingChar(((SqlString)value).Value, actualLength, offset, _defaultEncoding, stateObj); 
                    }
 
                    break; 

                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR:
                case TdsEnums.SQLNTEXT:
                case TdsEnums.SQLXMLTYPE:
 
                    if (type.IsPlp) {
                        if(IsBOMNeeded(type, value)) { 
                            WriteInt(actualLength+2, stateObj);               // chunk length 
                            WriteShort(TdsEnums.XMLUNICODEBOM , stateObj);
                        } else { 
                            WriteInt(actualLength, stateObj);               // chunk length
                        }
                    }
 
                    // convert to cchars instead of cbytes
                    // Xml type is already converted to string through GetCoercedValue 
                    if (actualLength != 0) 
                        actualLength >>= 1;
 
                    if (value is SqlChars) {
                        WriteCharArray(((SqlChars)value).Value, actualLength, offset, stateObj);
                    }
                    else { 
                        Debug.Assert(value is SqlString);
                        WriteString(((SqlString)value).Value, actualLength, offset, stateObj); 
                    } 
                    break;
 
                case TdsEnums.SQLNUMERICN:
                    Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
                    WriteSqlDecimal((SqlDecimal)value, stateObj);
                    break; 

                case TdsEnums.SQLDATETIMN: 
                    SqlDateTime dt = (SqlDateTime)value; 

                    if (type.FixedLength == 4) { 
                        if (0 > dt.DayTicks || dt.DayTicks > UInt16.MaxValue)
                            throw SQL.SmallDateTimeOverflow(dt.ToString());

                        WriteShort(dt.DayTicks, stateObj); 
                        WriteShort(dt.TimeTicks / SqlDateTime.SQLTicksPerMinute, stateObj);
                    } 
                    else { 
                        WriteInt(dt.DayTicks, stateObj);
                        WriteInt(dt.TimeTicks, stateObj); 
                    }

                    break;
 
                case TdsEnums.SQLMONEYN:
                    { 
                        WriteSqlMoney((SqlMoney)value, type.FixedLength, stateObj); 
                        break;
                    } 

                case TdsEnums.SQLUDT:
                    Debug.Assert(false, "Called WriteSqlValue on UDT param.Should have already been handled");
                    throw SQL.UDTUnexpectedResult(value.GetType().AssemblyQualifiedName); 

                default: 
                    Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); 
                    break;
            } // switch 

            if (type.IsPlp && (actualLength > 0)) {
                WriteInt(0, stateObj);               // chunk terminator
            } 
            // Debug.WriteLine("value:  " + value.ToString(CultureInfo.InvariantCulture));
        } 
 
        // For MAX types, this method can only write everything in one big chunk. If multiple
        // chunk writes needed, please use WritePlpBytes/WritePlpChars 
        private void WriteValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj) {
            Debug.Assert((null != value) && (DBNull.Value != value), "unexpected missing or empty object");

            // parameters are always sent over as BIG or N types 
            switch (type.NullableType) {
                case TdsEnums.SQLFLTN: 
                    if (type.FixedLength == 4) 
                        WriteFloat((Single)value, stateObj);
                    else { 
                        Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
                        WriteDouble((Double)value, stateObj);
                    }
 
                    break;
 
                case TdsEnums.SQLBIGBINARY: 
                case TdsEnums.SQLBIGVARBINARY:
                case TdsEnums.SQLIMAGE: 
                case TdsEnums.SQLUDT: {
                        // An array should be in the object
                        Debug.Assert(value.GetType() == typeof(byte[]), "Value should be an array of bytes");
 
                        Byte[] b = (byte[])value;
 
                        if (type.IsPlp) { 
                            WriteInt(actualLength, stateObj);               // chunk length
                        } 
                        WriteByteArray(b, actualLength, offset, stateObj);
                        break;
                    }
 
                case TdsEnums.SQLUNIQUEID: {
                        System.Guid guid = (System.Guid)value; 
                        byte[] b = guid.ToByteArray(); 

                        Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object"); 
                        WriteByteArray(b, actualLength, 0, stateObj);
                        break;
                    }
 
                case TdsEnums.SQLBITN: {
                        Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type"); 
                        if ((bool)value == true) 
                            WriteByte(1, stateObj);
                        else 
                            WriteByte(0, stateObj);

                        break;
                    } 

                case TdsEnums.SQLINTN: 
                    if (type.FixedLength == 1) 
                        WriteByte((byte)value, stateObj);
                    else if (type.FixedLength == 2) 
                        WriteShort((Int16)value, stateObj);
                    else if (type.FixedLength == 4)
                        WriteInt((Int32)value, stateObj);
                    else { 
                        Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
                        WriteLong((Int64)value, stateObj); 
                    } 

                    break; 

                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT: 
                    if (type.IsPlp) {
                        WriteInt(encodingByteSize, stateObj);               // chunk length 
                    } 
                    if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
                        WriteByteArray((byte[]) value, actualLength, 0, stateObj); 
                    }
                    else {
                        WriteEncodingChar((string)value, actualLength, offset, _defaultEncoding, stateObj);
                    } 
                    break;
 
                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR:
                case TdsEnums.SQLNTEXT: 
                case TdsEnums.SQLXMLTYPE:
                    if (type.IsPlp) {
                        if(IsBOMNeeded(type, value)) {
                            WriteInt(actualLength+2, stateObj);               // chunk length 
                            WriteShort(TdsEnums.XMLUNICODEBOM , stateObj);
                        } else { 
                            WriteInt(actualLength, stateObj);               // chunk length 
                        }
                    } 
                    if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
                        WriteByteArray((byte[]) value, actualLength, 0, stateObj);
                    }
                    else { 
                        // convert to cchars instead of cbytes
                        actualLength >>= 1; 
                        WriteString((string)value, actualLength, offset, stateObj); 
                    }
                    break; 

                case TdsEnums.SQLNUMERICN:
                    Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
                    WriteDecimal((Decimal)value, stateObj); 
                    break;
 
                case TdsEnums.SQLDATETIMN: 
                    Debug.Assert(type.FixedLength <= 0xff, "Invalid Fixed Length");
 
                    TdsDateTime dt = MetaType.FromDateTime((DateTime)value, (byte)type.FixedLength);

                    if (type.FixedLength == 4) {
                        if (0 > dt.days || dt.days > UInt16.MaxValue) 
                            throw SQL.SmallDateTimeOverflow(MetaType.ToDateTime(dt.days, dt.time, 4).ToString(CultureInfo.InvariantCulture));
 
                        WriteShort(dt.days, stateObj); 
                        WriteShort(dt.time, stateObj);
                    } 
                    else {
                        WriteInt(dt.days, stateObj);
                        WriteInt(dt.time, stateObj);
                    } 

                    break; 
 
                case TdsEnums.SQLMONEYN: {
                        WriteCurrency((Decimal)value, type.FixedLength, stateObj); 
                        break;
                    }

                case TdsEnums.SQLDATE: { 
                        WriteDate((DateTime)value, stateObj);
                        break; 
                    } 

                case TdsEnums.SQLTIME: 
                        if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
                            throw SQL.TimeScaleValueOutOfRange(scale);
                        }
                        WriteTime((TimeSpan)value, scale, actualLength, stateObj); 
                        break;
 
                case TdsEnums.SQLDATETIME2: 
                        if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
                            throw SQL.TimeScaleValueOutOfRange(scale); 
                        }
                        WriteDateTime2((DateTime)value, scale, actualLength, stateObj);
                        break;
 
                case TdsEnums.SQLDATETIMEOFFSET:
                        WriteDateTimeOffset((DateTimeOffset)value, scale, actualLength, stateObj); 
                        break; 

                default: 
                    Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null));
                    break;
            } // switch
            if (type.IsPlp && (actualLength > 0)) { 
                WriteInt(0, stateObj);               // chunk terminator
            } 
 
            // Debug.WriteLine("value:  " + value.ToString(CultureInfo.InvariantCulture));
        } 

        //
        // we always send over nullable types for parameters so we always write the varlen fields
        // 

        internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsParserStateObject stateObj) { 
            if (type.IsLong) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths 
                if (isNull) {
                    if (type.IsPlp) { 
                        WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
                    }
                    else {
                        WriteInt(unchecked((int)TdsEnums.VARLONGNULL), stateObj); 
                    }
                } 
                else if (type.NullableType == TdsEnums.SQLXMLTYPE) { 
                    WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
                } 
                else if (type.IsPlp) {
                    // Non-xml plp types
                    WriteLong((long)size, stateObj);
                } 
                else {
                    WriteInt(size, stateObj); 
                } 
            }
            else if (type.IsVarTime) { 
                if (isNull) {
                    WriteByte(TdsEnums.FIXEDNULL, stateObj);
                }
                else { 
                    WriteByte((byte)size, stateObj);
                } 
            } 
            else if (false == type.IsFixed) { // non-long but variable length column, must be a BIG* type: 2 byte length
                if (isNull) { 
                    WriteShort(TdsEnums.VARNULL, stateObj);
                }
                else {
                    WriteShort(size, stateObj); 
                }
            } 
            else { 
                if (isNull) {
                    WriteByte(TdsEnums.FIXEDNULL, stateObj); 
                }
                else {
                    Debug.Assert(type.FixedLength <= 0xff, "WriteParameterVarLen: invalid one byte length!");
                    WriteByte((byte)(type.FixedLength & 0xff), stateObj); // 1 byte for everything else 
                }
            } 
        } 

        // Reads the next chunk in a nvarchar(max) data stream. 
        // This call must be preceeded by a call to ReadPlpLength or ReadDataLength.
        // Will not start reading into the next chunk if bytes requested is larger than
        // the current chunk length. Do another ReadPlpLength, ReadPlpUnicodeChars in that case.
        // Returns the actual chars read 
        private int ReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsParserStateObject stateObj) {
            int charsRead; 
 
            Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
            Debug.Assert((stateObj._longlen != 0) && (stateObj._longlen != TdsEnums.SQL_PLP_NULL), 
                        "Out of [....] plp read request");
            if (stateObj._longlenleft == 0) {
                Debug.Assert(false, "Out of [....] read request");
                return 0; 
            }
 
            charsRead = len; 

            // stateObj._longlenleft is in bytes 
            if ((stateObj._longlenleft >> 1) < (ulong)len)
                charsRead = (int)(stateObj._longlenleft >> 1);

            for (int ii = 0; ii < charsRead; ii++) 
                buff[offst + ii] = stateObj.ReadChar();
 
            stateObj._longlenleft -= ((ulong)charsRead << 1); 
            return charsRead;
        } 

        // Reads the requested number of chars from a plp data stream, or the entire data if
        // requested length is -1 or larger than the actual length of data. First call to this method
        //  should be preceeded by a call to ReadPlpLength or ReadDataLength. 
        // Returns the actual chars read.
        internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj) { 
            int charsRead = 0; 
            int charsLeft = 0;
            int totalcharsRead = 0; 
            char[] newbuf;

            if (stateObj._longlen == 0) {
                Debug.Assert(stateObj._longlenleft == 0); 
                return 0;       // No data
            } 
 
            Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
                    "Out of [....] plp read request"); 

            Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
            charsLeft = len;
 
            // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time
            if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) { 
                buff = new char[(int)Math.Min((int)stateObj._longlen, len)]; 
            }
 
            if (stateObj._longlenleft == 0) {
                stateObj.ReadPlpLength(false);
                if (stateObj._longlenleft == 0)  // Data read complete
                    return 0; 
            }
 
            while (charsLeft > 0) { 
                charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
                if ((buff == null) || (buff.Length < (offst + charsRead))) { 
                    // Grow the array
                    newbuf = new char[offst + charsRead];
                    if (buff != null) {
                        Buffer.BlockCopy(buff, 0, newbuf, 0, offst*2); 
                    }
                    buff = newbuf; 
                } 
                if (charsRead > 0) {
                    charsRead = ReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj); 
                    charsLeft -= charsRead;
                    offst += charsRead;
                    totalcharsRead += charsRead;
                } 
                // Special case single byte left
                if (stateObj._longlenleft == 1 && (charsLeft > 0)) { 
                    byte  b1 = stateObj.ReadByte(); 
                    stateObj._longlenleft--;
                    stateObj.ReadPlpLength(false); 
                    Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
                    byte b2 = stateObj.ReadByte();
                    stateObj._longlenleft--;
                    // Put it at the end of the array. At this point we know we have an extra byte. 
                    buff[offst] = (char)(((b2 & 0xff) << 8) + (b1 & 0xff));
                    offst = checked((int)offst + 1); 
                    charsRead++; 
                    charsLeft--;
                    totalcharsRead++; 
                }
                if (stateObj._longlenleft == 0)  // Read the next chunk or cleanup state if hit the end
                    stateObj.ReadPlpLength(false);
 
                if (stateObj._longlenleft == 0)   // Data read complete
                    break; 
            } 
            return (totalcharsRead);
        } 

        internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj) {
            int charsRead = 0;
            int charsLeft = 0; 
            int bytesRead = 0;
            int totalcharsRead = 0; 
 
            if (stateObj._longlen == 0) {
                Debug.Assert(stateObj._longlenleft == 0); 
                return 0;       // No data
            }

            Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), 
                    "Out of [....] plp read request");
 
            Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpAnsiChars()!"); 
            charsLeft = len;
 
            if (stateObj._longlenleft == 0) {
                stateObj.ReadPlpLength(false);
                if (stateObj._longlenleft == 0)  // Data read complete
                    return 0; 
            }
 
            Encoding enc = metadata.encoding; 

            if (enc == null) { 
                if (null == _defaultEncoding) {
                    ThrowUnsupportedCollationEncountered(stateObj);
                }
 
                enc = _defaultEncoding;
            } 
 
            while (charsLeft > 0) {
                bytesRead = (int)Math.Min(stateObj._longlenleft, (ulong)charsLeft); 
                if ((stateObj._bTmp == null) || (stateObj._bTmp.Length < bytesRead)) {
                    // Grow the array
                    stateObj._bTmp = new byte[bytesRead];
                } 

                bytesRead = stateObj.ReadPlpBytesChunk(stateObj._bTmp, 0, bytesRead); 
 
                //
                charsRead = enc.GetChars(stateObj._bTmp, 0, bytesRead, buff, offst); 
                charsLeft -= charsRead;
                offst += charsRead;
                totalcharsRead += charsRead;
                if (stateObj._longlenleft == 0)  // Read the next chunk or cleanup state if hit the end 
                    stateObj.ReadPlpLength(false);
 
                if (stateObj._longlenleft == 0)   // Data read complete 
                    break;
            } 
            return (totalcharsRead);
        }

        internal ulong SkipPlpValue(ulong cb, TdsParserStateObject stateObj) { 
            // Read and skip cb bytes or until  ReadPlpLength returns 0.
            int bytesSkipped; 
            ulong totalBytesSkipped = 0; 

            if (stateObj._longlenleft == 0) 
                stateObj.ReadPlpLength(false);

            while ((totalBytesSkipped < cb) &&
                    (stateObj._longlenleft > 0)) { 
                if (stateObj._longlenleft > Int32.MaxValue)
                    bytesSkipped = Int32.MaxValue; 
                else 
                    bytesSkipped = (int)stateObj._longlenleft;
                bytesSkipped = ((cb - totalBytesSkipped) < (ulong) bytesSkipped) ? (int)(cb - totalBytesSkipped) : bytesSkipped; 

                SkipBytes(bytesSkipped, stateObj);
                stateObj._longlenleft -= (ulong)bytesSkipped;
                totalBytesSkipped += (ulong)bytesSkipped; 

                if (stateObj._longlenleft == 0) 
                    stateObj.ReadPlpLength(false); 
            }
 
            return totalBytesSkipped;
        }

        internal ulong PlpBytesLeft(TdsParserStateObject stateObj) { 
            if ((stateObj._longlen != 0) && (stateObj._longlenleft == 0))
                stateObj.ReadPlpLength(false); 
 
            return stateObj._longlenleft;
        } 

        private const ulong _indeterminateSize = 0xffffffffffffffff;        // Represents unknown size

        internal ulong PlpBytesTotalLength(TdsParserStateObject stateObj) { 
            if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN)
                return _indeterminateSize; 
            else if (stateObj._longlen == TdsEnums.SQL_PLP_NULL) 
                return 0;
 
            return stateObj._longlen;
        }

#if STREAMINGPARAMETERS 
        // Writes the plp data byte stream into tds buffer
        // Needs to know if this is the first or last chunk data 
        // 
        internal void WritePlpBytes(byte[] buff, int offst, int len, bool isLastWrite, bool isFirstWrite, TdsParserStateObject stateObj) {
            int bytesLeft; 
            int bytesToWrite;

            Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to WritePlpBytes()!");
            Debug.Assert(stateObj._longlenleft == 0, "Out of [....] plp write request"); 
            if (isFirstWrite) {
                // This is the first write, so we send the PLP length type first 
                if (isLastWrite) { 
                    WriteLong((long)len, stateObj);
                    stateObj._longlen = 0; 
                    bytesLeft = len;
                }
                else if (len == 0) {
                    WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); 
                    stateObj._longlen = 0;
                    bytesLeft = 0; 
                } 
                else {
                    // Write in chunks 
                    stateObj._longlen = TdsEnums.SQL_PLP_UNKNOWNLEN;
                    WriteUnsignedLong(stateObj._longlen, stateObj);
                    bytesLeft = len;
                } 
            }
            else { 
                Debug.Assert(stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN, "Invalid Plp data stream state"); 
                bytesLeft = len;
            } 

            while (bytesLeft > 0) {
                bytesToWrite = bytesLeft;
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN) { 
                    if (bytesToWrite > (int)TdsEnums.SQL_USHORTVARMAXLEN)
                        bytesToWrite = (int)TdsEnums.SQL_USHORTVARMAXLEN; 
 
                    WriteShort((short)bytesToWrite, stateObj);
                } 

                WriteByteArray(buff, bytesToWrite, offst, stateObj);
                offst += bytesToWrite;
                bytesLeft -= bytesToWrite; 
            }
 
            if (isLastWrite) { 
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN)
                    WriteShort(TdsEnums.SQL_PLP_CHUNK_TERMINATOR, stateObj); 

                stateObj._longlen = 0;
            }
        } 

        // Writes the plp data char stream into tds buffer 
        internal void WritePlpChars(char[] buff, int offst, int len, bool isLastWrite, bool isFirstWrite, TdsParserStateObject stateObj) { 
            int charsLeft;
            int charsToWrite; 
            byte[] byteData = null;

            Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to WritePlpBytes()!");
            Debug.Assert(stateObj._longlenleft == 0, "Out of [....] plp write request"); 
            if (isFirstWrite) {
                // This is the first write, so we send the PLP length type first 
                if (isLastWrite) { 
                    WriteLong((long)len, stateObj);
                    stateObj._longlen = 0; 
                    charsLeft = len;
                }
                else if (len == 0) {
                    WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); 
                    stateObj._longlen = 0;
                    charsLeft = 0; 
                } 
                else {
                    // Write in chunks 
                    stateObj._longlen = TdsEnums.SQL_PLP_UNKNOWNLEN;
                    WriteUnsignedLong(stateObj._longlen, stateObj);
                    charsLeft = len;
                } 
            }
            else { 
                Debug.Assert(stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN, "Invalid Plp data stream state"); 
                charsLeft = len;
            } 

            while (charsLeft > 0) {
                charsToWrite = charsLeft;
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN) { 
                    if (charsToWrite > (int)TdsEnums.SQL_USHORTVARMAXLEN)
                        charsToWrite = (int)TdsEnums.SQL_USHORTVARMAXLEN; 
 
                    WriteShort((short)charsToWrite, stateObj);
                } 

                if (byteData == null) {
                    byteData = new byte[ADP.CharSize * charsToWrite];
                } 

                CopyCharsToBytes(buff, offst, byteData, 0, charsToWrite); 
                WriteByteArray(byteData, ADP.CharSize * charsToWrite, 0, stateObj); 
                offst += charsToWrite;
                charsLeft -= charsToWrite; 
            }

            if (isLastWrite) {
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN) 
                    WriteShort(TdsEnums.SQL_PLP_CHUNK_TERMINATOR, stateObj);
 
                stateObj._longlen = 0; 
            }
        } 
#endif //STREAMINGPARAMETERS

        const string StateTraceFormatString = "\n\t"
                                        + "         _physicalStateObj = {0}\n\t" 
                                        + "         _pMarsPhysicalConObj = {1}\n\t"
                                        + "         _state = {2}\n\t" 
                                        + "         _server = {3}\n\t" 
                                        + "         _fResetConnection = {4}\n\t"
                                        + "         _defaultCollation = {5}\n\t" 
                                        + "         _defaultCodePage = {6}\n\t"
                                        + "         _defaultLCID = {7}\n\t"
                                        + "         _defaultEncoding = {8}\n\t"
                                        + "         _encryptionOption = {10}\n\t" 
                                        + "         _currentTransaction = {11}\n\t"
                                        + "         _pendingTransaction = {12}\n\t" 
                                        + "         _retainedTransactionId = {13}\n\t" 
                                        + "         _nonTransactedOpenResultCount = {14}\n\t"
                                        + "         _connHandler = {15}\n\t" 
                                        + "         _fAsync = {16}\n\t"
                                        + "         _fMARS = {17}\n\t"
                                        + "         _fAwaitingPreLogin = {18}\n\t"
                                        + "         _fPreLoginErrorOccurred = {19}\n\t" 
                                        + "         _sessionPool = {20}\n\t"
                                        + "         _isShiloh = {21}\n\t" 
                                        + "         _isShilohSP1 = {22}\n\t" 
                                        + "         _isYukon = {23}\n\t"
                                        + "         _sniServerUserName = {24}\n\t" 
                                        + "         _errors = {25}\n\t"
                                        + "         _warnings = {26}\n\t"
                                        + "         _attentionErrors = {27}\n\t"
                                        + "         _attentionWarnings = {28}\n\t" 
                                        + "         _statistics = {29}\n\t"
                                        + "         _statisticsIsInTransaction = {30}\n\t" 
                                        + "         _fPreserveTransaction = {31}" 
                                        ;
        internal string TraceString() { 
            return String.Format(/*IFormatProvider*/ null,
                            StateTraceFormatString,
                            null == _physicalStateObj,
                            null == _pMarsPhysicalConObj, 
                            _state,
                            _server, 
                            _fResetConnection, 
                            null == _defaultCollation ? "(null)" : _defaultCollation.TraceString(),
                            _defaultCodePage, 
                            _defaultLCID,
                            TraceObjectClass(_defaultEncoding),
                            "",
                            _encryptionOption, 
                            null == _currentTransaction ? "(null)" : _currentTransaction.TraceString(),
                            null == _pendingTransaction ? "(null)" : _pendingTransaction.TraceString(), 
                            _retainedTransactionId, 
                            _nonTransactedOpenResultCount,
                            null == _connHandler ? "(null)" : _connHandler.ObjectID.ToString((IFormatProvider)null), 
                            _fAsync,
                            _fMARS,
                            _fAwaitingPreLogin,
                            _fPreLoginErrorOccurred, 
                            null == _sessionPool ? "(null)" : _sessionPool.TraceString(),
                            _isShiloh, 
                            _isShilohSP1, 
                            _isYukon,
                            null == _sniServerUserName ? "(null)" : _sniServerUserName.Length.ToString((IFormatProvider)null), 
                            null == _errors ? "(null)" : _errors.Count.ToString((IFormatProvider)null),
                            null == _warnings ? "(null)" : _warnings.Count.ToString((IFormatProvider)null),
                            null == _attentionErrors ? "(null)" : _attentionErrors.Count.ToString((IFormatProvider)null),
                            null == _attentionWarnings ? "(null)" : _attentionWarnings.Count.ToString((IFormatProvider)null), 
                            null == _statistics,
                            _statisticsIsInTransaction, 
                            _fPreserveTransaction); 
        }
 
        private string TraceObjectClass(object instance) {
            if (null == instance) {
                return "(null)";
            } 
            else {
                return instance.GetType().ToString(); 
            } 
        }
    }    // tdsparser 
}//namespace


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
// [....] 
//----------------------------------------------------------------------------- 

namespace System.Data.SqlClient { 
    using System;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Data.ProviderBase; 
    using System.Data.Sql;
    using System.Data.SqlTypes; 
    using System.Diagnostics; 
    using System.Globalization;
    using System.Runtime.CompilerServices; 
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    using System.Xml; 

    using MSS=Microsoft.SqlServer.Server; 
 
    // The TdsParser Object controls reading/writing to the netlib, parsing the tds,
    // and surfacing objects to the user. 
    sealed internal class TdsParser {
        private static int _objectTypeCount; // Bid counter
        internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 
        internal int ObjectID {
            get { 
                return _objectID; 
            }
        } 

#if DEBUG
        static public LocalDataStoreSlot ReliabilitySlot = Thread.AllocateDataSlot();       // contains whether we've setup for catching reliability work
#else 
        static public LocalDataStoreSlot ReliabilitySlot = null;                            // contains whether we've setup for catching reliability work
#endif //DEBUG 
 
        // Default state object for parser
        internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI. 

        // Also, default logical stateObj and connection for MARS over SNI.
        internal TdsParserStateObject _pMarsPhysicalConObj = null; // With MARS enabled, cached physical stateObj and connection.
 
        // Must keep this around - especially for callbacks on pre-MARS
        // ReadAsync which will return if physical connection broken! 
        // 
        // Per Instance TDS Parser variables
        // 

        // State variables
        internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
 
        private string _server = "";                            // name of server that the parser connects to
 
        internal volatile bool _fResetConnection = false;                 // flag to denote whether we are needing to call sp_reset 
        internal volatile bool _fPreserveTransaction = false;             // flag to denote whether we need to preserve the transaction when reseting
 
        private SqlCollation   _defaultCollation;                         // default collation from the server

        private int _defaultCodePage;
 
        private int _defaultLCID;
 
        internal Encoding _defaultEncoding = null;                  // for sql character data 

        private static EncryptionOptions    _sniSupportedEncryptionOption = SNILoadHandle.SingletonInstance.Options; 

        private EncryptionOptions           _encryptionOption             = _sniSupportedEncryptionOption;

        private SqlInternalTransaction      _currentTransaction; 
        private SqlInternalTransaction      _pendingTransaction;    // pending transaction for Yukon and beyond.
        // SQLHOT 483 
        //  need to hold on to the transaction id if distributed transaction merely rolls back without defecting. 
        private long                       _retainedTransactionId        = SqlInternalTransaction.NullTransactionId;
 
        // This counter is used for the entire connection to track the open result count for all
        // operations not under a transaction.
        private int _nonTransactedOpenResultCount = 0;
 
        // Connection reference
        private SqlInternalConnectionTds    _connHandler; 
 
        // Async/Mars variables
        private bool _fAsync = false; 

        private bool _fMARS = false;

        internal volatile bool _fAwaitingPreLogin = false; 

        internal volatile bool _fPreLoginErrorOccurred = false; 
 
        internal AutoResetEvent         _resetConnectionEvent = null;  // Used to serialize executes and call reset on first execute only.
 
        private TdsParserSessionPool _sessionPool = null;  // initialized only when we're a MARS parser.


        // Version variables 
        private bool _isShiloh = false; // set to true if we connect to a 8.0 server (SQL 2000) or later
 
        private bool _isShilohSP1 = false; // set to true if speaking to Shiloh SP1 or later 

        private bool _isYukon = false; // set to true if speaking to Yukon or later 

        private bool _isKatmai = false;

#if WINFSFunctionality 
        private bool _isWinFS = false; // set to true if speaking to WinFS or later
#endif 
 
        private byte[] _sniServerUserName = null;
 
        //

        // local exceptions to cache warnings and errors
        private SqlErrorCollection _errors; 

        private SqlErrorCollection _warnings; 
 
        // Local exceptions to cache warnings and errors for attention handling
        private SqlErrorCollection _attentionErrors; 

        private SqlErrorCollection _attentionWarnings;

        private object _ErrorCollectionLock = new object (); 

        // SqlStatistics 
        private SqlStatistics _statistics = null; 

        private bool _statisticsIsInTransaction = false; 

        //
        // STATIC TDS Parser variables
        // 

        private byte[] datetimeBuffer = new byte[10]; // cache for raw bytes of new Katmai Date/Time type 
 
        // NIC address caching
        private static byte[] s_nicAddress;             // cache the NIC address from the registry 

        // SSPI variables
        private static bool s_fSSPILoaded = false; // bool to indicate whether library has been loaded
 
        private volatile static UInt32 s_maxSSPILength = 0;     // variable to hold max SSPI data size, keep for token from server
 
        // textptr sequence 
        private static readonly byte[] s_longDataHeader = { 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
        private static object s_tdsParserLock = new object();

        // Various other statics
        private const int ATTENTION_TIMEOUT = 5000;  // internal attention timeout, in ticks 

        // XML metadata substitue sequence 
        private static readonly byte[] s_xmlMetadataSubstituteSequence = { 0xe7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }; 

#if NONETWORK 
// DO NOT REMOVE THIS CODE UNLESS YOU TALK WITH [....] FIRST
// WE USE THIS CODE TO CREATE HARD CODED PACKETS FOR PEFORMANCE
// TESTING!!!!
 
private int recv_packet_number=0;
 
// Put the output from the BUILDHARDCODEDPACKETCODE run here, adjust 
// the variable names, and add them to the array of packets below.
 
static private byte[][] recv_packet = { recv_packet_1, recv_packet_2 };

#endif //NONETWORK
 
#if OBJECT_BINDING
        // DataStream prototype 
        private ReadBehavior readBehavior = System.Data.SqlClient.ReadBehavior.AsRow; 
#endif
 
        internal TdsParser(bool MARS, bool fAsynchronous) {
            _fMARS = MARS; // may change during Connect to pre Yukon servers
            _fAsync = fAsynchronous;
            _physicalStateObj = new TdsParserStateObject(this); 
        }
 
        internal bool AsyncOn { 
            get {
                return _fAsync; 
            }
        }

        internal SqlInternalConnectionTds Connection { 
            get {
                return _connHandler; 
            } 
        }
 
        internal SqlInternalTransaction CurrentTransaction {
            get {
                return _currentTransaction;
            } 
            set {
                Debug.Assert(value == _currentTransaction 
                          || null == _currentTransaction 
                          || null == value
                          || (null != _currentTransaction && !_currentTransaction.IsLocal), "attempting to change current transaction?"); 

                // If there is currently a transaction active, we don't want to
                // change it; this can occur when there is a delegated transaction
                // and the user attempts to do an API begin transaction; in these 
                // cases, it's safe to ignore the set.
                if ((null == _currentTransaction && null != value) 
                  ||(null != _currentTransaction && null == value)) { 
                    _currentTransaction = value;
                } 
            }
        }

        internal int DefaultLCID { 
            get {
                return _defaultLCID; 
            } 
        }
 
        internal EncryptionOptions EncryptionOptions {
            get {
                return _encryptionOption;
            } 
            set {
                _encryptionOption = value; 
            } 
        }
 
        internal SqlErrorCollection Errors {
            get {
                lock (_ErrorCollectionLock) {
                    if (null == _errors) { 
                        _errors = new SqlErrorCollection();
                    } 
                    return _errors; 
                }
            } 
        }

        internal bool IsYukonOrNewer {
            get { 
                return _isYukon;
            } 
        } 

        internal bool IsKatmaiOrNewer { 
            get {
                return _isKatmai;
            }
        } 

#if WINFSFunctionality 
        internal bool IsWinFS { 
            get {
                return _isWinFS; 
            }
        }
#endif
 
        internal bool MARSOn {
            get { 
                return _fMARS; 
            }
        } 

        internal SqlInternalTransaction PendingTransaction {
            get {
                return _pendingTransaction; 
            }
            set { 
                Debug.Assert (null != value, "setting a non-null PendingTransaction?"); 
                _pendingTransaction = value;
            } 
        }

#if OBJECT_BINDING
        internal ReadBehavior ReadBehavior { 
            get {
                return readBehavior; 
            } 
            set {
                readBehavior = value; 
            }
        }
#endif
 
        internal string Server {
            get { 
                return _server; 
            }
        } 

        internal TdsParserState State {
            get {
                return _state; 
            }
            set { 
                _state = value; 
            }
        } 

        internal SqlStatistics Statistics {
            get {
                return _statistics; 
            }
            set { 
                _statistics = value; 
            }
        } 

        internal SqlErrorCollection Warnings {
            get {
                lock (_ErrorCollectionLock) { 
                    if (null == _warnings) {
                        _warnings = new SqlErrorCollection(); 
                    } 
                    return _warnings;
                } 
            }
        }

        internal int IncrementNonTransactedOpenResultCount() { 
            // IMPORTANT - this increments the connection wide open result count for all
            // operations not under a transaction!  Do not call if you intend to modify the 
            // count for a transaction! 
            Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
            int result = Interlocked.Increment(ref _nonTransactedOpenResultCount); 
            return result;
        }

        internal void DecrementNonTransactedOpenResultCount() { 
            // IMPORTANT - this decrements the connection wide open result count for all
            // operations not under a transaction!  Do not call if you intend to modify the 
            // count for a transaction! 
            Interlocked.Decrement(ref _nonTransactedOpenResultCount);
            Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state"); 
        }

        internal void ProcessPendingAck(TdsParserStateObject stateObj) {
            if (stateObj._attentionSent) { 
                ProcessAttention(stateObj);
            } 
        } 

        internal void Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, bool encrypt, bool trustServerCert, bool integratedSecurity, SqlConnection owningObject) { 
            if (_state != TdsParserState.Closed) {
                Debug.Assert(false, "TdsParser.Connect called on non-closed connection!");
                return;
            } 

            _connHandler = connHandler; 
 
            UInt32 sniStatus = SNILoadHandle.SingletonInstance.SNIStatus;
            if (sniStatus != TdsEnums.SNI_SUCCESS) { 
                Errors.Add(ProcessSNIError(_physicalStateObj));
                _physicalStateObj.Dispose();
                ThrowExceptionAndWarning(_physicalStateObj);
                Debug.Assert(false, "SNI returned status != success, but no error thrown?"); 
            }
 
            if (integratedSecurity) { 
                LoadSSPILibrary();
                // now allocate proper length of buffer 
                _sniServerUserName = new byte[s_maxSSPILength];
                Bid.Trace(" SSPI authentication\n");
            }
            else { 
                Bid.Trace(" SQL authentication\n");
            } 
 
            byte[] instanceName = null;
 
            _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire,
                        out instanceName, integratedSecurity, _sniServerUserName, false, _fAsync);

            if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { 
                Errors.Add(ProcessSNIError(_physicalStateObj));
 
                // Since connect failed, free the unmanaged connection memory. 
                // HOWEVER - only free this after the netlib error was processed - if you
                // don't, the memory for the connection object might not be accurate and thus 
                // a bad error could be returned (as it was when it was freed to early for me).
                _physicalStateObj.Dispose();
                Bid.Trace(" Login failure\n");
                ThrowExceptionAndWarning(_physicalStateObj); 
                Debug.Assert(false, "SNI returned status != success, but no error thrown?");
            } 
 
            _server = serverInfo.ResolvedServerName;
 
            if (null != connHandler.PoolGroupProviderInfo) {
                // If we are pooling, check to see if we were processing an
                // alias which has changed, which means we need to clean out
                // the pool. See Webdata 104293. 
                connHandler.PoolGroupProviderInfo.AliasCheck(serverInfo.ResolvedServerName);
            } 
            _state = TdsParserState.OpenNotLoggedIn; 
            _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfullWrite; // SQL BU DT 376766
            _physicalStateObj.TimeoutTime = timerExpire; 

            bool marsCapable = false;

            // 
            SendPreLoginHandshake(instanceName, encrypt);
 
            _physicalStateObj.SniContext = SniContext.Snix_PreLogin; 

            PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, out marsCapable); 

            // 1) Sphinx does not support pre-login handshakes, so the server will kill any
            // connection on which it receives a handshake on after open.
            // 2) Must verify instance name in pre-login handshake.  If we receive instance 
            // error (we did not connect to correct instance), we need to close the connection
            // and re-connect and in the process flush the netlib cache for this.  Stale 
            // cache should be the primary reason for this type of failure. 
            if (status == PreLoginHandshakeStatus.SphinxFailure) {
                _fMARS = false; // Turn off MARS, since we know it's not supported by 7.0! 

                // Release reference to Packet - so we don't incorrectly re-use with different connection.
                _physicalStateObj._sniPacket = null;
                _physicalStateObj.SniContext=SniContext.Snix_Connect; 
                _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, _sniServerUserName, false, _fAsync);
 
                if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { 
                    Errors.Add(ProcessSNIError(_physicalStateObj));
                    Bid.Trace(" Login failure\n"); 
                    ThrowExceptionAndWarning(_physicalStateObj);
                }
            }
            else if (status == PreLoginHandshakeStatus.InstanceFailure) { 
                _physicalStateObj.Dispose(); // Close previous connection
 
                // On Instance failure re-connect and flush SNI named instance cache. 
                _physicalStateObj.SniContext=SniContext.Snix_Connect;
                _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, _sniServerUserName, true, _fAsync); 

                if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) {
                    Errors.Add(ProcessSNIError(_physicalStateObj));
                    Bid.Trace(" Login failure\n"); 
                    ThrowExceptionAndWarning(_physicalStateObj);
                } 
 
                SendPreLoginHandshake(instanceName, encrypt);
                status = ConsumePreLoginHandshake(encrypt, trustServerCert, out marsCapable); 

                // Don't need to check for Sphinx failure, since we've already consumed
                // one pre-login packet and know we are connecting to Shiloh.
                if (status == PreLoginHandshakeStatus.InstanceFailure) { 
                    Bid.Trace(" Login failure\n");
                    throw SQL.InstanceFailure(); 
                } 
            }
 
            if (_fMARS && marsCapable) {
                // if user explictly disables mars or mars not supported, don't create the session pool
                _sessionPool = new TdsParserSessionPool(this);
            } 
            else {
                _fMARS = false; 
            } 
            return;
        } 

        internal void RemoveEncryption() {
            Debug.Assert(_encryptionOption == EncryptionOptions.LOGIN, "Invalid encryption option state");
 
            UInt32 error = 0;
 
            // Remove SSL (Encryption) SNI provider since we only wanted to encrypt login. 
            error = SNINativeMethodWrapper.SNIRemoveProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV);
            if (error != TdsEnums.SNI_SUCCESS) { 
                Errors.Add(ProcessSNIError(_physicalStateObj));
                ThrowExceptionAndWarning(_physicalStateObj);
            }
            // create a new packet encryption changes the internal packet size Bug# 228403 
            try {}   // EmptyTry/Finally to avoid FXCop violation
            finally { 
                _physicalStateObj._sniPacket.Dispose(); 
                _physicalStateObj._sniPacket = new SNIPacket(_physicalStateObj.Handle);
            } 
        }

        internal void EnableMars(string server) {
            if (_fMARS) { 
                // Cache physical stateObj and connection.
                _pMarsPhysicalConObj = _physicalStateObj; 
 
                UInt32 error = 0;
                UInt32 info = 0; 

                // Add SMUX (MARS) SNI provider.
                error = SNINativeMethodWrapper.SNIAddProvider(_pMarsPhysicalConObj.Handle, SNINativeMethodWrapper.ProviderEnum.SMUX_PROV, ref info);
 
                if (error != TdsEnums.SNI_SUCCESS) {
                    Errors.Add(ProcessSNIError(_physicalStateObj)); 
                    ThrowExceptionAndWarning(_physicalStateObj); 
                }
 
                // HACK HACK HACK - for Async only
                // Have to post read to intialize MARS - will get callback on this when connection goes
                // down or is closed.
 
                IntPtr temp = IntPtr.Zero;
 
                if (_fAsync) { 
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try {} finally { 
                        _pMarsPhysicalConObj.IncrementPendingCallbacks();

                        error = SNINativeMethodWrapper.SNIReadAsync(_pMarsPhysicalConObj.Handle, ref temp);
 
                        if (temp != IntPtr.Zero) {
                            // Be sure to release packet, otherwise it will be leaked by native. 
                            SNINativeMethodWrapper.SNIPacketRelease(temp); 
                        }
                    } 
                    Debug.Assert(IntPtr.Zero == temp, "unexpected syncReadPacket without corresponding SNIPacketRelease");
                    if (TdsEnums.SNI_SUCCESS_IO_PENDING != error) {
                        Debug.Assert(TdsEnums.SNI_SUCCESS != error, "Unexpected successfull read async on physical connection before enabling MARS!");
                        Errors.Add(ProcessSNIError(_physicalStateObj)); 
                        ThrowExceptionAndWarning(_physicalStateObj);
                    } 
                } 

                _physicalStateObj = CreateSession(); // Create and open default MARS stateObj and connection. 
            }
        }

        internal TdsParserStateObject CreateSession() { 
            TdsParserStateObject session = new TdsParserStateObject(this, (SNIHandle)_pMarsPhysicalConObj.Handle, _fAsync);
            if (Bid.AdvancedOn) { 
                Bid.Trace(" %d# created session %d\n", ObjectID, session.ObjectID); 
            }
            return session; 
        }

        internal TdsParserStateObject GetSession(object owner) {
            TdsParserStateObject session = null; 

            // 
 
            if (MARSOn) {
                session = _sessionPool.GetSession(owner); 

                Debug.Assert(!session._pendingData, "pending data on a pooled MARS session");
                if (Bid.AdvancedOn) {
                    Bid.Trace(" %d# getting session %d from pool\n", ObjectID, session.ObjectID); 
                }
            } 
            else { 
                session = _physicalStateObj;
                if (Bid.AdvancedOn) { 
                    Bid.Trace(" %d# getting physical session %d\n", ObjectID, session.ObjectID);
                }
            }
            Debug.Assert(session._outputPacketNumber==1, "The packet number is expected to be 1"); 
            return session;
        } 
 
        internal void PutSession(TdsParserStateObject session) {
            if (MARSOn) { 
                _sessionPool.PutSession(session);
            }
        }
 
        internal SNIHandle GetBestEffortCleanupTarget() {
            if (null != _physicalStateObj) { 
                return _physicalStateObj.Handle; 
            }
 
            return null;
        }

        private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) { 
            // PreLoginHandshake buffer consists of:
            // 1) Standard header, with type = MT_PRELOGIN 
            // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length) 
            // 3) Consecutive data blocks for each option
 
            // NOTE: packet data needs to be big endian - not the standard little endian used by
            // the rest of the parser.

            _physicalStateObj._outputMessageType = TdsEnums.MT_PRELOGIN; 

            // Initialize option offset into payload buffer 
            // 5 bytes for each option (1 byte length, 2 byte offset, 2 byte payload length) 
            int offset = (int)PreLoginOptions.NUMOPT * 5 + 1;
 
            byte[] payload = new byte[(int)PreLoginOptions.NUMOPT * 5 + TdsEnums.MAX_PRELOGIN_PAYLOAD_LENGTH];
            int payloadLength = 0;

            // 

 
            for (int option = (int)PreLoginOptions.VERSION; option < (int)PreLoginOptions.NUMOPT; option++) { 
                int optionDataSize = 0;
 
                // Fill in the option
                WriteByte((byte)option, _physicalStateObj);

                // Fill in the offset of the option data 
                WriteByte((byte)(offset & 0xff00), _physicalStateObj); // send upper order byte
                WriteByte((byte)(offset & 0x00ff), _physicalStateObj); // send lower order byte 
 
                switch (option) {
                    case (int)PreLoginOptions.VERSION: 
                        // For now, send 9.00.000 (Yukon RTM)
                        // Apparently, it doesn't matter what I send over. :)
                        // The server does not yet use it.
                        payload[payloadLength++] = 0x10; 
                        payload[payloadLength++] = 0x00;
                        payload[payloadLength++] = 0x00; 
                        payload[payloadLength++] = 0x00; 
                        payload[payloadLength++] = 0;
                        payload[payloadLength++] = 0; 
                        offset += 6;
                        optionDataSize = 6;
                        break;
 
                    case (int)PreLoginOptions.ENCRYPT:
                        if (_encryptionOption == EncryptionOptions.NOT_SUP) { 
                            // If OS doesn't support encryption, inform server not supported. 
                            payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
                        } 
                        else {
                            // Else, inform server of user request.
                            if (encrypt) {
                                payload[payloadLength] = (byte)EncryptionOptions.ON; 
                                _encryptionOption = EncryptionOptions.ON;
                            } 
                            else { 
                                payload[payloadLength] = (byte)EncryptionOptions.OFF;
                                _encryptionOption = EncryptionOptions.OFF; 
                            }
                        }

                        payloadLength += 1; 
                        offset += 1;
                        optionDataSize = 1; 
                        break; 

                    case (int)PreLoginOptions.INSTANCE: 
                        int i = 0;

                        while (instanceName[i] != 0) {
                            payload[payloadLength] = instanceName[i]; 
                            payloadLength++;
                            i++; 
                        } 

                        payload[payloadLength] = 0; // null terminate 
                        payloadLength++;
                        i++;

                        offset += i; 
                        optionDataSize = i;
                        break; 
 
                    case (int)PreLoginOptions.THREADID:
                        Int32 threadID = ADP.GetCurrentThreadId(); 

                        payload[payloadLength++] = (byte)(0xff000000 & threadID);
                        payload[payloadLength++] = (byte)(0x00ff0000 & threadID);
                        payload[payloadLength++] = (byte)(0x0000ff00 & threadID); 
                        payload[payloadLength++] = (byte)(0x000000ff & threadID);
                        offset += 4; 
                        optionDataSize = 4; 
                        break;
 
                    case (int)PreLoginOptions.MARS:
                        payload[payloadLength++] = (byte)(_fMARS ? 1 : 0);
                        offset += 1;
                        optionDataSize += 1; 
                        break;
 
                    default: 
                        Debug.Assert(false, "UNKNOWN option in SendPreLoginHandshake");
                        break; 
                }

                // Write data length
                WriteByte((byte)(optionDataSize & 0xff00), _physicalStateObj); 
                WriteByte((byte)(optionDataSize & 0x00ff), _physicalStateObj);
            } 
 
            // Write out last option - to let server know the second part of packet completed
            WriteByte((byte)PreLoginOptions.LASTOPT, _physicalStateObj); 

            // Write out payload
            WriteByteArray(payload, payloadLength, 0, _physicalStateObj);
 
            // Flush packet
            _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); 
        } 

        private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trustServerCert, out bool marsCapable) { 
            // int PRELOGIN_TIMEOUT  = 10;
            marsCapable = _fMARS; // Assign default value

            bool isYukonOrLater = false; 

            // Set bool for callback async read thread not to throw, but to set error flag 
            _fAwaitingPreLogin = true; 

            // 

            _physicalStateObj.ReadNetworkPacket();

            _fAwaitingPreLogin = false; 

            if (_physicalStateObj._inBytesRead == 0 || _fPreLoginErrorOccurred) { 
                if (encrypt) { 
                    // If encryption was required by client, but not connecting to
                    // 8.0 clients and beyond, fail to connect.  This is what netlibs do. 
                    // Pre-8.0 servers do not support encryption.
                    Errors.Add(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
                    _physicalStateObj.Dispose();
                    ThrowExceptionAndWarning(_physicalStateObj); 
                }
 
                // Sending pre-login to a 7.0 server will cause the server to kill 
                // the connection!!! Error and reconnect without sending prelogin packet.
                return PreLoginHandshakeStatus.SphinxFailure; 
            }

            // SEC
            byte[] payload = new byte[_physicalStateObj._inBytesRead - _physicalStateObj._inBytesUsed - _physicalStateObj._inputHeaderLen]; 

            _physicalStateObj.ReadByteArray(payload, 0, payload.Length); 
 
            if (payload[0] == 0xaa) {
                // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which 
                // is not supported.  SQL BU DT 296425
                throw SQL.InvalidSQLServerVersionUnknown();
            }
 
            int offset = 0;
            int payloadOffset = 0; 
            int payloadLength = 0; 
            int option = payload[offset++];
 
            while (option != (byte)PreLoginOptions.LASTOPT) {
                switch (option) {
                    case (int)PreLoginOptions.VERSION:
                        payloadOffset = payload[offset++] << 8 | payload[offset++]; 
                        payloadLength = payload[offset++] << 8 | payload[offset++];
 
                        byte majorVersion = payload[payloadOffset]; 
                        byte minorVersion = payload[payloadOffset + 1];
                        int level = (payload[payloadOffset + 2] << 8) | 
                                             payload[payloadOffset + 3];

                        isYukonOrLater = majorVersion >= 9;
                        if (!isYukonOrLater) { 
                            marsCapable = false;            // If pre-Yukon, MARS not supported.
                        } 
 
                        break;
 
                    case (int)PreLoginOptions.ENCRYPT:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++];
 
                        EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset];
 
                        /* internal enum EncryptionOptions { 
                            OFF,
                            ON, 
                            NOT_SUP,
                            REQ,
                            LOGIN
                        } */ 

                        switch (_encryptionOption) { 
                            case (EncryptionOptions.ON): 
                                if (serverOption == EncryptionOptions.NOT_SUP) {
                                    Errors.Add(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); 
                                    _physicalStateObj.Dispose();
                                    ThrowExceptionAndWarning(_physicalStateObj);
                                }
 
                                break;
 
                            case (EncryptionOptions.OFF): 
                                if (serverOption == EncryptionOptions.OFF) {
                                    // Only encrypt login. 
                                    _encryptionOption = EncryptionOptions.LOGIN;
                                }
                                else if (serverOption == EncryptionOptions.REQ) {
                                    // Encrypt all. 
                                    _encryptionOption = EncryptionOptions.ON;
                                } 
 
                                break;
 
                            case (EncryptionOptions.NOT_SUP):
                                if (serverOption == EncryptionOptions.REQ) {
                                    Errors.Add(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
                                    _physicalStateObj.Dispose(); 
                                    ThrowExceptionAndWarning(_physicalStateObj);
                                } 
 
                                break;
 
                            default:
                                Debug.Assert(false, "Invalid client encryption option detected");
                                break;
                        } 

                        if (_encryptionOption == EncryptionOptions.ON || 
                            _encryptionOption == EncryptionOptions.LOGIN) { 
                            UInt32 error = 0;
                            UInt32 info = ((encrypt && !trustServerCert) ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) 
                                | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);

                            // Add SSL (Encryption) SNI provider.
                            error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref info); 

                            if (error != TdsEnums.SNI_SUCCESS) { 
                                Errors.Add(ProcessSNIError(_physicalStateObj)); 
                                ThrowExceptionAndWarning(_physicalStateObj);
                            } 
        			// create a new packet encryption changes the internal packet size Bug# 228403
                        	try {} // EmptyTry/Finally to avoid FXCop violation
                            finally {
                                  _physicalStateObj._sniPacket.Dispose(); 
                                  _physicalStateObj._sniPacket = new SNIPacket(_physicalStateObj.Handle);
                             } 
                        } 

                        break; 

                    case (int)PreLoginOptions.INSTANCE:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++]; 

                        byte ERROR_INST = 0x1; 
                        byte instanceResult = payload[payloadOffset]; 

                        if (instanceResult == ERROR_INST) { 
                            // Check if server says ERROR_INST. That either means the cached info
                            // we used to connect is not valid or we connected to a named instance
                            // listening on default params.
                            return PreLoginHandshakeStatus.InstanceFailure; 
                        }
 
                        break; 

                    case (int)PreLoginOptions.THREADID: 
                        // DO NOTHING FOR THREADID
                        offset += 4;
                        break;
 
                    case (int)PreLoginOptions.MARS:
                        payloadOffset = payload[offset++] << 8 | payload[offset++]; 
                        payloadLength = payload[offset++] << 8 | payload[offset++]; 

                        marsCapable = (payload[payloadOffset] == 0 ? false : true); 

                        Debug.Assert(payload[payloadOffset] == 0 || payload[payloadOffset] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
                        break;
 
                    default:
                        Debug.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option); 
 
                        // DO NOTHING FOR THESE UNKNOWN OPTIONS
                        offset += 4; 

                        break;
                }
 
                if (offset < payload.Length) {
                    option = payload[offset++]; 
                } 
                else {
                    break; 
                }
            }

            return PreLoginHandshakeStatus.Successful; 
        }
 
        internal void Deactivate(bool connectionIsDoomed) { 
            // Called when the connection that owns us is deactivated.
 
            if (Bid.AdvancedOn) {
                Bid.Trace(" %d# deactivating\n", ObjectID);
            }
 
            if (Bid.StateDumpOn) {
                Bid.Trace(" %d#, %s\n", ObjectID, TraceString()); 
            } 

            if (MARSOn) { 
                _sessionPool.Deactivate();
            }

            Debug.Assert(connectionIsDoomed || null == _pendingTransaction, "pending transaction at disconnect?"); 

            if (!connectionIsDoomed && null != _physicalStateObj) { 
                if (_physicalStateObj._pendingData) { 
                    _physicalStateObj.CleanWire();
                } 

                if (_physicalStateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
                    _physicalStateObj.DecrementOpenResultCount();
                } 
            }
 
            // Any active, non-distributed transaction must be rolled back.  We 
            // need to wait for distributed transactions to be completed by the
            // transaction manager -- we don't want to automatically roll them 
            // back.
            //
            // Note that when there is a transaction delegated to this connection,
            // we will defer the deactivation of this connection until the 
            // transaction manager completes the transaction.
            SqlInternalTransaction currentTransaction = CurrentTransaction; 
 
            if (null != currentTransaction && currentTransaction.HasParentTransaction) {
                currentTransaction.CloseFromConnection(); 
                Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
            }

            Statistics = null; // must come after CleanWire or we won't count the stuff that happens there... 
        }
 
        // Used to close the connection and then free the memory allocated for the netlib connection. 
        internal void Disconnect() {
            if (null != _sessionPool) { 
                // MARSOn may be true, but _sessionPool not yet created
                _sessionPool.Dispose();
            }
 
            // Can close the connection if its open or broken
            if (_state != TdsParserState.Closed) { 
                //benign assert - the user could close the connection before consuming all the data 
                //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
 
                _state = TdsParserState.Closed;
                _physicalStateObj.SniContext = SniContext.Snix_Close;

                if (_fMARS) { 
                    try {
                        _physicalStateObj.Dispose(); 
 
                        if (null != _pMarsPhysicalConObj) {
                            // Not allocated until MARS is actually enabled in SNI. 
                            _pMarsPhysicalConObj.Dispose();
                        }
                    }
                    finally { 
                        _pMarsPhysicalConObj = null;
                    } 
                } 
                else {
                    _physicalStateObj.Dispose(); 
                }
#if DEBUG
                _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
#endif 
            }
        } 
 
        // Fires a single InfoMessageEvent
        private void FireInfoMessageEvent(TdsParserStateObject stateObj, SqlError error) { 

            string serverVersion = null;

            if (_state == TdsParserState.OpenLoggedIn) { 
                serverVersion = _connHandler.ServerVersion;
            } 
 
            SqlErrorCollection sqlErs = new SqlErrorCollection();
 
            sqlErs.Add(error);

            SqlException exc = SqlException.CreateException(sqlErs, serverVersion);
 
            _connHandler.Connection.OnInfoMessage(new SqlInfoMessageEventArgs(exc));
            return; 
        } 

        internal void DisconnectTransaction(SqlInternalTransaction internalTransaction) { 
            Debug.Assert(_currentTransaction != null && _currentTransaction == internalTransaction, "disconnecting different transaction");

            if (_currentTransaction != null && _currentTransaction == internalTransaction) {
                _currentTransaction = null; 
            }
        } 
 
        internal void RollbackOrphanedAPITransactions() {
            // Any active, non-distributed transaction must be rolled back. 
            SqlInternalTransaction currentTransaction = CurrentTransaction;

            if (null != currentTransaction && currentTransaction.HasParentTransaction && currentTransaction.IsOrphaned) {
                currentTransaction.CloseFromConnection(); 
                Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
            } 
        } 

 
        internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj) {
            // Lock the error collection
            // so the error collections are kept in a known state.
            // the error collections are modified here and in ProcessAttention. 
            // which can come in at any time
            lock (_ErrorCollectionLock) { 
                // This function should only be called when there was an error or warning.  If there aren't any 
                // errors, the handler will be called for the warning(s).  If there was an error, the warning(s) will
                // be copied to the end of the error collection so that the user may see all the errors and also the 
                // warnings that occurred.
                // can be deleted)
                Debug.Assert(_warnings != null || _errors != null, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
                Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!"); 
                SqlErrorCollection temp = null;  // temp variable to store that which is being thrown - so that local copies
 
                bool breakConnection = AddSqlErrorToCollection (ref temp, ref _errors); 
                breakConnection |= AddSqlErrorToCollection (ref temp,ref  _attentionErrors);
                breakConnection |= AddSqlErrorToCollection (ref temp, ref _warnings); 
                breakConnection |= AddSqlErrorToCollection (ref temp,ref _attentionWarnings);
                if (breakConnection) {
                     _state = TdsParserState.Broken;
                } 

                Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection"); 
                if (temp != null && temp.Count > 0) { 
                    // Construct the exception now that we've collected all the errors
                    string serverVersion = null; 
                    if (_state == TdsParserState.OpenLoggedIn) {
                        serverVersion = _connHandler.ServerVersion;
                    }
                    SqlException exception = SqlException.CreateException(temp, serverVersion); 
                    // the following handler will throw an exception or generate a warning event
                    _connHandler.OnError(exception, breakConnection); 
                } 
            }
        } 

 	private bool AddSqlErrorToCollection (ref SqlErrorCollection temp,ref SqlErrorCollection InputCollection) {
            if (null == InputCollection) {
                return false; 
            }
            bool broken = false; 
            if (null == temp) { 
                temp = new SqlErrorCollection ();
            } 
            for (int x = 0; x < InputCollection.Count; ++x) {
                SqlError error = InputCollection[x];
                temp.Add(error);
                if (error.Class >= TdsEnums.FATAL_ERROR_CLASS) { 
                    broken = true;                   			
                } 
            } 
            InputCollection = null;
            return (broken && TdsParserState.Closed != _state); 
        }

        internal static void ClearPoolCallback(object state) {
            Debug.Assert(null != state, "Unexpected null arg!"); 

            if (null != state) { 
                DbConnectionPoolGroup poolGroup = (DbConnectionPoolGroup) state; 
                poolGroup.Clear();
            } 
        }

        internal SqlError ProcessSNIError(TdsParserStateObject stateObj) {
#if DEBUG 
            Debug.Assert(SniContext.Undefined!=stateObj.DebugOnlyCopyOfSniContext, "SniContext must not be None");
#endif 
            SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error(); 
            SNINativeMethodWrapper.SNIGetLastError(sniError);
 
            if (_state == TdsParserState.OpenLoggedIn) {
                // Automatic pool cleaning logic upon server failure -
                // If a fatal netlib error occurs and the parser is in the OpenLoggedIn state (not
                // at login or pre-login time) then a fatal failure occurred that we were not informed 
                // of by the server.  As a result, clear out the PoolGroup that corresponds to this
                // parser and internal connection (same connection string).  This will refresh the pool 
                // and prevent other connections from failing in the same manner and forcing the user 
                // to go through all bad connections in the pool.
                if (null != _connHandler.Pool) { // If pooled, then just do it! 
                    Bid.Trace(" %d# clearing pool(ProcessSNIError)\n", ObjectID);
                    if (_fAsync) {
                        // SQL BU DT 399871 - do not call clear pool on same thread!
                        ThreadPool.QueueUserWorkItem(new WaitCallback(ClearPoolCallback), _connHandler.Pool.PoolGroup); 
                    }
                    else { 
                        _connHandler.Pool.PoolGroup.Clear(); 
                    }
                } 
            }
            // error.errorMessage is null terminated with garbage beyond that, since fixed length
            string errorMessage;
            int MessageLength = Array.IndexOf(sniError.errorMessage, '\0'); 
            if (MessageLength == -1) {
                errorMessage = String.Empty;  // If we don't see the expected null return nothing. 
            } else { 
                errorMessage = new String(sniError.errorMessage, 0, MessageLength);
            } 

            //  Format SNI errors and add Context Information
            //
            //  General syntax is: 
            //  
            //  (provider:, error:  - ) 
            // 
            // errorMessage | sniError |
            // ------------------------------------------- 
            // ==null       | x        | must never happen
            // !=null       | != 0     | retrieve corresponding errorMessage from resources
            // !=null       | == 0     | replace text left of errorMessage
            // 

            Debug.Assert(!ADP.IsEmpty(errorMessage),"Empty error message received from SNI"); 
 
            string sqlContextInfo = Res.GetString(Enum.GetName(typeof(SniContext), stateObj.SniContext));
 
            string providerRid = String.Format((IFormatProvider)null,"SNI_PN{0}", (int)sniError.provider);
            string providerName = Res.GetString(providerRid);
            Debug.Assert(!ADP.IsEmpty(providerName), String.Format((IFormatProvider)null,"invalid providerResourceId '{0}'", providerRid));
 
            if (sniError.sniError == 0) {
                // Provider error. The message from provider is preceeded with non-localizable info from SNI 
                // strip provider info from SNI 
                //
                int iColon = errorMessage.IndexOf(':'); 
                Debug.Assert(0<=iColon, "':' character missing in sni errorMessage");
                Debug.Assert(errorMessage.Length>iColon+1 && errorMessage[iColon+1]==' ', "Expecting a space after the ':' character");

                // extract the message excluding the colon and trailing cr/lf chars 
                if (0<=iColon) {
                    int len = errorMessage.Length; 
                    len -=2;    // exclude "\r\n" sequence 
                    iColon+=2;  // skip over ": " sequence
                    len-=iColon; 
                    /*
                        The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
                        Fix Bug 370686, if the message is recieved on a Win9x OS, the error message will not contain MESSAGE TEXT
                        per Bug: 269574.  If we get a errormessage with no message text, just return the entire message otherwise 
                        return just the message text.
                    */ 
                    if (len > 0) { 
                        errorMessage = errorMessage.Substring(iColon, len);
                    } 
                }
            }
            else {
                // SNI error. Replace the entire message 
                //
                string errorMessageId = String.Format((IFormatProvider)null,"SNI_ERROR_{0}",(int)sniError.sniError); 
                errorMessage = Res.GetString(errorMessageId); 
            }
            errorMessage = String.Format((IFormatProvider)null, "{0} (provider: {1}, error: {2} - {3})", 
                sqlContextInfo, providerName, (int)sniError.sniError, errorMessage);

            return new SqlError((int)sniError.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
                                _server, errorMessage, sniError.function, (int)sniError.lineNumber); 
        }
 
        internal void CheckResetConnection(TdsParserStateObject stateObj) { 
            if (_fResetConnection && !stateObj._fResetConnectionSent) {
                Debug.Assert(stateObj._outputPacketNumber == 1 || stateObj._outputPacketNumber == 2, "In ResetConnection logic unexpectedly!"); 
                try {
                    if (_fAsync && _fMARS && !stateObj._fResetEventOwned) {
                        // If using Async & MARS and we do not own ResetEvent - grab it.  We need to not grab lock here
                        // for case where multiple packets are sent to server from one execute. 
                        stateObj._fResetEventOwned = _resetConnectionEvent.WaitOne(TdsParserStaticMethods.GetTimeoutMilliseconds(stateObj.TimeoutTime), false);
 
                        if (stateObj._fResetEventOwned) { 
                            if (stateObj.TimeoutHasExpired) {
                                // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining. 
                                stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
                                stateObj.TimeoutTime = 0;
                            }
                        } 

                        if (!stateObj._fResetEventOwned) { 
                            // We timed out waiting for ResetEvent.  Throw timeout exception and reset 
                            // the buffer.  Nothing else to do since we did not actually send anything
                            // to the server. 
                            stateObj.ResetBuffer();
                            Errors.Add(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.Timeout(), "", 0));
                            ThrowExceptionAndWarning(stateObj);
                        } 
                    }
 
                    if (_fResetConnection) { 
                        // Check again to see if we need to send reset.
 
                        Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state for sending reset connection");
                        Debug.Assert(_isShiloh, "TdsParser.cs: Error!  _fResetConnection true when not going against Shiloh or later!");

                        if (_fPreserveTransaction) { 
                            // if we are reseting, set bit in header by or'ing with other value
                            stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION); 
                        } 
                        else {
                            // if we are reseting, set bit in header by or'ing with other value 
                            stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION);
                        }

                        if (!(_fAsync && _fMARS)) { 
                            _fResetConnection = false; // If not MARS, can turn off flag now.
                            _fPreserveTransaction = false; 
                        } 
                        else {
                            stateObj._fResetConnectionSent = true; // Otherwise set flag so we don't resend on multiple packet execute. 
                        }
                    }
                    else if (_fAsync && _fMARS && stateObj._fResetEventOwned) {
                        Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection"); 

                        // Otherwise if Yukon and we grabbed the event, free it.  Another execute grabbed the event and 
                        // took care of sending the reset. 
                        stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
                        Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); 
                    }
                }
                catch (Exception) {
                    if (_fAsync && _fMARS && stateObj._fResetEventOwned) { 
                        // If exception thrown, and we are on Yukon and own the event, release it!
                        stateObj._fResetConnectionSent = false; 
                        stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); 
                        Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!");
                    } 

                    throw;
                }
            } 
#if DEBUG
            else { 
                Debug.Assert(!_fResetConnection || 
                             (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned),
                             "Unexpected state on else ResetConnection block in WritePacket"); 
            }
#endif
        }
 

        // Takes in a single byte and writes it to the buffer.  If the buffer is full, it is flushed 
        // and then the buffer is re-initialized in flush() and then the byte is put in the buffer. 
        internal void WriteByte(byte b, TdsParserStateObject stateObj) {
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to WriteByte");  // you need to setup for a thread abort somewhere before you call this method 

            Debug.Assert(stateObj._outBytesUsed <= stateObj._outBuff.Length, "ERROR - TDSParser: _outBytesUsed > _outBuff.Length");

            // check to make sure we haven't used the full amount of space available in the buffer, if so, flush it 
            if (stateObj._outBytesUsed == stateObj._outBuff.Length)
                stateObj.WritePacket(TdsEnums.SOFTFLUSH); 
 
            // set byte in buffer and increment the counter for number of bytes used in the out buffer
            stateObj._outBuff[stateObj._outBytesUsed++] = b; 
        }

        //
        // Takes a byte array and writes it to the buffer. 
        //
        internal void WriteByteArray(Byte[] b, int len, int offsetBuffer, TdsParserStateObject stateObj) { 
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to WriteByteArray");  // you need to setup for a thread abort somewhere before you call this method 

            // Do we have to send out in packet size chunks, or can we rely on netlib layer to break it up? 
            // would prefer to to do something like:
            //
            // if (len > what we have room for || len > out buf)
            //   flush buffer 
            //   UnsafeNativeMethods.Write(b)
            // 
 
            int offset = offsetBuffer;
 
            Debug.Assert(b.Length >= len, "Invalid length sent to WriteByteArray()!");

            // loop through and write the entire array
            while (len > 0) { 
                if ((stateObj._outBytesUsed + len) > stateObj._outBuff.Length) {
                    // If the remainder of the string won't fit into the buffer, then we have to put 
                    // whatever we can into the buffer, and flush that so we can then put more into 
                    // the buffer on the next loop of the while.
 
                    int remainder = stateObj._outBuff.Length - stateObj._outBytesUsed;

                    // write the remainder
                    Buffer.BlockCopy(b, offset, stateObj._outBuff, stateObj._outBytesUsed, remainder); 

                    // handle counters 
                    offset += remainder; 
                    stateObj._outBytesUsed += remainder;
 
                    // Flush the buffer if full.
                    if (stateObj._outBytesUsed == stateObj._outBuff.Length) {
                        stateObj.WritePacket(TdsEnums.SOFTFLUSH);
                    } 

                    len -= remainder; 
                } 
                else { //((stateObj._outBytesUsed + len) <= stateObj._outBuff.Length )
                    // Else the remainder of the string will fit into the buffer, so copy it into the 
                    // buffer and then break out of the loop.

                    Buffer.BlockCopy(b, offset, stateObj._outBuff, stateObj._outBytesUsed, len);
 
                    // handle out buffer bytes used counter
                    stateObj._outBytesUsed += len; 
                    break; 
                }
            } 
        }

        //
        // Takes a 16 bit short and writes it. 
        //
        internal void WriteShort(int v, TdsParserStateObject stateObj) { 
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to WriteShort");  // you need to setup for a thread abort somewhere before you call this method 

            if ((stateObj._outBytesUsed + 2) > stateObj._outBuff.Length) { 
                // if all of the short doesn't fit into the buffer

                WriteByte((byte)(v & 0xff), stateObj);
                WriteByte((byte)((v >> 8) & 0xff), stateObj); 
            }
            else { 
                // all of the short fits into the buffer 

                stateObj._outBuff[stateObj._outBytesUsed++] = (byte)(v & 0xFF); 
                stateObj._outBuff[stateObj._outBytesUsed++] = (byte)((v >> 8) & 0xFF);
            }
        }
 
        internal void WriteUnsignedShort(ushort us, TdsParserStateObject stateObj) {
            WriteShort((short)us, stateObj); 
        } 

        // 
        // Takes a long and writes out an unsigned int
        //
        internal void WriteUnsignedInt(uint i, TdsParserStateObject stateObj) {
            WriteByteArray(BitConverter.GetBytes(i), 4, 0, stateObj); 
        }
 
        // 
        // Takes an int and writes it as an int.
        // 
        internal void WriteInt(int v, TdsParserStateObject stateObj) {
            WriteByteArray(BitConverter.GetBytes(v), 4, 0, stateObj);
        }
 
        //
        // Takes a float and writes it as a 32 bit float. 
        // 
        internal void WriteFloat(float v, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(v); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        //
        // Takes a long and writes it as a long. 
        // 
        internal void WriteLong(long v, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(v); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        //
        // Takes a ulong and writes it as a ulong. 
        // 
        internal void WriteUnsignedLong(ulong uv, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(uv); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        //
        // Takes a double and writes it as a 64 bit double. 
        // 
        internal void WriteDouble(double v, TdsParserStateObject stateObj) {
            byte[] bytes = BitConverter.GetBytes(v); 

            WriteByteArray(bytes, bytes.Length, 0, stateObj);
        }
 
        // Reads bytes from the buffer but doesn't return them, in effect simply deleting them.
        // Does not handle plp fields, need to use SkipPlpBytesValue for those. 
        internal void SkipLongBytes(ulong num, TdsParserStateObject stateObj) { 
            int cbSkip = 0;
 
            while (num > 0) {
                cbSkip = (num > Int32.MaxValue ? Int32.MaxValue : (int)num);
                stateObj.ReadByteArray(null, 0, cbSkip);
                num -= (ulong)cbSkip; 
            }
        } 
 
        // Reads bytes from the buffer but doesn't return them, in effect simply deleting them.
        public void SkipBytes(int num, TdsParserStateObject stateObj) { 
            stateObj.ReadByteArray(null, 0, num);
        }

        internal void PrepareResetConnection(bool preserveTransaction) { 
            // Set flag to reset connection upon next use - only for use on shiloh!
            _fResetConnection = true; 
            _fPreserveTransaction = preserveTransaction; 
        }
 
        // Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces
        internal bool Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) {
            Debug.Assert(null != Thread.GetData(ReliabilitySlot), "unreliable call to Run");  // you need to setup for a thread abort somewhere before you call this method
            Debug.Assert(   SniContext.Undefined != stateObj.SniContext 
                        &&  (SniContext.Snix_Execute != stateObj.SniContext || stateObj._attentionSent)
                        &&  SniContext.Snix_SendRows != stateObj.SniContext, 
                        String.Format((IFormatProvider)null, "Unexpected SniContext on call to GetValueInternal; SniContext={0}", stateObj.SniContext)); 

            if (TdsParserState.Broken == State || TdsParserState.Closed == State ) { 
                return true; // Just in case this is called in a loop, expecting data to be returned.
            }
            bool dataReady = false;
 
            do {
                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.

                byte token = stateObj.ReadByte(); 
                if (false == (
                             token == TdsEnums.SQLERROR || 
                             token == TdsEnums.SQLINFO || 
                             token == TdsEnums.SQLLOGINACK ||
                             token == TdsEnums.SQLENVCHANGE || 
                             token == TdsEnums.SQLRETURNVALUE ||
                             token == TdsEnums.SQLRETURNSTATUS ||
                             token == TdsEnums.SQLCOLNAME ||
                             token == TdsEnums.SQLCOLFMT || 
                             token == TdsEnums.SQLCOLMETADATA ||
                             token == TdsEnums.SQLALTMETADATA || 
                             token == TdsEnums.SQLTABNAME || 
                             token == TdsEnums.SQLCOLINFO ||
                             token == TdsEnums.SQLORDER || 
                             token == TdsEnums.SQLALTROW ||
                             token == TdsEnums.SQLROW ||
                             token == TdsEnums.SQLDONE ||
                             token == TdsEnums.SQLDONEPROC || 
                             token == TdsEnums.SQLDONEINPROC ||
                             token == TdsEnums.SQLROWCRC || 
                             token == TdsEnums.SQLSECLEVEL || 
                             token == TdsEnums.SQLCONTROL ||
                             token == TdsEnums.SQLPROCID || 
                             token == TdsEnums.SQLOFFSET ||
                             token == TdsEnums.SQLSSPI)) {
                    Debug.Assert(false, String.Format((IFormatProvider)null, "unexpected token; token = {0,-2:X2}", token));
                    _state = TdsParserState.Broken; 
                    _connHandler.BreakConnection();
                    throw SQL.ParsingError(); // MDAC 82443 
                } 

                int tokenLength = GetTokenLength(token, stateObj); 

                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 = ProcessError(token, stateObj);

                    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.
                        if ((_connHandler != null) && (_connHandler.Connection != null) && 
                            (_connHandler.Connection.FireInfoMessageEventOnUserErrors == true) && 
                            (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS)) {
                            // Fire SqlInfoMessage here 
                            FireInfoMessageEvent(stateObj, error);
                        }
                        else {
                            // insert error/info into the appropriate exception - warning if info, exception if error 
                            if (error.Class < TdsEnums.MIN_ERROR_CLASS) {
                                Warnings.Add(error); 
                            } 
                            else if (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS) {
                                Errors.Add(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 and that was a bug preventing the user from 
                                // processing subsequent results.
 
                                if (null != dataStream) { // Webdata 104560
                                    if (!dataStream.IsInitialized) {
                                        runBehavior = RunBehavior.UntilDone;
                                    } 
                                }
                            } 
                            else { 
                                Errors.Add(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) {
                        Errors.Add(error); 
                    }
                    break;
                }
 
                case TdsEnums.SQLCOLINFO:
                { 
                    if (null != dataStream) { 
                        _SqlMetaDataSet metaDataSet = ProcessColInfo(dataStream.MetaData, dataStream, stateObj);
                        dataStream.SetMetaData (metaDataSet, false); 
                        dataStream.BrowseModeInfoConsumed = true;
                    }
                    else { // no dataStream
                        SkipBytes(tokenLength, stateObj); 
                    }
                    break; 
                } 

                case TdsEnums.SQLDONE: 
                case TdsEnums.SQLDONEPROC:
                case TdsEnums.SQLDONEINPROC:
                {
                    // RunBehavior can be modified - see SQL BU DT 269516 & 290090 
                    ProcessDone(cmdHandler, dataStream, ref runBehavior, stateObj);
                    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 
                    SkipBytes(tokenLength, stateObj); 
                    break;
                } 

                case TdsEnums.SQLALTMETADATA:
                {
                    if (stateObj._cleanupAltMetaDataSetArray == null) {  // create object on demand (lazy creation) 
                        stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection();
                    } 
 
                    _SqlMetaDataSet cleanupAltMetaDataSet = ProcessAltMetaData(tokenLength, stateObj);
 
                    stateObj._cleanupAltMetaDataSetArray.Add(cleanupAltMetaDataSet);
                    if (null != dataStream) {
                        dataStream.SetAltMetaDataSet(cleanupAltMetaDataSet, (TdsEnums.SQLALTMETADATA != stateObj.PeekByte()));
                    } 

                    break; 
                } 

                case TdsEnums.SQLALTROW: 
                {
                    // read will call run until dataReady. Must not read any data if returnimmetiately set
                    if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) {
                        int altRowId = stateObj.ReadUInt16();             // get altRowId 

                        SkipRow(stateObj._cleanupAltMetaDataSetArray[altRowId], stateObj);  // skip altRow 
                    } 

                    dataReady = true; 
                    break;
                }

                case TdsEnums.SQLENVCHANGE: 
                {
                    SqlEnvChange[] env = ProcessEnvChange(tokenLength, stateObj); 
 
                    for (int ii = 0; ii < env.Length; ii++) {
                        if (env[ii] != null) { 
                            switch (env[ii].type) {
                            case TdsEnums.ENV_BEGINTRAN:
                            case TdsEnums.ENV_ENLISTDTC:
                                // 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 = (TdsEnums.ENV_BEGINTRAN == env[ii].type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed;
                                    _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue); 
                                }
                                if (null != _statistics && !_statisticsIsInTransaction) {
                                    _statistics.SafeIncrement(ref _statistics._transactions);
                                } 
                                _statisticsIsInTransaction = true;
                                _retainedTransactionId = SqlInternalTransaction.NullTransactionId; 
                                break; 
                            case TdsEnums.ENV_DEFECTDTC:
                            case TdsEnums.ENV_TRANSACTIONENDED: 
                            case TdsEnums.ENV_COMMITTRAN:
                                // SQLHOT 483
                                //  Must clear the retain id if the server-side transaction ends by anything other
                                //  than rollback. 
                                _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
                                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. 
                                        if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) { 
                                            _retainedTransactionId = env[ii].oldLongValue;
                                        } 
                                        _currentTransaction.Completed(TransactionState.Aborted);
                                    }
                                    else {
                                        // 
                                        _currentTransaction.Completed(TransactionState.Unknown);
                                    } 
                                    _currentTransaction = null; 
                                }
                                _statisticsIsInTransaction = false; 
                                break;
                            default:
                                _connHandler.OnEnvChange(env[ii]);
                                break; 
                            }
                        } 
                    } 
                    break;
                } 
                case TdsEnums.SQLLOGINACK:
                {
                    SqlLoginAck ack = ProcessLoginAck(stateObj);
 
                    _connHandler.OnLoginAck(ack);
                    break; 
                } 
                case TdsEnums.SQLCOLMETADATA:
                { 
                    if (tokenLength != TdsEnums.VARNULL) {
                        stateObj._cleanupMetaData = ProcessMetaData(tokenLength, stateObj);
                    }
                    else { 
                        if (cmdHandler != null) {
                            stateObj._cleanupMetaData = cmdHandler.MetaData; 
                        } 
                    }
 
                    if (null != dataStream) {
                        byte peekedToken = stateObj.PeekByte(); // temporarily cache next byte

                        dataStream.SetMetaData(stateObj._cleanupMetaData, (TdsEnums.SQLTABNAME == peekedToken || TdsEnums.SQLCOLINFO == peekedToken)); 
                    }
                    else if (null != bulkCopyHandler) { 
                        bulkCopyHandler.SetMetaData(stateObj._cleanupMetaData); 
                    }
                    break; 
                }
                case TdsEnums.SQLROW:
                {
                    if (null != bulkCopyHandler) { 
                        //
                        ProcessRow(stateObj._cleanupMetaData, bulkCopyHandler.CreateRowBuffer(), bulkCopyHandler.CreateIndexMap(), stateObj); 
                    } 
                    else if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior))
                        SkipRow(stateObj._cleanupMetaData, stateObj); // skip rows 

                    if (_statistics != null) {
                        _statistics.WaitForDoneAfterRow = true;
                    } 
                    dataReady = true;
                    break; 
                } 
                case TdsEnums.SQLRETURNSTATUS:
                    int status = stateObj.ReadInt32(); 
                    if (cmdHandler != null) {
                        cmdHandler.OnReturnStatus(status);
                    }
                    break; 
                case TdsEnums.SQLRETURNVALUE:
                { 
                    SqlReturnValue returnValue = ProcessReturnValue(tokenLength, stateObj); 
                    if (cmdHandler != null) {
                        cmdHandler.OnReturnValue(returnValue); 
                    }
                    break;
                }
                case TdsEnums.SQLSSPI: 
                {
                    // token length is length of SSPI data - call ProcessSSPI with it 
                    ProcessSSPI(tokenLength); 
                    break;
                } 
                case TdsEnums.SQLTABNAME:
                {
                    if (null != dataStream) {
                            dataStream.TableNames = ProcessTableName(tokenLength, stateObj); 
                    }
                    else { 
                        SkipBytes(tokenLength, stateObj); 
                    }
                    break; 
                }

                default:
                    Debug.Assert(false, "Unhandled token:  " + token.ToString(CultureInfo.InvariantCulture)); 
                    break;
            } 
            } 
                // 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 (!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._attentionSent && stateObj._attentionReceived) {
                // 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!
                    Errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0));
                } 
            }
 
            if (_errors != null || _warnings != null) { 
                ThrowExceptionAndWarning(stateObj);
            } 
            return dataReady;
        }

        private SqlEnvChange[] ProcessEnvChange(int tokenLength, TdsParserStateObject stateObj) { 
            // There could be multiple environment change messages following this token.
            int processedLength = 0; 
            int nvalues = 0; 
            SqlEnvChange[] envarray = new SqlEnvChange[3];  // Why is this hardcoded to 3?
 
            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(); 

                env.type = stateObj.ReadByte(); 
                envarray[nvalues] = env; 
                nvalues++;
 
                switch (env.type) {
                    case TdsEnums.ENV_DATABASE:
                    case TdsEnums.ENV_LANG:
                        ReadTwoStringFields(env, stateObj); 
                        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!");
                        ReadTwoStringFields(env, stateObj);
                        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
                        ReadTwoStringFields(env, stateObj);
                        // 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._sniPacket.Dispose();

                            // 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"); 

                            _physicalStateObj._sniPacket = new SNIPacket(_physicalStateObj.Handle); 
                        }

                        break;
 
                    case TdsEnums.ENV_LOCALEID:
                        // 
 
                        ReadTwoStringFields(env, stateObj);
                        _defaultLCID = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); 
                        break;

                    case TdsEnums.ENV_COMPFLAGS:
                        ReadTwoStringFields(env, stateObj); 
                        break;
 
                    case TdsEnums.ENV_COLLATION: 
                        Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!");
                        env.newLength = stateObj.ReadByte(); 
                        if (env.newLength == 5) {
                            env.newCollation = ProcessCollation(stateObj);

                            // 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;
                        }
 
                        env.oldLength = stateObj.ReadByte();
                        Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!"); 
                        if (env.oldLength == 5) 
                            env.oldCollation = ProcessCollation(stateObj);
 
                        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!");

                        env.newLength = stateObj.ReadByte();
                        Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!"); 

                        if (env.newLength > 0) { 
                            env.newLongValue = stateObj.ReadInt64(); 
                            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.
                        }
 
                        env.oldLength = stateObj.ReadByte();
                        Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!"); 
 
                        if (env.oldLength > 0) {
                            env.oldLongValue = stateObj.ReadInt64(); 
                            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 
                        ReadTwoStringFields(env, stateObj);
                        break; 
 
                    case TdsEnums.ENV_PROMOTETRANSACTION:
                        Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!"); 

                        env.newLength = stateObj.ReadInt32();                          // new value has 4 byte length
                        env.newBinValue = new byte[env.newLength];
                        stateObj.ReadByteArray(env.newBinValue, 0, env.newLength); // read new value with 4 byte length 

                        env.oldLength = stateObj.ReadByte(); 
                        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!"); 
                        ReadTwoBinaryFields(env, stateObj);
                        break; 

                    case TdsEnums.ENV_USERINSTANCE:
                        Debug.Assert(!_isYukon, "Received ENV_USERINSTANCE on non 9.0 server!");
                        ReadTwoStringFields(env, stateObj); 
                        break;
 
                    default: 
                        Debug.Assert(false, "Unknown environment change token: " + env.type);
                        break; 
                }
                processedLength += env.length;
            }
 
            return envarray;
        } 
 
        private void ReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject stateObj) {
            // Used by ProcessEnvChangeToken 
            env.newLength = stateObj.ReadByte();
            env.newBinValue = new byte[env.newLength];
            stateObj.ReadByteArray(env.newBinValue, 0, env.newLength);
            env.oldLength = stateObj.ReadByte(); 
            env.oldBinValue = new byte[env.oldLength];
            stateObj.ReadByteArray(env.oldBinValue, 0, env.oldLength); 
 
            // env.length includes 1 byte type token
            env.length = 3 + env.newLength + env.oldLength; 
        }

        private void ReadTwoStringFields(SqlEnvChange env, TdsParserStateObject stateObj) {
            // Used by ProcessEnvChangeToken 
            env.newLength = stateObj.ReadByte();
            env.newValue = stateObj.ReadString(env.newLength); 
            env.oldLength = stateObj.ReadByte(); 
            env.oldValue = stateObj.ReadString(env.oldLength);
 
            // env.length includes 1 byte type token
            env.length = 3 + env.newLength * 2 + env.oldLength * 2;
        }
 
        private void ProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavior run, TdsParserStateObject stateObj) {
            ushort curCmd; 
            ushort status; 
            int count;
 
            // status
            // command
            // rowcount (valid only if DONE_COUNT bit is set)
 
            status = (ushort)stateObj.ReadUInt16();
            curCmd = (ushort)stateObj.ReadUInt16(); 
            if (_isYukon) { 
                count = (int) stateObj.ReadInt64();
            } 
            else {
                count = stateObj.ReadInt32();
                // 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 = stateObj.PeekByte(); 
                        if (b == 0) {
                            // This is an invalid token value
                            count = stateObj.ReadInt32();
                        } 
                    }
                } 
            } 

            // 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)) && _errors == null && 
                  stateObj._errorTokenReceived == false && (RunBehavior.Clean != (RunBehavior.Clean & run))) { 
                Errors.Add(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))) {
                Errors.Add(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();
            } 
        } 

        private void ProcessSqlStatistics(ushort curCmd, ushort status, int count) { 
            // SqlStatistics bookkeeping stuff
            //
            if (null != _statistics) {
                // any done after row(s) counts as a resultset 
                if (_statistics.WaitForDoneAfterRow) {
                    _statistics.SafeIncrement(ref _statistics._sumResultSets); 
                    _statistics.WaitForDoneAfterRow = false; 
                }
 
                // clear row count DONE_COUNT flag is not set
                if (!(TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) {
                    count = 0;
                } 

                switch (curCmd) { 
                    case TdsEnums.INSERT: 
                    case TdsEnums.DELETE:
                    case TdsEnums.UPDATE: 
                    case TdsEnums.MERGE:
                        _statistics.SafeIncrement(ref _statistics._iduCount);
                        _statistics.SafeAdd(ref _statistics._iduRows, count);
                        if (!_statisticsIsInTransaction) { 
                            _statistics.SafeIncrement(ref _statistics._transactions);
                        } 
 
                        break;
 
                    case TdsEnums.SELECT:
                        _statistics.SafeIncrement(ref _statistics._selectCount);
                        _statistics.SafeAdd(ref _statistics._selectRows, count);
                        break; 

                    case TdsEnums.BEGINXACT: 
                        if (!_statisticsIsInTransaction) { 
                            _statistics.SafeIncrement(ref _statistics._transactions);
                        } 
                        _statisticsIsInTransaction = true;
                        break;

                    case TdsEnums.OPENCURSOR: 
                        _statistics.SafeIncrement(ref _statistics._cursorOpens);
                        break; 
 
                    case TdsEnums.ABORT:
                        _statisticsIsInTransaction = false; 
                        break;

                    case TdsEnums.ENDXACT:
                        _statisticsIsInTransaction = false; 
                        break;
                } // switch 
            } 
            else {
                switch (curCmd) { 
                    case TdsEnums.BEGINXACT:
                        _statisticsIsInTransaction = true;
                        break;
 
                    case TdsEnums.ABORT:
                    case TdsEnums.ENDXACT: 
                        _statisticsIsInTransaction = false; 
                        break;
                } 
            }
        }

        private SqlLoginAck ProcessLoginAck(TdsParserStateObject stateObj) { 
            SqlLoginAck a = new SqlLoginAck();
 
            // read past interface type and version 
            SkipBytes(1, stateObj);
 
            byte[] b = new byte[TdsEnums.VERSION_SIZE];
            stateObj.ReadByteArray(b, 0, b.Length);
            UInt32 tdsVersion = (UInt32)((((((b[0]<<8)|b[1])<<8)|b[2])<<8)|b[3]); // bytes are in motorola order (high byte first)
            UInt32 majorMinor = tdsVersion & 0xff00ffff; 
            UInt32 increment  = (tdsVersion >> 16) & 0xff;
 
            // Server responds: 
            // 0x07000000 -> Sphinx         // Notice server response format is different for bwd compat
            // 0x07010000 -> Shiloh RTM     // Notice server response format is different for bwd compat 
            // 0x71000001 -> Shiloh SP1
            // 0x72xx0002 -> Yukon RTM
            // 0x73xx0003 -> WinFS RTM
            // information provided by S. Ashwin 

            switch (majorMinor) { 
                case TdsEnums.SPHINXORSHILOH_MAJOR<<24|TdsEnums.DEFAULT_MINOR:    // Sphinx & Shiloh RTM 
                    // note that sphinx and shiloh_rtm can only be distinguished by the increment
                    switch (increment) { 
                        case TdsEnums.SHILOH_INCREMENT:
                            _isShiloh = true;
                            break;
                        case TdsEnums.SPHINX_INCREMENT: 
                            // no flag will be set
                            break; 
                        default: 
                            throw SQL.InvalidTDSVersion();
                    } 
                    break;
                case TdsEnums.SHILOHSP1_MAJOR<<24|TdsEnums.SHILOHSP1_MINOR: // Shiloh SP1
                    if (increment != TdsEnums.SHILOHSP1_INCREMENT) { throw SQL.InvalidTDSVersion(); }
                    _isShilohSP1 = true; 
                    break;
                case TdsEnums.YUKON_MAJOR<<24|TdsEnums.YUKON_RTM_MINOR:     // Yukon 
                    if (increment != TdsEnums.YUKON_INCREMENT) { throw SQL.InvalidTDSVersion(); } 
                    _isYukon = true;
                    break; 
                case TdsEnums.KATMAI_MAJOR<<24|TdsEnums.KATMAI_MINOR:
                    if (increment != TdsEnums.KATMAI_INCREMENT) { throw SQL.InvalidTDSVersion(); }
                    _isKatmai = true;
                    break; 
#if WINFSFunctionality
                case TdsEnums.WINFS_MAJOR<<24|TdsEnums.WINFS_RTM_MINOR:     // WinFS 
                    if (increment != TdsEnums.WINFS_INCREMENT) { throw SQL.InvalidTDSVersion(); } 
                    _isWinFS = true;
                    break; 
#endif
                default:
                    throw SQL.InvalidTDSVersion();
            } 

#if WINFSFunctionality 
            _isYukon        |= _isWinFS;            // the highest supported version 
#endif
            _isYukon        |= _isKatmai; 
            _isShilohSP1    |= _isYukon;            // includes all lower versions
            _isShiloh       |= _isShilohSP1;        //

            a.isVersion8 = _isShiloh; 

            stateObj._outBytesUsed = stateObj._outputHeaderLen; 
            byte len = stateObj.ReadByte(); 

            a.programName = stateObj.ReadString(len); 
            a.majorVersion = stateObj.ReadByte();
            a.minorVersion = stateObj.ReadByte();
            a.buildNum = (short)((stateObj.ReadByte() << 8) + stateObj.ReadByte());
 
            Debug.Assert(_state == TdsParserState.OpenNotLoggedIn, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn");
            _state = TdsParserState.OpenLoggedIn; 
 
            if (_isYukon) {
                if (_fAsync && _fMARS) { 
                    _resetConnectionEvent = new AutoResetEvent(true);
                }
            }
 
            // Fail if SSE UserInstance and we have not received this info.
            if ( _connHandler.ConnectionOptions.UserInstance && 
                ADP.IsEmpty(_connHandler.InstanceName)) { 
                Errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, Server, SQLMessage.UserInstanceFailure(), "", 0));
                ThrowExceptionAndWarning(_physicalStateObj); 
            }

            return a;
        } 

        internal SqlError ProcessError(byte token, TdsParserStateObject stateObj) { 
            int len; 
            int number = stateObj.ReadInt32();
            byte state = stateObj.ReadByte(); 
            byte errorClass = stateObj.ReadByte();

            Debug.Assert(((errorClass >= TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLERROR) ||
                          ((errorClass < TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLINFO), "class and token don't match!"); 

            len = stateObj.ReadUInt16(); 
 
            string message = stateObj.ReadString(len);
 
            len = (int)stateObj.ReadByte();

            string server;
 
            // MDAC bug #49307 - server sometimes does not send over server field! In those cases
            // we will use our locally cached value. 
            if (len == 0) 
                server = _server;
            else 
                server = stateObj.ReadString(len);

            len = (int)stateObj.ReadByte();
 
            string procedure = stateObj.ReadString(len);
 
             int line; 
             if (_isYukon) {
                line = stateObj.ReadInt32(); 
            }
            else {
                line = stateObj.ReadUInt16();
                // 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 = stateObj.PeekByte();
                    if (b == 0) { 
                        // This is an invalid token value
                        line = (line << 16) + stateObj.ReadUInt16();
                    }
                } 
            }
 
            return new SqlError(number, state, errorClass, _server, message, procedure, line); 
        }
 

        internal SqlReturnValue ProcessReturnValue(int length, TdsParserStateObject stateObj) {
            SqlReturnValue rec = new SqlReturnValue();
            rec.length = length;        // In Yukon this length is -1 
            if (_isYukon) {
                rec.parmIndex = stateObj.ReadUInt16(); 
            } 
            byte len = stateObj.ReadByte();      // Length of parameter name
 
            if (len > 0)
                rec.parameter = stateObj.ReadString(len);

            // read status and ignore 
            stateObj.ReadByte();
 
            UInt32 userType; 

            // read user type - 4 bytes Yukon, 2 backwards 
            if (IsYukonOrNewer) {
                userType = stateObj.ReadUInt32();
            }
            else { 
                userType = stateObj.ReadUInt16();
            } 
 
            // read off the flags
            stateObj.ReadUInt16(); 

            // read the type
            byte tdsType = stateObj.ReadByte();
 
            // 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 {
                tdsLen = GetTokenLength(tdsType, stateObj); 
            }

            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) {
                rec.precision = stateObj.ReadByte(); 
                rec.scale = stateObj.ReadByte(); 
            }
 
            if (rec.metaType.IsVarTime) {
                rec.scale = stateObj.ReadByte();
            }
 
            if (tdsType == TdsEnums.SQLUDT) {
                ProcessUDTMetaData((SqlMetaDataPriv) rec, stateObj); 
            } 

            if (rec.type == SqlDbType.Xml) { 
                // Read schema info
                byte schemapresent = stateObj.ReadByte();

                if ((schemapresent & 1) != 0) { 
                    len = stateObj.ReadByte();
                    if (len != 0) 
                        rec.xmlSchemaCollectionDatabase = stateObj.ReadString(len); 

                    len = stateObj.ReadByte(); 
                    if (len != 0)
                        rec.xmlSchemaCollectionOwningSchema = stateObj.ReadString(len);

                    short slen = stateObj.ReadInt16(); 
                    if (slen != 0)
                        rec.xmlSchemaCollectionName = stateObj.ReadString(slen); 
 
                }
            } 
            else if (_isShiloh && rec.metaType.IsCharType) {
                // read the collation for 8.x servers
                rec.collation = ProcessCollation(stateObj);
 
                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 = ProcessColumnHeader(rec, stateObj, out isNull);

            // 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 { 
                ReadSqlValue(rec.value, rec, intlen, stateObj);
            } 

            return rec;
        }
 
        internal SqlCollation ProcessCollation(TdsParserStateObject stateObj) {
            SqlCollation collation = new SqlCollation(); 
 
            collation.info = stateObj.ReadUInt32();
            collation.sortId = stateObj.ReadByte(); 
            return collation;
        }

        internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) { 
            int codePage = 0;
 
            if (0 != collation.sortId) { 
                codePage = TdsEnums.CODE_PAGE_FROM_SORT_ID[collation.sortId];
                Debug.Assert(0 != codePage, "GetCodePage accessed codepage array and produced 0!, sortID =" + ((Byte)(collation.sortId)).ToString((IFormatProvider)null)); 
            }
            else {
                int cultureId = collation.LCID;
                bool success = false; 

                try { 
                    codePage = CultureInfo.GetCultureInfo(cultureId).TextInfo.ANSICodePage; 

                    // SqlHot 50001398: CodePage can be zero, but we should defer such errors until 
                    //  we actually MUST use the code page (i.e. don't error if no ANSI data is sent).
                    success = true;
                }
                catch (ArgumentException e) { 
                    ADP.TraceExceptionWithoutRethrow(e);
                } 
 
                // If we failed, it is quite possible this is because certain culture id's
                // were removed in Win2k and beyond, however Sql Server still supports them. 
                // There is a workaround for the culture id's listed below, which is to mask
                // off the sort id (the leading 1). If that fails, or we have a culture id
                // other than the special cases below, we throw an error and throw away the
                // rest of the results. For additional info, see MDAC 65963. 

                // SqlHot 50001398: Sometimes GetCultureInfo will return CodePage 0 instead of throwing. 
                //  treat this as an error also, and switch into the special-case logic. 
                if (!success || codePage == 0) {
                    CultureInfo ci = null; 
                    switch (cultureId) {
                        case 0x10404: // Chinese - ----
                        case 0x10804: // Chinese - People's Republic of China
                        case 0x10c04: // Chinese - Hong Kong SAR 
                        case 0x11004: // Chinese - Singapore
                        case 0x11404: // Chinese - Macau SAR 
                        case 0x10411: // Japanese - Japan 
                        case 0x10412: // Korean - Korea
                            // If one of the following special cases, mask out sortId and 
                            // retry.
                            cultureId = cultureId & 0x03fff;

                            try { 
                                ci = new CultureInfo(cultureId);
                                success = true; 
                            } 
                            catch (ArgumentException e) {
                                ADP.TraceExceptionWithoutRethrow(e); 
                            }
                            break;
                        case 0x827:     // Non-supported Lithuanian code page, map it to supported Lithuanian.
                            try { 
                                ci = new CultureInfo(0x427);
                                success = true; 
                            } 
                            catch (ArgumentException e) {
                                ADP.TraceExceptionWithoutRethrow(e); 
                            }
                            break;
                        default:
                            break; 
                    }
 
                    // I don't believe we should still be in failure case, but just in case. 
                    if (!success) {
                        ThrowUnsupportedCollationEncountered(stateObj); 
                    }

                    if (null != ci) {
                        codePage = ci.TextInfo.ANSICodePage; 
                    }
                } 
            } 

            return codePage; 
        }

        internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj) {
            Errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.CultureIdError(), "", 0)); 

            if (null != stateObj) { 
                stateObj.CleanWire(); 

                stateObj._pendingData = false; 
            }

            ThrowExceptionAndWarning(stateObj);
        } 

 
 
        internal _SqlMetaDataSet ProcessAltMetaData(int cColumns, TdsParserStateObject stateObj) {
            Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!"); 

            _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns);
            int[] indexMap = new int[cColumns];
 
            altMetaDataSet.id = stateObj.ReadUInt16();
 
            int byCols = stateObj.ReadByte(); 

            while (byCols > 0) { 
                SkipBytes(2, stateObj);   // ignore ColNum ...
                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];
 
                col.op = stateObj.ReadByte();
                col.operand = stateObj.ReadUInt16();

                CommonProcessMetaData(stateObj, col); 

                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;
 
            return altMetaDataSet;
        }

 
        internal _SqlMetaDataSet ProcessMetaData(int cColumns, TdsParserStateObject stateObj) {
            Debug.Assert(cColumns > 0, "should have at least 1 column in metadata!"); 
 
            _SqlMetaDataSet metaData = new _SqlMetaDataSet(cColumns);
            for (int i = 0; i < cColumns; i++) { 
                CommonProcessMetaData(stateObj, metaData[i]);
            }

            return metaData; 
        }
 
        private bool IsVarTimeTds(byte tdsType) { 
            return tdsType == TdsEnums.SQLTIME || tdsType == TdsEnums.SQLDATETIME2 || tdsType == TdsEnums.SQLDATETIMEOFFSET;
        } 

        private void CommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col) {
            int    len      = 0;
            UInt32 userType; 

            // read user type - 4 bytes Yukon, 2 backwards 
            if (IsYukonOrNewer) { 
                userType = stateObj.ReadUInt32();
            } 
            else {
                userType = stateObj.ReadUInt16();
            }
 
            // read flags and set appropriate flags in structure
            byte flags = stateObj.ReadByte(); 
 
            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
            stateObj.ReadByte(); 

            col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet)); 
 
            byte tdsType = stateObj.ReadByte();
 
            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 {
                col.length = GetTokenLength(tdsType, stateObj); 
            }

            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 WINFSFunctionality
                    if (_isWinFS) { 
                        col.metaType = MetaType.GetUdtMetaType(true); 
                    }
#endif 

                    ProcessUDTMetaData((SqlMetaDataPriv) col, stateObj);
                }
 
                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 = stateObj.ReadByte(); 

                        if ((schemapresent & 1) != 0) { 
                            len = stateObj.ReadByte();
                            if (len != 0)
                                col.xmlSchemaCollectionDatabase = stateObj.ReadString(len);
 
                            len = stateObj.ReadByte();
                            if (len != 0) 
                                col.xmlSchemaCollectionOwningSchema = stateObj.ReadString(len); 

                            len = stateObj.ReadInt16(); 
                            if (len != 0)
                                col.xmlSchemaCollectionName = stateObj.ReadString(len);
                        }
                    } 
                }
            } 
 
            if (col.type == SqlDbType.Decimal) {
                col.precision = stateObj.ReadByte(); 
                col.scale = stateObj.ReadByte();
            }

            if (col.metaType.IsVarTime) { 
                col.scale = stateObj.ReadByte();
 
                // 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)) {
                col.collation = ProcessCollation(stateObj); 

                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
                    col.multiPartTableName = ProcessOneTable(stateObj, ref unusedLen);
                } else { 
                    len = stateObj.ReadUInt16();
                    string tableName = stateObj.ReadString(len); 
                    // 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);
                }
            } 

            len = stateObj.ReadByte(); 
            col.column = stateObj.ReadString(len); 

            // 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;
        }
 
        private void ProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObject stateObj) {
            int length; 
 
            metaData.length = stateObj.ReadUInt16(); // max byte size
 
            // database name
            length = stateObj.ReadByte();
            if (length != 0) {
                metaData.udtDatabaseName = stateObj.ReadString(length); 
            }
 
            // schema name 
            length = stateObj.ReadByte();
            if (length != 0) { 
                metaData.udtSchemaName = stateObj.ReadString(length);
            }

            // type name 
            length = stateObj.ReadByte();
            if (length != 0) { 
                metaData.udtTypeName = stateObj.ReadString(length); 
            }
 
            length = stateObj.ReadUInt16();
            if (length != 0) {
                metaData.udtAssemblyQualifiedName = stateObj.ReadString(length);
            } 
        }
 
        private void WriteUDTMetaData(object value, string database, string schema, string type, 
                                      TdsParserStateObject stateObj) {
            // database 
            if (ADP.IsEmpty(database)) {
                WriteByte(0, stateObj);
            }
            else { 
                WriteByte((byte) database.Length, stateObj);
                WriteString(database, stateObj); 
            } 

            // schema 
            if (ADP.IsEmpty(schema)) {
                WriteByte(0, stateObj);
            }
            else { 
                WriteByte((byte) schema.Length, stateObj);
                WriteString(schema, stateObj); 
            } 

            // type 
            if (ADP.IsEmpty(type)) {
                WriteByte(0, stateObj);
            }
            else { 
                WriteByte((byte) type.Length, stateObj);
                WriteString(type, stateObj); 
            } 
        }
 
        internal MultiPartTableName[] ProcessTableName(int length, TdsParserStateObject stateObj) {
            int tablesAdded = 0;

            MultiPartTableName[] tables = new MultiPartTableName[1]; 
            MultiPartTableName mpt;
            while (length > 0) { 
                // 

 
                mpt = ProcessOneTable(stateObj, ref length);
                if (tablesAdded == 0) {
                    tables[tablesAdded] = mpt;
                } 
                else {
                    MultiPartTableName[] newTables = new MultiPartTableName[tables.Length + 1]; 
                    Array.Copy(tables, 0, newTables, 0, tables.Length); 
                    newTables[tables.Length] = mpt;
                    tables = newTables; 
                }

                tablesAdded++;
            } 
            return tables;
        } 
 
        private MultiPartTableName ProcessOneTable(TdsParserStateObject stateObj, ref int length ) {
            ushort tableLen; 
            MultiPartTableName mpt;

            if (_isShilohSP1) {
 
                    mpt = new MultiPartTableName();
                    byte nParts; 
 
                    // Find out how many parts in the TDS stream
                    nParts = stateObj.ReadByte(); 
                    length--;
                    if (nParts == 4) {
                        tableLen = stateObj.ReadUInt16();
                        length -= 2; 
                        mpt.ServerName = stateObj.ReadString(tableLen);
                        nParts--; 
                        length -= (tableLen * 2); // wide bytes 
                    }
                    if (nParts == 3) { 
                        tableLen = stateObj.ReadUInt16();
                        length -= 2;
                        mpt.CatalogName = stateObj.ReadString(tableLen);
                        length -= (tableLen * 2); // wide bytes 
                        nParts--;
                    } 
                    if (nParts == 2) { 
                        tableLen = stateObj.ReadUInt16();
                        length -= 2; 
                        mpt.SchemaName = stateObj.ReadString(tableLen);
                        length -= (tableLen * 2); // wide bytes
                        nParts--;
                    } 
                    if (nParts == 1) {
                        tableLen = stateObj.ReadUInt16(); 
                        length -= 2; 
                        mpt.TableName = stateObj.ReadString(tableLen);
                        length -= (tableLen * 2); // wide bytes 
                        nParts--;
                    }
                    Debug.Assert(nParts == 0 , "ProcessTableName:Unidentified parts in the table name token stream!");
 
            }
            else { 
                tableLen = stateObj.ReadUInt16(); 
                length -= 2;
                string tableName = stateObj.ReadString(tableLen); 
                length -= (tableLen * 2); // wide bytes
                mpt = new MultiPartTableName(MultipartIdentifier.ParseMultipartIdentifier(tableName, "[\"", "]\"", Res.SQL_TDSParserTableName, false));
            }
            return mpt; 
        }
 
        // augments current metadata with table and key information 
        private _SqlMetaDataSet ProcessColInfo(_SqlMetaDataSet columns, SqlDataReader reader, TdsParserStateObject stateObj) {
            Debug.Assert(columns != null && columns.Length > 0, "no metadata available!"); 

            for (int i = 0; i < columns.Length; i++) {
                _SqlMetaData col = columns[i];
 
                stateObj.ReadByte();    // colnum, ignore
                col.tableNum = stateObj.ReadByte(); 
 
                // interpret status
                byte status = stateObj.ReadByte(); 

                col.isDifferentName = (TdsEnums.SQLDifferentName == (status & TdsEnums.SQLDifferentName));
                col.isExpression = (TdsEnums.SQLExpression == (status & TdsEnums.SQLExpression));
                col.isKey = (TdsEnums.SQLKey == (status & TdsEnums.SQLKey)); 
                col.isHidden = (TdsEnums.SQLHidden == (status & TdsEnums.SQLHidden));
 
                // read off the base table name if it is different than the select list column name 
                if (col.isDifferentName) {
                    byte len = stateObj.ReadByte(); 

                    col.baseColumn = stateObj.ReadString(len);
                }
 
                // Fixup column name - only if result of a table - that is if it was not the result of
                // an expression. 
                if ((reader.TableNames != null) && (col.tableNum > 0)) { 
                    Debug.Assert(reader.TableNames.Length >= col.tableNum, "invalid tableNames array!");
                    col.multiPartTableName = reader.TableNames[col.tableNum - 1]; 
                }

                // MDAC 60109: expressions are readonly
                if (col.isExpression) { 
                    col.updatability = 0;
                } 
            } 

            // set the metadata so that the stream knows some metadata info has changed 
            return columns;
        }

        // takes care of any per data header information: 
        // for long columns, reads off textptrs, reads length, check nullability
        // for other columns, reads length, checks nullability 
        // returns length and nullability 
        internal ulong ProcessColumnHeader(SqlMetaDataPriv col, TdsParserStateObject stateObj, out bool isNull) {
            if (col.metaType.IsLong && !col.metaType.IsPlp) { 
                //
                // we don't care about TextPtrs, simply go after the data after it
                //
                byte textPtrLen = stateObj.ReadByte(); 

                if (0 != textPtrLen) { 
                    // read past text pointer 
                    SkipBytes(textPtrLen, stateObj);
 
                    // read past timestamp
                    SkipBytes(TdsEnums.TEXT_TIME_STAMP_LEN, stateObj);
                    isNull = false;
                    return GetDataLength(col, stateObj); 
                }
                else { 
                    isNull = true; 
                    return 0;
 
                }
            }
            else {
                // non-blob columns 
                ulong longlen = GetDataLength(col, stateObj);
                isNull = IsNull(col.metaType, longlen); 
                return (isNull ? 0 : longlen); 
            }
        } 

        // assumes that the current position is at the start of an altrow!
        internal int GetAltRowId(TdsParserStateObject stateObj) {
            byte token = stateObj.ReadByte();    // skip over ALTROW token 

            Debug.Assert((token == TdsEnums.SQLALTROW), ""); 
            return (int)stateObj.ReadUInt16(); 
        }
 
        // Used internally by BulkCopy only
        private void ProcessRow(_SqlMetaDataSet columns, object[] buffer, int[] map, TdsParserStateObject stateObj) {
            SqlBuffer data = new SqlBuffer();
 
            for (int i = 0; i < columns.Length; i++) {
                _SqlMetaData md = columns[i]; 
                Debug.Assert(md != null, "_SqlMetaData should not be null for column " + i.ToString(CultureInfo.InvariantCulture)); 

                bool isNull; 
                ulong len = ProcessColumnHeader(md, stateObj, out isNull);

                if (isNull) {
                    GetNullSqlValue(data, md); 
                    buffer[map[i]] = data.SqlValue;
                } 
                else { 
                    // We only read up to 2Gb. Throw if data is larger. Very large data
                    // should be read in chunks in sequential read mode 
                    // For Plp columns, we may have gotten only the length of the first chunk
                    ReadSqlValue(data, md, md.metaType.IsPlp ? (Int32.MaxValue) : (int)len, stateObj);
                    buffer[map[i]] = data.SqlValue;
                    if (stateObj._longlen != 0) { 
                        throw new SqlTruncateException(Res.GetString(Res.SqlMisc_TruncationMaxDataMessage));
                    } 
                } 
                data.Clear();
            } 
        }

        internal object GetNullSqlValue(SqlBuffer nullVal, SqlMetaDataPriv md) {
            switch (md.type) { 
                case SqlDbType.Real:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Single); 
                    break; 

                case SqlDbType.Float: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Double);
                    break;

                case SqlDbType.Udt: 
                case SqlDbType.Binary:
                case SqlDbType.VarBinary: 
                case SqlDbType.Image: 
                    nullVal.SqlBinary = SqlBinary.Null;
                    break; 

                case SqlDbType.UniqueIdentifier:
                    nullVal.SqlGuid = SqlGuid.Null;
                    break; 

                case SqlDbType.Bit: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Boolean); 
                    break;
 
                case SqlDbType.TinyInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Byte);
                    break;
 
                case SqlDbType.SmallInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int16); 
                    break; 

                case SqlDbType.Int: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int32);
                    break;

                case SqlDbType.BigInt: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int64);
                    break; 
 
                case SqlDbType.Char:
                case SqlDbType.VarChar: 
                case SqlDbType.NChar:
                case SqlDbType.NVarChar:
                case SqlDbType.Text:
                case SqlDbType.NText: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.String);
                    break; 
 
                case SqlDbType.Decimal:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Decimal); 
                    break;

                case SqlDbType.DateTime:
                case SqlDbType.SmallDateTime: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime);
                    break; 
 
                case SqlDbType.Money:
                case SqlDbType.SmallMoney: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Money);
                    break;

                case SqlDbType.Variant: 
                    // DBNull.Value will have to work here
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Empty); 
                    break; 

                case SqlDbType.Xml: 
                    nullVal.SqlCachedBuffer = SqlCachedBuffer.Null;
                    break;

                case SqlDbType.Date: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Date);
                    break; 
 
                case SqlDbType.Time:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Time); 
                    break;

                case SqlDbType.DateTime2:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime2); 
                    break;
 
                case SqlDbType.DateTimeOffset: 
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTimeOffset);
                    break; 

                default:
                    Debug.Assert(false, "unknown null sqlType!" + md.type.ToString());
                    break; 
            }
 
            return nullVal; 
        }
 
        internal void SkipRow(_SqlMetaDataSet columns, TdsParserStateObject stateObj) {
            SkipRow(columns, 0, stateObj);
        }
 
        internal void SkipRow(_SqlMetaDataSet columns, int startCol, TdsParserStateObject stateObj) {
            for (int i = startCol; i < columns.Length; i++) { 
                _SqlMetaData md = columns[i]; 

                if (md.metaType.IsLong && !md.metaType.IsPlp) { 
                    byte textPtrLen = stateObj.ReadByte();

                    if (0 != textPtrLen)
                        SkipBytes(textPtrLen + TdsEnums.TEXT_TIME_STAMP_LEN, stateObj); 
                    else
                        continue; 
                } 

                SkipValue(md, stateObj); 
            }
        }

        internal void SkipValue(SqlMetaDataPriv md, TdsParserStateObject stateObj) { 
            if (md.metaType.IsPlp) {
                SkipPlpValue(UInt64.MaxValue, stateObj); 
            } 
            else {
                int length = GetTokenLength(md.tdsType, stateObj); 

                // if false, no value to skip - it's null
                if (!IsNull(md.metaType, (ulong)length))
                    SkipBytes(length, stateObj); 
            }
 
            return; 
        }
 
        private bool IsNull(MetaType mt, ulong length) {
            // null bin and char types have a length of -1 to represent null
            if (mt.IsPlp) {
                return (TdsEnums.SQL_PLP_NULL == length); 
            }
 
            // HOTFIX #50000415: for image/text, 0xFFFF is the length, not representing null 
            if ((TdsEnums.VARNULL == length) && !mt.IsLong) {
                return true; 
            }

            // other types have a length of 0 to represent null
            return ((TdsEnums.FIXEDNULL == length) && !mt.IsCharType && !mt.IsBinType); 
        }
 
        private void ReadSqlStringValue(SqlBuffer value, byte type, int length, Encoding encoding, bool isPlp, TdsParserStateObject stateObj) { 
            switch (type) {
                case TdsEnums.SQLCHAR: 
                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLVARCHAR:
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT: 
                    // If bigvarchar(max), we only read the first chunk here,
                    // expecting the caller to read the rest 
                    if (encoding == null) { 
                        // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
                        // 7.0 has no support for multiple code pages in data - single code page support only 
                        encoding = _defaultEncoding;
                    }
                    value.SetToString(stateObj.ReadStringWithEncoding(length, encoding, isPlp));
                    break; 

                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR: 
                case TdsEnums.SQLNTEXT:
                { 
                        String s = null;

                        if (isPlp) {
                            char[] cc = null; 

                            length = ReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj); 
                            if (length > 0) { 
                                s = new String(cc, 0, length);
                            } 
                            else {
                                s = ADP.StrEmpty;
                            }
                        } 
                        else {
                            s = stateObj.ReadString(length >> 1); 
                        } 

                        value.SetToString(s); 
                        break;
                    }

                default: 
                    Debug.Assert(false, "Unknown tds type for SqlString!" + type.ToString(CultureInfo.InvariantCulture));
                    break; 
            } 
        }
 
        internal void ReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, TdsParserStateObject stateObj) {
            Debug.Assert(md.metaType.IsPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!");
            if (md.metaType.IsPlp) {
                // We must read the column value completely, no matter what length is passed in 
                length = Int32.MaxValue;
            } 
            switch (md.tdsType) { 
                case TdsEnums.SQLDECIMALN:
                case TdsEnums.SQLNUMERICN: 
                    ReadSqlDecimal(value, length, md.precision, md.scale, stateObj);
                    break;

                case TdsEnums.SQLUDT: 
                case TdsEnums.SQLBINARY:
                case TdsEnums.SQLBIGBINARY: 
                case TdsEnums.SQLBIGVARBINARY: 
                case TdsEnums.SQLVARBINARY:
                case TdsEnums.SQLIMAGE: 
                    byte[] b = null;

                    // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
                    if (md.metaType.IsPlp) { 
                        // If we are given -1 for length, then we read the entire value,
                        // otherwise only the requested amount, usually first chunk. 
                        stateObj.ReadPlpBytes(ref b, 0, length); 
                    }
                    else { 
                        //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
                        b = new byte[length];
                        stateObj.ReadByteArray(b, 0, length);
                    } 

                    value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array 
 
                    break;
 
                case TdsEnums.SQLCHAR:
                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLVARCHAR:
                case TdsEnums.SQLBIGVARCHAR: 
                case TdsEnums.SQLTEXT:
                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR: 
                case TdsEnums.SQLNTEXT:
                    ReadSqlStringValue(value, md.tdsType, length, md.encoding, md.metaType.IsPlp, stateObj); 
                    break;

                case TdsEnums.SQLXMLTYPE:
                    // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader. 
                    SqlCachedBuffer sqlBuf = new SqlCachedBuffer(md, this, stateObj);
 
                    value.SqlCachedBuffer = sqlBuf; 
                    break;
 
                case TdsEnums.SQLDATE:
                case TdsEnums.SQLTIME:
                case TdsEnums.SQLDATETIME2:
                case TdsEnums.SQLDATETIMEOFFSET: 
                    ReadSqlDateTime(value, md.tdsType, length, md.scale, stateObj);
                    break; 
 
                default:
                    Debug.Assert(!md.metaType.IsPlp, "ReadSqlValue calling ReadSqlValueInternal with plp data"); 
                    ReadSqlValueInternal(value, md.tdsType, md.metaType.TypeId, length, stateObj);
                    break;
            }
 
            Debug.Assert((stateObj._longlen == 0) && (stateObj._longlenleft == 0), "ReadSqlValue did not read plp field completely, longlen =" + stateObj._longlen.ToString((IFormatProvider)null) + ",longlenleft=" + stateObj._longlenleft.ToString((IFormatProvider)null));
        } 
 
        private void ReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj) {
            stateObj.ReadByteArray(datetimeBuffer, 0, length); 

            switch (tdsType) {
                case TdsEnums.SQLDATE:
                    Debug.Assert(length == 3, "invalid length for date type!"); 
                    value.SetToDate(datetimeBuffer);
                    break; 
 
                case TdsEnums.SQLTIME:
                    Debug.Assert(3 <= length && length <= 5, "invalid length for time type!"); 
                    value.SetToTime(datetimeBuffer, length, scale);
                    break;

                case TdsEnums.SQLDATETIME2: 
                    Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!");
                    value.SetToDateTime2(datetimeBuffer, length, scale); 
                    break; 

                case TdsEnums.SQLDATETIMEOFFSET: 
                    Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!");
                    value.SetToDateTimeOffset(datetimeBuffer, length, scale);
                    break;
 
                default:
                    Debug.Assert(false, "ReadSqlDateTime is called with the wrong tdsType"); 
                    break; 
            }
        } 

        internal void ReadSqlValueInternal(SqlBuffer value, byte tdsType, int typeId, int length, TdsParserStateObject stateObj) {
            switch (tdsType) {
                case TdsEnums.SQLBIT: 
                case TdsEnums.SQLBITN:
                    Debug.Assert(length == 1, "invalid length for SqlBoolean type!"); 
                    value.Boolean = (stateObj.ReadByte() != 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!"); 
                    value.Byte = stateObj.ReadByte();
                    break; 

                case TdsEnums.SQLINT2:
                    Debug.Assert(length == 2, "invalid length for SqlInt16 type!");
                    value.Int16 = stateObj.ReadInt16(); 
                    break;
 
                case TdsEnums.SQLINT4: 
                    Debug.Assert(length == 4, "invalid length for SqlInt32 type!");
                    value.Int32 = stateObj.ReadInt32(); 
                    break;

                case TdsEnums.SQLINT8:
                    Debug.Assert(length == 8, "invalid length for SqlInt64 type!"); 
                    value.Int64 = stateObj.ReadInt64();
                    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!");
                    value.Single = stateObj.ReadSingle(); 
                    break;

                case TdsEnums.SQLFLT8:
                    Debug.Assert(length == 8, "invalid length for SqlDouble type!"); 
                    value.Double = stateObj.ReadDouble();
                    break; 
 
                case TdsEnums.SQLMONEYN:
                    if (length == 4) { 
                        goto case TdsEnums.SQLMONEY4;
                    }
                    else {
                        goto case TdsEnums.SQLMONEY; 
                    }
 
                case TdsEnums.SQLMONEY: 
                {
                        int mid = stateObj.ReadInt32(); 
                        uint lo = stateObj.ReadUInt32();
                        long l = (((long)mid) << 0x20) + ((long)lo);

                        value.SetToMoney(l); 
                        break;
                    } 
 
                case TdsEnums.SQLMONEY4:
                    value.SetToMoney(stateObj.ReadInt32()); 
                    break;

                case TdsEnums.SQLDATETIMN:
                    if (length == 4) { 
                        goto case TdsEnums.SQLDATETIM4;
                    } 
                    else { 
                        goto case TdsEnums.SQLDATETIME;
                    } 

                case TdsEnums.SQLDATETIM4:
                    value.SetToDateTime(stateObj.ReadUInt16(), stateObj.ReadUInt16() * SqlDateTime.SQLTicksPerMinute);
                    break; 

                case TdsEnums.SQLDATETIME: 
                    value.SetToDateTime(stateObj.ReadInt32(), (int)stateObj.ReadUInt32()); 
                    break;
 
                case TdsEnums.SQLUNIQUEID:
                {
                    Debug.Assert(length == 16, "invalid length for SqlGuid type!");
 
                    byte[] b = new byte[length];
 
                    stateObj.ReadByteArray(b, 0, length); 
                    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];
                    stateObj.ReadByteArray(b, 0, length);
                    value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array 

                    break; 
                } 

                case TdsEnums.SQLVARIANT: 
                    ReadSqlVariant(value, length, stateObj);
                    break;

                default: 
                    Debug.Assert(false, "Unknown SqlType!" + tdsType.ToString(CultureInfo.InvariantCulture));
                    break; 
            } // switch 
        }
 
        //
        // Read in a SQLVariant
        //
        // SQLVariant looks like: 
        // struct
        // { 
        //      BYTE TypeTag 
        //      BYTE cbPropBytes
        //      BYTE[] Properties 
        //      BYTE[] DataVal
        // }
        internal void ReadSqlVariant(SqlBuffer value, int lenTotal, TdsParserStateObject stateObj) {
            Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variaint in pre-SQL2000 server!"); 
            // get the SQLVariant type
            byte type = stateObj.ReadByte(); 
            int lenMax = 0; // maximum lenData of value inside variant 

            // read cbPropBytes 
            byte cbPropsActual = stateObj.ReadByte();
            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:
                    ReadSqlValueInternal(value, type, 0, lenData, stateObj); 
                    break;

                case TdsEnums.SQLDECIMALN:
                case TdsEnums.SQLNUMERICN: 
                {
                        Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for decimal/numeric type!"); 
 
                        byte precision = stateObj.ReadByte();
                        byte scale = stateObj.ReadByte(); 

                        // skip over unknown properties
                        if (cbPropsActual > cbPropsExpected) {
                            SkipBytes(cbPropsActual - cbPropsExpected, stateObj); 
                        }
 
                        ReadSqlDecimal(value, TdsEnums.MAX_NUMERIC_LEN, precision, scale, stateObj); 
                        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!");
 
                    lenMax = stateObj.ReadUInt16(); 
                    Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarbinary(max) in a sqlvariant");
 
                    // skip over unknown properties
                    if (cbPropsActual > cbPropsExpected) {
                        SkipBytes(cbPropsActual - cbPropsExpected, stateObj);
                    } 

                    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!"); 

                        // 
 
                        ProcessCollation(stateObj);
 
                        lenMax = stateObj.ReadUInt16();
                        Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarchar(max) or nvarchar(max) in a sqlvariant");

                        // skip over unknown properties 
                        if (cbPropsActual > cbPropsExpected) {
                            SkipBytes(cbPropsActual - cbPropsExpected, stateObj); 
                        } 

                        ReadSqlStringValue(value, type, lenData, null, false, stateObj); 
                        break;
                    }
                case TdsEnums.SQLDATE:
                    ReadSqlDateTime(value, type, lenData, 0, stateObj); 
                    break;
 
                case TdsEnums.SQLTIME: 
                case TdsEnums.SQLDATETIME2:
                case TdsEnums.SQLDATETIMEOFFSET: 
                {
                    Debug.Assert(cbPropsExpected == 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!");

                    byte scale = stateObj.ReadByte(); 

                    // skip over unknown properties 
                    if (cbPropsActual > cbPropsExpected) { 
                        SkipBytes(cbPropsActual - cbPropsExpected, stateObj);
                    } 

                    ReadSqlDateTime(value, type, lenData, scale, stateObj);
                    break;
                } 

                default: 
                    Debug.Assert(false, "Unknown tds type in SqlVariant!" + type.ToString(CultureInfo.InvariantCulture)); 
                    break;
            } // switch 
        }

        //
        // Translates a com+ object -> SqlVariant 
        // when the type is ambiguous, we always convert to the bigger type
        // note that we also write out the maxlen and actuallen members (4 bytes each) 
        // in addition to the SQLVariant structure 
        //
        internal void WriteSqlVariantValue(object value, int length, int offset, TdsParserStateObject stateObj) { 
            Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");

            // handle null values
            if (ADP.IsNull(value)) { 
                WriteInt(TdsEnums.FIXEDNULL, stateObj); //maxlen
                WriteInt(TdsEnums.FIXEDNULL, stateObj); //actuallen 
                return; 
            }
 
            MetaType mt = MetaType.GetMetaTypeFromValue(value);

            if (mt.IsAnsiType) {
                length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding); 
            }
 
            // max and actual len are equal to 
            // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
            WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // maxLen 
            WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // actualLen

            // write the SQLVariant header (type and cbPropBytes)
            WriteByte(mt.TDSType, stateObj); 
            WriteByte(mt.PropBytes, stateObj);
 
            // now write the actual PropBytes and data 
            switch (mt.TDSType) {
                case TdsEnums.SQLFLT4: 
                    WriteFloat((Single)value, stateObj);
                    break;

                case TdsEnums.SQLFLT8: 
                    WriteDouble((Double)value, stateObj);
                    break; 
 
                case TdsEnums.SQLINT8:
                    WriteLong((Int64)value, stateObj); 
                    break;

                case TdsEnums.SQLINT4:
                    WriteInt((Int32)value, stateObj); 
                    break;
 
                case TdsEnums.SQLINT2: 
                    WriteShort((Int16)value, stateObj);
                    break; 

                case TdsEnums.SQLINT1:
                    WriteByte((byte)value, stateObj);
                    break; 

                case TdsEnums.SQLBIT: 
                    if ((bool)value == true) 
                        WriteByte(1, stateObj);
                    else 
                        WriteByte(0, stateObj);

                    break;
 
                case TdsEnums.SQLBIGVARBINARY:
                    { 
                        byte[] b = (byte[])value; 

                        WriteShort(length, stateObj); // propbytes: varlen 
                        WriteByteArray(b, length, offset, stateObj);
                        break;
                    }
 
                case TdsEnums.SQLBIGVARCHAR:
                    { 
                        string s = (string)value; 

                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info 
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId
                        WriteShort(length, stateObj); // propbyte: varlen
                        WriteEncodingChar(s, _defaultEncoding, stateObj);
                        break; 
                    }
 
                case TdsEnums.SQLUNIQUEID: 
                    {
                        System.Guid guid = (System.Guid)value; 
                        byte[] b = guid.ToByteArray();

                        Debug.Assert((length == b.Length) && (length == 16), "Invalid length for guid type in com+ object");
                        WriteByteArray(b, length, 0, stateObj); 
                        break;
                    } 
 
                case TdsEnums.SQLNVARCHAR:
                    { 
                        string s = (string)value;

                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId 
                        WriteShort(length, stateObj); // propbyte: varlen
 
                        // string takes cchar, not cbyte so convert 
                        length >>= 1;
                        WriteString(s, length, offset, stateObj); 
                        break;
                    }

                case TdsEnums.SQLDATETIME: 
                    {
                        TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8); 
 
                        WriteInt(dt.days, stateObj);
                        WriteInt(dt.time, stateObj); 
                        break;
                    }

                case TdsEnums.SQLMONEY: 
                    {
                        WriteCurrency((Decimal)value, 8, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLNUMERICN:
                    {
                        WriteByte(mt.Precision, stateObj); //propbytes: precision
                        WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10), stateObj); // propbytes: scale 
                        WriteDecimal((Decimal)value, stateObj);
                        break; 
                    } 

                case TdsEnums.SQLTIME: 
                    WriteByte(mt.Scale, stateObj); //propbytes: scale
                    WriteTime((TimeSpan)value, mt.Scale, length, stateObj);
                    break;
 
                case TdsEnums.SQLDATETIMEOFFSET:
                    WriteByte(mt.Scale, stateObj); //propbytes: scale 
                    WriteDateTimeOffset((DateTimeOffset)value, mt.Scale, length, stateObj); 
                    break;
 
                default:
                    Debug.Assert(false, "unknown tds type for sqlvariant!");
                    break;
            } // switch 
        }
 
// todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider 
// combining these tow methods.
 
        //
        // Translates a com+ object -> SqlVariant
        // when the type is ambiguous, we always convert to the bigger type
        // note that we also write out the maxlen and actuallen members (4 bytes each) 
        // in addition to the SQLVariant structure
        // 
        // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value. 
        // Therefore the sql_variant value must not include the MaxLength. This is the major difference
        // between this method and WriteSqlVariantValue above. 
        //
        internal void WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj) {
            Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
 
            // handle null values
            if ((null == value) || (DBNull.Value == value)) { 
                WriteInt(TdsEnums.FIXEDNULL, stateObj); 
                return;
            } 

            MetaType metatype = MetaType.GetMetaTypeFromValue(value);
            int length = 0;
 
            if (metatype.IsAnsiType) {
                length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding); 
            } 

            switch (metatype.TDSType) { 
                case TdsEnums.SQLFLT4:
                    WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteFloat((Single)value, stateObj);
                    break; 

                case TdsEnums.SQLFLT8: 
                    WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); 
                    WriteDouble((Double)value, stateObj);
                    break; 

                case TdsEnums.SQLINT8:
                    WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteLong((Int64)value, stateObj); 
                    break;
 
                case TdsEnums.SQLINT4: 
                    WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteInt((Int32)value, stateObj); 
                    break;

                case TdsEnums.SQLINT2:
                    WriteSqlVariantHeader(4, metatype.TDSType, metatype.PropBytes, stateObj); 
                    WriteShort((Int16)value, stateObj);
                    break; 
 
                case TdsEnums.SQLINT1:
                    WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj); 
                    WriteByte((byte)value, stateObj);
                    break;

                case TdsEnums.SQLBIT: 
                    WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj);
                    if ((bool)value == true) 
                        WriteByte(1, stateObj); 
                    else
                        WriteByte(0, stateObj); 

                    break;

                case TdsEnums.SQLBIGVARBINARY: 
                    {
                        byte[] b = (byte[])value; 
 
                        length = b.Length;
                        WriteSqlVariantHeader(4 + length, metatype.TDSType, metatype.PropBytes, stateObj); 
                        WriteShort(length, stateObj); // propbytes: varlen
                        WriteByteArray(b, length, 0, stateObj);
                        break;
                    } 

                case TdsEnums.SQLBIGVARCHAR: 
                    { 
                        string s = (string)value;
 
                        length = s.Length;
                        WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId 
                        WriteShort(length, stateObj);
                        WriteEncodingChar(s, _defaultEncoding, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLUNIQUEID:
                    {
                        System.Guid guid = (System.Guid)value;
                        byte[] b = guid.ToByteArray(); 

                        length = b.Length; 
                        Debug.Assert(length == 16, "Invalid length for guid type in com+ object"); 
                        WriteSqlVariantHeader(18, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteByteArray(b, length, 0, stateObj); 
                        break;
                    }

                case TdsEnums.SQLNVARCHAR: 
                    {
                        string s = (string)value; 
 
                        length = s.Length * 2;
                        WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj); 
                        WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
                        WriteByte(_defaultCollation.sortId, stateObj); // propbytes: collation.SortId
                        WriteShort(length, stateObj); // propbyte: varlen
 
                        // string takes cchar, not cbyte so convert
                        length >>= 1; 
                        WriteString(s, length, 0, stateObj); 
                        break;
                    } 

                case TdsEnums.SQLDATETIME:
                    {
                        TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8); 

                        WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj); 
                        WriteInt(dt.days, stateObj); 
                        WriteInt(dt.time, stateObj);
                        break; 
                    }

                case TdsEnums.SQLMONEY:
                    { 
                        WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteCurrency((Decimal)value, 8, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLNUMERICN:
                    {
                        WriteSqlVariantHeader(21, metatype.TDSType, metatype.PropBytes, stateObj);
                        WriteByte(metatype.Precision, stateObj); //propbytes: precision 
                        WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10), stateObj); // propbytes: scale
                        WriteDecimal((Decimal)value, stateObj); 
                        break; 
                    }
 
                case TdsEnums.SQLTIME:
                    WriteSqlVariantHeader(8, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteByte(metatype.Scale, stateObj); //propbytes: scale
                    WriteTime((TimeSpan)value, metatype.Scale, 5, stateObj); 
                    break;
 
                case TdsEnums.SQLDATETIMEOFFSET: 
                    WriteSqlVariantHeader(13, metatype.TDSType, metatype.PropBytes, stateObj);
                    WriteByte(metatype.Scale, stateObj); //propbytes: scale 
                    WriteDateTimeOffset((DateTimeOffset)value, metatype.Scale, 10, stateObj);
                    break;

                default: 
                    Debug.Assert(false, "unknown tds type for sqlvariant!");
                    break; 
            } // switch 
        }
 
        internal void WriteSqlVariantHeader(int length, byte tdstype, byte propbytes, TdsParserStateObject stateObj) {
            WriteInt(length, stateObj);
            WriteByte(tdstype, stateObj);
            WriteByte(propbytes, stateObj); 
        }
 
        private void WriteSqlMoney(SqlMoney value, int length, TdsParserStateObject stateObj) { 
            //
            int[] bits = Decimal.GetBits(value.Value); 

            // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
            bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
            long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0]; 

            if (isNeg) 
                l = -l; 

            if (length == 4) { 
                Decimal decimalValue = value.Value;

                // validate the value can be represented as a small money
                if (decimalValue < TdsEnums.SQL_SMALL_MONEY_MIN || decimalValue > TdsEnums.SQL_SMALL_MONEY_MAX) { 
                    throw SQL.MoneyOverflow(decimalValue.ToString(CultureInfo.InvariantCulture));
                } 
 
                WriteInt((int)l, stateObj);
            } 
            else {
                WriteInt((int)(l >> 0x20), stateObj);
                WriteInt((int)l, stateObj);
            } 
        }
 
        private void WriteCurrency(Decimal value, int length, TdsParserStateObject stateObj) { 
            SqlMoney m = new SqlMoney(value);
            int[] bits = Decimal.GetBits(m.Value); 

            // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
            bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
            long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0]; 

            if (isNeg) 
                l = -l; 

            if (length == 4) { 
                // validate the value can be represented as a small money
                if (value < TdsEnums.SQL_SMALL_MONEY_MIN || value > TdsEnums.SQL_SMALL_MONEY_MAX) {
                    throw SQL.MoneyOverflow(value.ToString(CultureInfo.InvariantCulture));
                } 

                WriteInt((int)l, stateObj); 
            } 
            else {
                WriteInt((int)(l >> 0x20), stateObj); 
                WriteInt((int)l, stateObj);
            }
        }
 
        private void WriteDate(DateTime value, TdsParserStateObject stateObj) {
            int days = value.Subtract(DateTime.MinValue).Days; 
            WriteByteArray(BitConverter.GetBytes(days), 3, 0, stateObj); 
        }
 
        private void WriteTime(TimeSpan value, byte scale, int length, TdsParserStateObject stateObj) {
            if (0 > value.Ticks || value.Ticks >= TimeSpan.TicksPerDay) {
                throw SQL.TimeOverflow(value.ToString());
            } 
            Int64 time = value.Ticks / TdsEnums.TICKS_FROM_SCALE[scale];
            WriteByteArray(BitConverter.GetBytes(time), length, 0, stateObj); 
        } 

        private void WriteDateTime2(DateTime value, byte scale, int length, TdsParserStateObject stateObj) { 
            Int64 time = value.TimeOfDay.Ticks / TdsEnums.TICKS_FROM_SCALE[scale]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
            WriteByteArray(BitConverter.GetBytes(time), length - 3, 0, stateObj);
            WriteDate(value, stateObj);
        } 

        private void WriteDateTimeOffset(DateTimeOffset value, byte scale, int length, TdsParserStateObject stateObj) { 
            WriteDateTime2(value.UtcDateTime, scale, length - 2, stateObj); 
            Int16 offset = (Int16)value.Offset.TotalMinutes;
            WriteByte((byte)(offset & 0xff), stateObj); 
            WriteByte((byte)((offset >> 8) & 0xff), stateObj);
        }

        private void ReadSqlDecimal(SqlBuffer value, int length, byte precision, byte scale, TdsParserStateObject stateObj) { 
            bool fPositive = (1 == stateObj.ReadByte());
 
            length = checked((int)length-1); 

            int[] bits = ReadDecimalBits(length, stateObj); 

            value.SetToDecimal(precision, scale, fPositive, bits);
        }
 
        // @devnote: length should be size of decimal without the sign
        // @devnote: sign should have already been read off the wire 
        private int[] ReadDecimalBits(int length, TdsParserStateObject stateObj) { 
            int[] 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 
                bits[i] = stateObj.ReadInt32();
            } 

            return bits;
        }
 
        static internal SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale) {
            if (d.Scale != newScale) { 
                return SqlDecimal.AdjustScale(d, newScale - d.Scale, false /* Don't round, truncate.  MDAC 69229 */); 
            }
 
            return d;
        }

        static internal decimal AdjustDecimalScale(decimal value, int newScale) { 
            int oldScale = (Decimal.GetBits(value)[3] & 0x00ff0000) >> 0x10;
 
            if (newScale != oldScale) { 
                SqlDecimal num = new SqlDecimal(value);
 
                num = SqlDecimal.AdjustScale(num, newScale - oldScale, false /* Don't round, truncate.  MDAC 69229 */);
                return num.Value;
            }
 
            return value;
        } 
 
        internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) {
            // sign 
            if (d.IsPositive)
                WriteByte(1, stateObj);
            else
                WriteByte(0, stateObj); 

            // four ints now 
            int[] data = d.Data; 

            WriteInt(data[0], stateObj); 
            WriteInt(data[1], stateObj);
            WriteInt(data[2], stateObj);
            WriteInt(data[3], stateObj);
        } 

        private void WriteDecimal(decimal value, TdsParserStateObject stateObj) { 
            stateObj._decimalBits = Decimal.GetBits(value); 
            Debug.Assert(null != stateObj._decimalBits, "decimalBits should be filled in at TdsExecuteRPC time");
 
            /*
             Returns a binary representation of a Decimal. The return value is an integer
             array with four elements. Elements 0, 1, and 2 contain the low, middle, and
             high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains 
             the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
             unused; bits 16-23 contain a value between 0 and 28, indicating the power of 
             10 to divide the 96-bit integer part by to produce the Decimal value; bits 24- 
             30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
             meaning positive and 1 meaning negative. 

             SQLDECIMAL/SQLNUMERIC has a byte stream of:
             struct {
                 BYTE sign; // 1 if positive, 0 if negative 
                 BYTE data[];
             } 
 
             For TDS 7.0 and above, there are always 17 bytes of data
            */ 

            // write the sign (note that COM and SQL are opposite)
            if (0x80000000 == (stateObj._decimalBits[3] & 0x80000000))
                WriteByte(0, stateObj); 
            else
                WriteByte(1, stateObj); 
 
            WriteInt(stateObj._decimalBits[0], stateObj);
            WriteInt(stateObj._decimalBits[1], stateObj); 
            WriteInt(stateObj._decimalBits[2], stateObj);
            WriteInt(0, stateObj);
        }
 
        private void WriteIdentifier(string s, TdsParserStateObject stateObj) {
            if (null != s) { 
                WriteByte(checked((byte) s.Length), stateObj); 
                WriteString(s, stateObj);
            } 
            else {
                WriteByte((byte)0, stateObj);
            }
        } 

        private void WriteIdentifierWithShortLength(string s, TdsParserStateObject stateObj) { 
            if (null != s) { 
                WriteShort(checked((short)s.Length), stateObj);
                WriteString(s, stateObj); 
            }
            else {
                WriteShort(0, stateObj);
            } 
        }
 
        private void WriteString(string s, TdsParserStateObject stateObj) { 
            WriteString(s, s.Length, 0, stateObj);
        } 

        internal void WriteCharArray(char[] carr, int length, int offset, TdsParserStateObject stateObj) {
            int cBytes = ADP.CharSize * length;
 
            // Perf shortcut: If it fits, write directly to the outBuff
            if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) { 
                CopyCharsToBytes(carr, offset, stateObj._outBuff, stateObj._outBytesUsed, length); 
                stateObj._outBytesUsed += cBytes;
            } 
            else {
                if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
                    stateObj._bTmp = new byte[cBytes];
                } 

                CopyCharsToBytes(carr, offset, stateObj._bTmp, 0, length); 
                WriteByteArray(stateObj._bTmp, cBytes, 0, stateObj); 
            }
        } 

        internal void WriteString(string s, int length, int offset, TdsParserStateObject stateObj) {
            int cBytes = ADP.CharSize * length;
 
            // Perf shortcut: If it fits, write directly to the outBuff
            if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) { 
                CopyStringToBytes(s, offset, stateObj._outBuff, stateObj._outBytesUsed, length); 
                stateObj._outBytesUsed += cBytes;
            } 
            else {
                if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
                    stateObj._bTmp = new byte[cBytes];
                } 

                CopyStringToBytes(s, offset, stateObj._bTmp, 0, length); 
                WriteByteArray(stateObj._bTmp, cBytes, 0, stateObj); 
            }
        } 


        private unsafe static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength) {
            // DEVNOTE: BE EXTREMELY CAREFULL in this method.  Since it pins the buffers and copies the memory 
            //  directly, it bypasses all of the CLR's usual array-bounds checking.  For maintainability, the checks
            //  here should NOT be removed just because some other code will check them ahead of time. 
            if (charLength < 0) { 
                throw ADP.InvalidDataLength(charLength);
            } 

            if (checked(sourceOffset + charLength) > source.Length || sourceOffset < 0) {
                throw ADP.IndexOutOfRange(sourceOffset);
            } 

            // charLength >= 0 & checked conversion implies byteLength >= 0 
            int byteLength = checked(charLength * ADP.CharSize); 

            if (checked(destOffset + byteLength) > dest.Length || destOffset < 0) { 
                throw ADP.IndexOutOfRange(destOffset);
            }

            fixed (char* sourcePtr = source) { 
                char* srcPtr = sourcePtr;  // Can't increment the target of a Fixed statement
                srcPtr += sourceOffset;  // char* increments by sizeof(char) 
                fixed (byte* destinationPtr = dest) { 
                    byte* destPtr = destinationPtr;
                    destPtr += destOffset; 
                    NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength);
                }
            }
        } 

        private unsafe static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength) { 
            // DEVNOTE: BE EXTREMELY CAREFULL in this method.  Since it pins the buffers and copies the memory 
            //  directly, it bypasses all of the CLR's usual array-bounds checking.  For maintainability, the checks
            //  here should NOT be removed just because some other code will check them ahead of time. 
            if (charLength < 0) {
                throw ADP.InvalidDataLength(charLength);
            }
 
            if (sourceOffset + charLength > source.Length || sourceOffset < 0) {
                throw ADP.IndexOutOfRange(sourceOffset); 
            } 

            // charLength >= 0 & checked conversion implies byteLength >= 0 
            int byteLength = checked(charLength * ADP.CharSize);

            if (destOffset + byteLength > dest.Length || destOffset < 0) {
                throw ADP.IndexOutOfRange(destOffset); 
            }
 
            fixed (char* sourcePtr = source) { 
                char* srcPtr = sourcePtr;  // Can't increment the target of a Fixed statement
                srcPtr += sourceOffset;  // char* increments by sizeof(char) 
                fixed (byte* destinationPtr = dest) {
                    byte* destPtr = destinationPtr;
                    destPtr += destOffset;
                    NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength); 
                }
            } 
        } 

        private void WriteEncodingChar(string s, Encoding encoding, TdsParserStateObject stateObj) { 
            WriteEncodingChar(s, s.Length, 0, encoding, stateObj);
        }

        private void WriteEncodingChar(string s, int numChars, int offset, Encoding encoding, TdsParserStateObject stateObj) { 
            char[] charData;
            byte[] byteData; 
 
            // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
            // 7.0 has no support for multiple code pages in data - single code page support only 
            if (encoding == null)
                encoding = _defaultEncoding;

            charData = s.ToCharArray(offset, numChars); 
            byteData = encoding.GetBytes(charData, 0, numChars);
            Debug.Assert(byteData != null, "no data from encoding"); 
            WriteByteArray(byteData, byteData.Length, 0, stateObj); 
        }
 
        internal int GetEncodingCharLength(string value, int numChars, int charOffset, Encoding encoding) {
            //

            if (value == null || value == ADP.StrEmpty) { 
                return 0;
            } 
 
            // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
            // 7.0 has no support for multiple code pages in data - single code page support only 
            if (encoding == null) {
                if (null == _defaultEncoding) {
                    ThrowUnsupportedCollationEncountered(null);
                } 

                encoding = _defaultEncoding; 
            } 

            char[] charData = value.ToCharArray(charOffset, numChars); 

            return encoding.GetByteCount(charData, 0, numChars);
        }
 
        //
        // Returns the data stream length of the data identified by tds type or SqlMetaData returns 
        // Returns either the total size or the size of the first chunk for partially length prefixed types. 
        //
        internal ulong GetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject stateObj) { 
            // Handle Yukon specific tokens
            if (_isYukon && colmeta.metaType.IsPlp) {
                Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
                             colmeta.tdsType == TdsEnums.SQLBIGVARCHAR || 
                             colmeta.tdsType == TdsEnums.SQLBIGVARBINARY ||
                             colmeta.tdsType == TdsEnums.SQLNVARCHAR || 
                             // Large UDTs is WinFS-only 
                             colmeta.tdsType == TdsEnums.SQLUDT,
                             "GetDataLength:Invalid streaming datatype"); 
                return stateObj.ReadPlpLength(true);
            }
            else {
                return (ulong)GetTokenLength(colmeta.tdsType, stateObj); 
            }
        } 
 
        //
        // 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 int GetTokenLength(byte token, TdsParserStateObject stateObj) {
            Debug.Assert(token != 0, "0 length token!"); 
 
            if (_isYukon) {     // Handle Yukon specific exceptions
                if (token == TdsEnums.SQLUDT) { // special case for UDTs 
                    return -1; // Should we return -1 or not call GetTokenLength for UDTs?
                }
                else if (token == TdsEnums.SQLRETURNVALUE) {
                    // In Yukon, the RETURNVALUE token stream no longer has length 
                    return -1;
                } 
                else if (token == TdsEnums.SQLXMLTYPE) { 
                    int tokenLength = (int)stateObj.ReadUInt16();
                    Debug.Assert(tokenLength == TdsEnums.SQL_USHORTVARMAXLEN, "Invalid token stream for xml datatype"); 
                    return tokenLength;
                }
            }
 
            switch (token & TdsEnums.SQLLenMask) {
                case TdsEnums.SQLFixedLen: 
                    return ((0x01 << ((token & 0x0c) >> 2))) & 0xff; 
                case TdsEnums.SQLZeroLen:
                    return 0; 
                case TdsEnums.SQLVarLen:
                case TdsEnums.SQLVarCnt:
                    if (0 != (token & 0x80)) {
                        return stateObj.ReadUInt16(); 
                    }
                    else if (0 == (token & 0x0c)) { 
                        // 
                        return stateObj.ReadInt32();
                    } 
                    else {
                        return stateObj.ReadByte();
                    }
                default: 
                    Debug.Assert(false, "Unknown token length!");
                    return 0; 
            } 
        }
 
        private void ProcessAttention(TdsParserStateObject stateObj) {
            if (_state == TdsParserState.Closed || _state == TdsParserState.Broken){
                return;
            } 
            Debug.Assert(stateObj._attentionSent, "invalid attempt to ProcessAttention, attentionSent == false!");
 
            // Attention processing scenarios: 
            // 1) EOM packet with header ST_AACK bit plus DONE with status DONE_ATTN
            // 2) Packet without ST_AACK header bit but has DONE with status DONE_ATTN 
            // 3) Secondary timeout occurs while reading, break connection

            // Since errors can occur and we need to cancel prior to throwing those errors, we
            // cache away error state and then process TDS for the attention.  We restore those 
            // errors after processing.
 
            // Lock the error collection 
            // so the error collections are kept in a known state.
            // the error collections are modified here and in ThrowExceptionAndWarning. 
            // which can come in at any time
            lock (_ErrorCollectionLock) {
                _attentionErrors = _errors;
                _attentionWarnings = _warnings; 
                _errors =  null;
                _warnings = null; 
 
                try {
                    // Call run loop to process looking for attention ack. 
                    Run(RunBehavior.Attention, null, null, null, stateObj);
                }
                catch (Exception e) {
                    // 
                    if (!ADP.IsCatchableExceptionType(e)) {
                        throw; 
                    } 

                    // If an exception occurs - break the connection. 
                    // Attention error will not be thrown in this case by Run(), but other failures may.
                    ADP.TraceExceptionWithoutRethrow(e);
                    _state = TdsParserState.Broken;
                    _connHandler.BreakConnection(); 

                  throw; 
                } 

                Debug.Assert(_errors == null && _warnings == null, "Unexpected error state on ProcessAttention"); 

                // Restore error state
                _errors = _attentionErrors;
                _warnings = _attentionWarnings; 

                _attentionErrors = null; 
                _attentionWarnings = null; 

                Debug.Assert(!stateObj._attentionSent, "Invalid attentionSent state at end of ProcessAttention"); 
            }
        }

        internal void TdsLogin(SqlLogin rec) { 
            _physicalStateObj.SetTimeoutSeconds(rec.timeout);
 
            Debug.Assert(TdsEnums.MAXLEN_HOSTNAME>=rec.hostName.Length, "_workstationId.Length exceeds the max length for this value"); 
            Debug.Assert(TdsEnums.MAXLEN_USERNAME>=rec.userName.Length, "_userID.Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_PASSWORD>=rec.password.Length, "_password.Length exceeds the max length for this value"); 
            Debug.Assert(TdsEnums.MAXLEN_APPNAME>=rec.applicationName.Length, "_applicationName.Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_SERVERNAME>=rec.serverName.Length, "_dataSource.Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_LANGUAGE>=rec.language.Length, "_currentLanguage .Length exceeds the max length for this value");
            Debug.Assert(TdsEnums.MAXLEN_DATABASE>=rec.database.Length, "_initialCatalog.Length exceeds the max length for this value"); 
            Debug.Assert(TdsEnums.MAXLEN_ATTACHDBFILE>=rec.attachDBFilename.Length, "_attachDBFileName.Length exceeds the max length for this value");
 
            // get the password up front to use in sspi logic below 
            byte[] encryptedPassword = null;
            encryptedPassword = TdsParserStaticMethods.EncryptPassword(rec.password); 

            byte[] encryptedChangePassword = null;
            encryptedChangePassword = TdsParserStaticMethods.EncryptPassword(rec.newPassword);
 
            // set the message type
            _physicalStateObj._outputMessageType = TdsEnums.MT_LOGIN7; 
 
            // length in bytes
            int length = TdsEnums.YUKON_LOG_REC_FIXED_LEN; 

            string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME;
            Debug.Assert(TdsEnums.MAXLEN_CLIENTINTERFACE >= clientInterfaceName.Length, "cchCltIntName can specify at most 128 unicode characters. See Tds spec");
 
            // add up variable-len portions (multiply by 2 for byte len of char strings)
            // 
            checked { 
                length += (rec.hostName.Length + rec.applicationName.Length +
                            rec.serverName.Length + clientInterfaceName.Length + 
                            rec.language.Length + rec.database.Length +
                            rec.attachDBFilename.Length) * 2;
            }
 
            // allocate memory for SSPI variables
            byte[] outSSPIBuff = null; 
            UInt32 outSSPILength = 0; 

            // only add lengths of password and username if not using SSPI 
            if (!rec.useSSPI) {
                checked {
                    length += (rec.userName.Length * 2) + encryptedPassword.Length
                    + encryptedChangePassword.Length; 
                }
            } 
            else { 
                if (rec.useSSPI) {
                    // now allocate proper length of buffer, and set length 
                    outSSPIBuff = new byte[s_maxSSPILength];
                    outSSPILength = s_maxSSPILength;

                    // Call helper function for SSPI data and actual length. 
                    // Since we don't have SSPI data from the server, send null for the
                    // byte[] buffer and 0 for the int length. 
                    Debug.Assert(SniContext.Snix_Login==_physicalStateObj.SniContext, String.Format((IFormatProvider)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj.SniContext)); 
                    _physicalStateObj.SniContext = SniContext.Snix_LoginSspi;
                    SSPIData(null, 0, outSSPIBuff, ref outSSPILength); 
                    if (outSSPILength > Int32.MaxValue) {
                        throw SQL.InvalidSSPIPacketSize();  // SqlBu 332503
                    }
                    _physicalStateObj.SniContext=SniContext.Snix_Login; 

                    checked { 
                        length += (Int32)outSSPILength; 
                    }
                } 
            }

            try {
                WriteInt(length, _physicalStateObj); 
#if WINFSFunctionality
                WriteInt((TdsEnums.WINFS_MAJOR << 24) | (TdsEnums.WINFS_INCREMENT<<16) | TdsEnums.WINFS_RTM_MINOR, _physicalStateObj); 
#else 
                WriteInt((TdsEnums.KATMAI_MAJOR << 24) | (TdsEnums.KATMAI_INCREMENT<<16) | TdsEnums.KATMAI_MINOR, _physicalStateObj);
#endif 
                WriteInt(rec.packetSize, _physicalStateObj);
                WriteInt(TdsEnums.CLIENT_PROG_VER, _physicalStateObj);
                WriteInt(TdsParserStaticMethods.GetCurrentProcessId(), _physicalStateObj); //MDAC 84718
                WriteInt(0, _physicalStateObj); // connectionID is unused 

                // Log7Flags (DWORD) 
                WriteByte(TdsEnums.USE_DB_ON << 5 | 
                          TdsEnums.INIT_DB_FATAL << 6 |
                          TdsEnums.SET_LANG_ON << 7, 
                          _physicalStateObj);
                WriteByte((byte)(TdsEnums.INIT_LANG_FATAL |
                          TdsEnums.ODBC_ON << 1 |
                          (rec.useReplication ? (TdsEnums.REPL_ON << 4) : 0) | 
                          (rec.useSSPI ? (TdsEnums.SSPI_ON << 7) : 0)),
                          _physicalStateObj); 
                WriteByte(TdsEnums.SQL_DFLT, _physicalStateObj); // TypeFlags 
                WriteByte((byte) (((byte)(ADP.IsEmpty(rec.newPassword)?0:1)) |
                                  ((byte)(rec.userInstance ? ((byte) 1) << 2 : 0))), 
                          _physicalStateObj); // OptionFlags3

                WriteInt(0, _physicalStateObj);  // ClientTimeZone is not used
                WriteInt(0, _physicalStateObj);  // LCID is unused by server 

                // Start writing offset and length of variable length portions 
                int offset = TdsEnums.YUKON_LOG_REC_FIXED_LEN; 

                // write offset/length pairs 

                // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record
                WriteShort(offset, _physicalStateObj); // host name offset
                WriteShort(rec.hostName.Length, _physicalStateObj); 
                offset += rec.hostName.Length * 2;
 
                // Only send user/password over if not fSSPI...  If both user/password and SSPI are in login 
                // rec, only SSPI is used.  Confirmed same bahavior as in luxor.
                if (rec.useSSPI == false) { 
                    WriteShort(offset, _physicalStateObj);  // userName offset
                    WriteShort(rec.userName.Length, _physicalStateObj);
                    offset += rec.userName.Length * 2;
 
                    // the encrypted password is a byte array - so length computations different than strings
                    WriteShort(offset, _physicalStateObj); // password offset 
                    WriteShort(encryptedPassword.Length / 2, _physicalStateObj); 
                    offset += encryptedPassword.Length;
                } 
                else {
                    // case where user/password data is not used, send over zeros
                    WriteShort(0, _physicalStateObj);  // userName offset
                    WriteShort(0, _physicalStateObj); 
                    WriteShort(0, _physicalStateObj);  // password offset
                    WriteShort(0, _physicalStateObj); 
                } 

                WriteShort(offset, _physicalStateObj); // app name offset 
                WriteShort(rec.applicationName.Length, _physicalStateObj);
                offset += rec.applicationName.Length * 2;

                WriteShort(offset, _physicalStateObj); // server name offset 
                WriteShort(rec.serverName.Length, _physicalStateObj);
                offset += rec.serverName.Length * 2; 
 
                WriteShort(offset, _physicalStateObj); // remote password name offset
                WriteShort(0, _physicalStateObj); 

                WriteShort(offset, _physicalStateObj); // client interface name offset
                WriteShort(clientInterfaceName.Length, _physicalStateObj);
                offset += clientInterfaceName.Length * 2; 

                WriteShort(offset, _physicalStateObj); // language name offset 
                WriteShort(rec.language.Length, _physicalStateObj); 
                offset += rec.language.Length * 2;
 
                WriteShort(offset, _physicalStateObj); // database name offset
                WriteShort(rec.database.Length, _physicalStateObj);
                offset += rec.database.Length * 2;
 
                //
 
                if (null == s_nicAddress) 
                    s_nicAddress = TdsParserStaticMethods.GetNIC();
 
                WriteByteArray(s_nicAddress, s_nicAddress.Length, 0, _physicalStateObj);

                WriteShort(offset, _physicalStateObj); // ibSSPI offset
                if (rec.useSSPI) { 
                    WriteShort((int)outSSPILength, _physicalStateObj);
                    offset += (int)outSSPILength; 
                } 
                else {
                    WriteShort(0, _physicalStateObj); 
                }

                WriteShort(offset, _physicalStateObj); // DB filename offset
                WriteShort(rec.attachDBFilename.Length, _physicalStateObj); 
                offset += rec.attachDBFilename.Length * 2;
 
                WriteShort(offset, _physicalStateObj); // reset password offset 
                WriteShort(encryptedChangePassword.Length / 2, _physicalStateObj);
 
                WriteInt(0, _physicalStateObj);        // reserved for chSSPI

                // write variable length portion
                WriteString(rec.hostName, _physicalStateObj); 

                // if we are using SSPI, do not send over username/password, since we will use SSPI instead 
                // same behavior as Luxor 
                if (!rec.useSSPI) {
                    WriteString(rec.userName, _physicalStateObj); 

                    // Cache offset in packet for tracing.
                    _physicalStateObj._tracePasswordOffset = _physicalStateObj._outBytesUsed;
                    _physicalStateObj._tracePasswordLength = encryptedPassword.Length; 

                    WriteByteArray(encryptedPassword, encryptedPassword.Length, 0, _physicalStateObj); 
                } 

                WriteString(rec.applicationName, _physicalStateObj); 
                WriteString(rec.serverName, _physicalStateObj);

                // Intentially skip, we never use this field
                // WriteString(rec.remotePassword); 
                WriteString(clientInterfaceName, _physicalStateObj);
                WriteString(rec.language, _physicalStateObj); 
                WriteString(rec.database, _physicalStateObj); 

                // send over SSPI data if we are using SSPI 
                if (rec.useSSPI)
                    WriteByteArray(outSSPIBuff, (int)outSSPILength, 0, _physicalStateObj);

                WriteString(rec.attachDBFilename, _physicalStateObj); 
                if (!rec.useSSPI) {
                    // Cache offset in packet for tracing. 
                    _physicalStateObj._traceChangePasswordOffset = _physicalStateObj._outBytesUsed; 
                    _physicalStateObj._traceChangePasswordLength = encryptedChangePassword.Length;
                    WriteByteArray(encryptedChangePassword, encryptedChangePassword.Length, 0, _physicalStateObj); 
                }

            } // try
            catch (Exception e) { 
                //
                if (ADP.IsCatchableExceptionType(e)) { 
                    // be sure to wipe out our buffer if we started sending stuff 
                    _physicalStateObj._outputPacketNumber = 1;  // end of message - reset to 1 - per ramas
                    _physicalStateObj.ResetBuffer(); 
                }

                throw;
            } 

            _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); 
            _physicalStateObj._pendingData = true; 
        }// tdsLogin
 
        private void SSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength) {
            SNISSPIData(receivedBuff, receivedLength, sendBuff, ref sendLength);
        }
 
        private void SNISSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength)
        { 
            if (receivedBuff == null) 
            {
                // we do not have SSPI data coming from server, so send over 0's for pointer and length 
                receivedLength = 0;
            }
            // we need to respond to the server's message with SSPI data
            if(0 != SNINativeMethodWrapper.SNISecGenClientContext(_physicalStateObj.Handle, receivedBuff, receivedLength, sendBuff, ref sendLength, _sniServerUserName)) 
            {
                SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT); 
            } 
        }
 

        private void ProcessSSPI(int receivedLength) {
            SniContext outerContext=_physicalStateObj.SniContext;
            _physicalStateObj.SniContext = SniContext.Snix_ProcessSspi; 
            // allocate received buffer based on length from SSPI message
            byte[] receivedBuff = new byte[receivedLength]; 
 
            // read SSPI data received from server
            _physicalStateObj.ReadByteArray(receivedBuff, 0, receivedLength); 

            // allocate send buffer and initialize length
            byte[] sendBuff = new byte[s_maxSSPILength];
            UInt32 sendLength = s_maxSSPILength; 

            // make call for SSPI data 
            SSPIData(receivedBuff, (UInt32)receivedLength, sendBuff, ref sendLength); 

            // DO NOT SEND LENGTH - TDS DOC INCORRECT!  JUST SEND SSPI DATA! 
            WriteByteArray(sendBuff, (int)sendLength, 0, _physicalStateObj);

            // set message type so server knows its a SSPI response
            _physicalStateObj._outputMessageType = TdsEnums.MT_SSPI; 

            // send to server 
            _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); 
            _physicalStateObj.SniContext=outerContext;
        } 

        private void SSPIError(string error, string procedure) {
            Debug.Assert(!ADP.IsEmpty(procedure), "TdsParser.SSPIError called with an empty or null procedure string");
            Debug.Assert(!ADP.IsEmpty(error), "TdsParser.SSPIError called with an empty or null error string"); 

            Errors.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, _server, error, procedure, 0)); 
            ThrowExceptionAndWarning(_physicalStateObj); 
        }
 
        private void LoadSSPILibrary() {
            // Outer check so we don't acquire lock once once it's loaded.
            if (!s_fSSPILoaded) {
                lock (s_tdsParserLock) { 
                    // re-check inside lock
                    if (!s_fSSPILoaded) { 
                        // use local for ref param to defer setting s_maxSSPILength until we know the call succeeded. 
                        UInt32 maxLength = 0;
                        if (0 != SNINativeMethodWrapper.SNISecInitPackage(ref maxLength)) 
                            SSPIError(SQLMessage.SSPIInitializeError(), TdsEnums.INIT_SSPI_PACKAGE);

                        s_maxSSPILength = maxLength;
                        s_fSSPILoaded = true; 

                    } 
                } 
            }
 
            if (s_maxSSPILength > Int32.MaxValue) {
                throw SQL.InvalidSSPIPacketSize();   // SqlBu 332503
            }
        } 

        internal byte[] GetDTCAddress(int timeout, TdsParserStateObject stateObj) { 
            // If this fails, the server will return a server error - Sameet Agarwal confirmed. 
            // Success: DTCAddress returned.  Failure: SqlError returned.
 
            byte[] dtcAddr = null;

            using (SqlDataReader dtcReader = TdsExecuteTransactionManagerRequest(
                                                        null, 
                                                        TdsEnums.TransactionManagerRequestType.GetDTCAddress,
                                                        null, 
                                                        TdsEnums.TransactionManagerIsolationLevel.Unspecified, 
                                                        timeout, null, stateObj, false)) {
 
                Debug.Assert(SniContext.Snix_Read==stateObj.SniContext, String.Format((IFormatProvider)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj.SniContext));
                if (null != dtcReader && dtcReader.Read()) {
                    Debug.Assert(dtcReader.GetName(0) == "TM Address", "TdsParser: GetDTCAddress did not return 'TM Address'");
 
                    // DTCAddress is of variable size, and does not have a maximum.  So we call GetBytes
                    // to get the length of the dtcAddress, then allocate a byte array of that length, 
                    // then call GetBytes again on that byte[] with the length 
                    long dtcLength = dtcReader.GetBytes(0, 0, null, 0, 0);
 
                    //
                    if (dtcLength <= Int32.MaxValue) {
                        int cb = (int)dtcLength;
 
                        dtcAddr = new byte[cb];
                        dtcReader.GetBytes(0, 0, dtcAddr, 0, cb); 
                    } 
#if DEBUG
                    else { 
                        Debug.Assert(false, "unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes");
                        // if we hit this case we'll just return a null address so that the user
                        // will get a transcaction enlistment error in the upper layers
                    } 
#endif
                } 
            } 
            return dtcAddr;
        } 

        // Propagate the dtc cookie to the server, enlisting the connection.
        internal void PropagateDistributedTransaction(byte[] buffer, int timeout, TdsParserStateObject stateObj) {
            // if this fails, the server will return a server error - Sameet Agarwal confirmed 
            // Success: server will return done token.  Failure: SqlError returned.
 
            TdsExecuteTransactionManagerRequest(buffer, 
                TdsEnums.TransactionManagerRequestType.Propagate, null,
                TdsEnums.TransactionManagerIsolationLevel.Unspecified, timeout, null, stateObj, false); 
        }

        internal SqlDataReader TdsExecuteTransactionManagerRequest(
                    byte[] buffer, 
                    TdsEnums.TransactionManagerRequestType request,
                    string transactionName, 
                    TdsEnums.TransactionManagerIsolationLevel isoLevel, 
                    int timeout,
                    SqlInternalTransaction transaction, 
                    TdsParserStateObject stateObj,
                    bool isDelegateControlRequest) {

            Debug.Assert(this == stateObj.Parser, "different parsers"); 

            if (TdsParserState.Broken == State || TdsParserState.Closed == State) { 
                return null; 
            }
 
            // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
            // delegated transactions often happen while there is an open result
            // set, so we need to handle them by using a different MARS session,
            // otherwise we'll write on the physical state objects while someone 
            // else is using it.  When we don't have MARS enabled, we need to
            // lock the physical state object to syncronize it's use at least 
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they
            // won't stomp on anything. 

            bool mustRelease = false;

            lock (_connHandler) { 
                try {
                    if (_isYukon && !MARSOn) { 
                        Monitor.Enter(_physicalStateObj); 
                        mustRelease = true;
                    } 

                    // This validation step MUST be done after locking the connection to guarantee we don't
                    //  accidentally execute after the transaction has completed on a different thread.
                    if (!isDelegateControlRequest) { 
                        _connHandler.ValidateTransaction();
                    } 
 
                    stateObj._outputMessageType = TdsEnums.MT_TRANS;       // set message type
                    stateObj.SetTimeoutSeconds(timeout); 

                    stateObj.SniContext = SniContext.Snix_Execute;

                    if (_isYukon) { 
                        WriteMarsHeader(stateObj, _currentTransaction);
                    } 
 
                    WriteShort((short)request, stateObj); // write TransactionManager Request type
 
                    bool returnReader = false;

                    switch (request) {
                        case TdsEnums.TransactionManagerRequestType.GetDTCAddress: 
                            WriteShort(0, stateObj);
 
                            returnReader = true; 
                            break;
                        case TdsEnums.TransactionManagerRequestType.Propagate: 
                            if (null != buffer) {
                                WriteShort(buffer.Length, stateObj);
                                WriteByteArray(buffer, buffer.Length, 0, stateObj);
                            } 
                            else {
                                WriteShort(0, stateObj); 
                            } 
                            break;
                        case TdsEnums.TransactionManagerRequestType.Begin: 
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
                            Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!");

                            // Only assign the passed in transaction if it is not equal to the current transaction. 
                            // And, if it is not equal, the current actually should be null.  Anything else
                            // is a unexpected state.  The concern here is mainly for the mixed use of 
                            // T-SQL and API transactions.  See SQL BU DT 345300 for full details and repro. 

                            // Expected states: 
                            // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
                            // passed in on BeginTransaction API call.
                            // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
                            // passed in but equivalent to _currentTransaction. 

                            // #1 will occur on standard BeginTransactionAPI call.  #2 should only occur if 
                            // t-sql transaction started followed by a call to SqlConnection.BeginTransaction. 
                            // Any other state is unknown.
                            if (_currentTransaction != transaction) { 
                                Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point");
                                PendingTransaction = transaction;
                            }
 
                            WriteByte((byte)isoLevel, stateObj);
 
                            WriteByte((byte)(transactionName.Length * 2), stateObj); // Write number of bytes (unicode string). 
                            WriteString(transactionName, stateObj);
                            break; 
                        case TdsEnums.TransactionManagerRequestType.Promote:
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
                            // No payload - except current transaction in header
                            // Promote returns a DTC cookie.  However, the transaction cookie we use for the 
                            // connection does not change after a promote.
                            break; 
                        case TdsEnums.TransactionManagerRequestType.Commit: 
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");
 
                            Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit");
                            WriteByte((byte)0, stateObj); // No xact name

                            WriteByte(0, stateObj);  // No flags 

                            Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); 
                            // WriteByte((byte) 0, stateObj); // IsolationLevel 
                            // WriteByte((byte) 0, stateObj); // No begin xact name
                            break; 
                        case TdsEnums.TransactionManagerRequestType.Rollback:
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");

                            WriteByte((byte)(transactionName.Length * 2), stateObj); // Write number of bytes (unicode string). 
                            WriteString(transactionName, stateObj);
 
                            WriteByte(0, stateObj);  // No flags 

                            Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); 
                            // WriteByte((byte) 0, stateObj); // IsolationLevel
                            // WriteByte((byte) 0, stateObj); // No begin xact name
                            break;
                        case TdsEnums.TransactionManagerRequestType.Save: 
                            Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");
 
                            WriteByte((byte)(transactionName.Length * 2), stateObj); // Write number of bytes (unicode string). 
                            WriteString(transactionName, stateObj);
                            break; 
                        default:
                            Debug.Assert(false, "Unexpected TransactionManagerRequest");
                            break;
                    } 

                    stateObj.WritePacket(TdsEnums.HARDFLUSH); 
                    stateObj._pendingData = true; 

                    SqlDataReader dtcReader = null; 
                    stateObj.SniContext = SniContext.Snix_Read;
                    if (returnReader) {
                        dtcReader = new SqlDataReader(null, CommandBehavior.Default);
                        Debug.Assert(this == stateObj.Parser, "different parser"); 
                        dtcReader.Bind(stateObj);
 
                        // force consumption of metadata 
                        _SqlMetaDataSet metaData = dtcReader.MetaData;
                    } 
                    else {
                        Run(RunBehavior.UntilDone, null, null, null, stateObj);
                    }
 
                    return dtcReader;
                } 
                catch (Exception e) { 
                    //
                    if (!ADP.IsCatchableExceptionType(e)) { 
                        throw;
                    }

                    FailureCleanup(stateObj, e); 

                    throw; 
                } 
                finally {
                    // SQLHotfix 50000518 
                    // make sure we don't leave temporary fields set when leaving this function
                    _pendingTransaction = null;

                    if (mustRelease) { 
                        Monitor.Exit(_physicalStateObj);
                    } 
                } 
            }
        } 

        internal void FailureCleanup(TdsParserStateObject stateObj, Exception e) {
            int old_outputPacketNumber = stateObj._outputPacketNumber;
 
            if (Bid.TraceOn) {
                Bid.Trace(" Exception caught on ExecuteXXX: '%ls' \n", e.ToString()); 
            } 

            if (stateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount if operation failed. 
                stateObj.DecrementOpenResultCount();
            }

            // be sure to wipe out our buffer if we started sending stuff 
            stateObj.ResetBuffer();
            stateObj._outputPacketNumber = 1;  // end of message - reset to 1 - per ramas 
 
            if (old_outputPacketNumber != 1 && _state == TdsParserState.OpenLoggedIn) {
                // If _outputPacketNumber prior to ResetBuffer was not equal to 1, a packet was already 
                // sent to the server and so we need to send an attention and process the attention ack.
                stateObj.SendAttention();
                ProcessAttention(stateObj);
            } 

            Bid.Trace(" Exception rethrown. \n"); 
        } 

        internal void TdsExecuteSQLBatch(string text, int timeout, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj) { 
            if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
                return;
            }
 
            if (stateObj.BcpLock) {
                throw SQL.ConnectionLockedForBcpEvent(); 
            } 

            // SQLBUDT #20010853 - Promote, Commit and Rollback requests for 
            // delegated transactions often happen while there is an open result
            // set, so we need to handle them by using a different MARS session,
            // otherwise we'll write on the physical state objects while someone
            // else is using it.  When we don't have MARS enabled, we need to 
            // lock the physical state object to syncronize it's use at least
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they 
            // won't stomp on anything.
 
            bool mustRelease = false;

            lock (_connHandler) {
                try { 
                    if (_isYukon && !MARSOn) {
                        Monitor.Enter(_physicalStateObj); 
                        mustRelease = true; 
                    }
 
                    // This validation step MUST be done after locking the connection to guarantee we don't
                    //  accidentally execute after the transaction has completed on a different thread.
                    _connHandler.ValidateTransaction();
 
                    stateObj.SetTimeoutSeconds(timeout);
                    stateObj.SniContext = SniContext.Snix_Execute; 
 
                    if (_isYukon) {
                        WriteMarsHeader(stateObj, CurrentTransaction); 
                        if (null != notificationRequest) {
                            WriteQueryNotificationHeader(notificationRequest, stateObj);
                        }
                    } 

                    stateObj._outputMessageType = TdsEnums.MT_SQL; 
 
                    WriteString(text, text.Length, 0, stateObj);
 
                    stateObj.ExecuteFlush();
                    stateObj.SniContext = SniContext.Snix_Read;
                }
                catch (Exception e) { 
                    //Debug.Assert(_state == TdsParserState.Broken, "Caught exception in TdsExecuteSQLBatch but connection was not broken!");
                    // 
                    if (!ADP.IsCatchableExceptionType(e)) { 
                        throw;
                    } 

                    FailureCleanup(stateObj, e);

                    throw; 
                }
                finally { 
                    if (mustRelease) { 
                        Monitor.Exit(_physicalStateObj);
                    } 
                }
            }
        }
 
       internal void TdsExecuteRPC(_SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool isCommandProc) {
            if (TdsParserState.Broken == State || TdsParserState.Closed == State) { 
                return; 
            }
            _SqlRPC rpcext = null; 
            int tempLen;

            // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
            // delegated transactions often happen while there is an open result 
            // set, so we need to handle them by using a different MARS session,
            // otherwise we'll write on the physical state objects while someone 
            // else is using it.  When we don't have MARS enabled, we need to 
            // lock the physical state object to syncronize it's use at least
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they
            // won't stomp on anything.

            bool mustRelease = false; 

            lock (_connHandler) { 
                try { 
                    if (_isYukon && !MARSOn) {
                        Monitor.Enter(_physicalStateObj); 
                        mustRelease = true;
                    }

                    // This validation step MUST be done after locking the connection to guarantee we don't 
                    //  accidentally execute after the transaction has completed on a different thread.
                    _connHandler.ValidateTransaction(); 
 
                    stateObj.SetTimeoutSeconds(timeout);
                    stateObj.SniContext = SniContext.Snix_Execute; 

                    if (_isYukon) {
                        WriteMarsHeader(stateObj, CurrentTransaction);
                        if (null != notificationRequest) { 
                            WriteQueryNotificationHeader(notificationRequest, stateObj);
                        } 
                    } 

                    stateObj._outputMessageType = TdsEnums.MT_RPC; 

                    for (int ii = 0; ii < rpcArray.Length; ii++) {
                        rpcext = rpcArray[ii];
                        if (rpcext.ProcID != 0 && _isShiloh) { 
                            // Perf optimization for Shiloh and later,
                            Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255"); 
                            WriteShort(0xffff, stateObj); 
                            WriteShort((short)(rpcext.ProcID), stateObj);
                        } 
                        else {
                            Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name");
                            tempLen = rpcext.rpcName.Length;
                            WriteShort(tempLen, stateObj); 
                            WriteString(rpcext.rpcName, tempLen, 0, stateObj);
                        } 
 
                        // Options
                        WriteShort((short)rpcext.options, stateObj); 

                        // Stream out parameters
                        SqlParameter[] parameters = rpcext.parameters;
 
                        for (int i = 0; i < parameters.Length; i++) {
                            //                Debug.WriteLine("i:  " + i.ToString(CultureInfo.InvariantCulture)); 
                            // parameters can be unnamed 
                            SqlParameter param = parameters[i];
                            // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters. 
                            if (param == null)
                                break;      // End of parameters for this execute

                            // Validate parameters are not variable length without size and with null value.  MDAC 66522 
#if WINFSFunctionality
                            param.Validate(i, IsWinFS); 
#else 
                            param.Validate(i, isCommandProc);
#endif 

                            // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
                            MetaType mt = param.InternalMetaType;
 
#pragma warning disable 162
#if !UseSmiForTds 
                            if (mt.IsNewKatmaiType) { 
#endif
                                WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj); 
                                continue;
#if !UseSmiForTds
                            }
#endif 

                            if ((!_isShiloh && !mt.Is70Supported) || 
                                (!_isYukon && !mt.Is80Supported) || 
                                (!_isKatmai && !mt.Is90Supported)) {
                                throw ADP.VersionDoesNotSupportDataType(mt.TypeName); 
                            }
                            object value = null;
                            // if we have an output param, set the value to null so we do not send it across to the server
                            if (param.Direction == ParameterDirection.Output) { 
                                bool isCLRType = param.ParamaterIsSqlType;  // We have to forward the TYPE info, we need to know what type we are returning.  Once we null the paramater we will no longer be able to distinguish what type were seeing.
                                param.Value = null; 
                                value = null; 
                                param.ParamaterIsSqlType = isCLRType;
                            } 
                            else {
#if WINFSFunctionality
                             value = param.GetCoercedValue(_isWinFS);
#else 
                                value = param.GetCoercedValue();
#endif 
                            } 
                            // Check for empty or null value before we write out meta info about the actual data (size, scale, etc)
                            bool isSqlVal; 
                            bool isNull = ADP.IsNull(value, out isSqlVal);


                            string parameterName = param.ParameterNameFixed; 
                            WriteParameterName(parameterName, stateObj);
 
                            // Write parameter status 
                            WriteByte(rpcext.paramoptions[i], stateObj);
 
                            //
                            // fixup the types by using the NullableType property of the MetaType class
                            //
                            // following rules should be followed based on feedback from the M-SQL team 
                            // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
                            // 2) always use nullable types (ex: instead of SQLINT use SQLINTN) 
                            // 3) DECIMALN should always be sent as NUMERICN 
                            //
                            WriteByte(mt.NullableType, stateObj); 

                            // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
                            if (mt.TDSType == TdsEnums.SQLVARIANT) {
                                // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant 
                                // param.GetActualSize is not used
#if WINFSFunctionality 
                                WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(_isWinFS), param.Offset, stateObj); 
#else
                                WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj); 
#endif
                                continue;
                            }
 
                            // MaxLen field is only written out for non-fixed length data types
                            // use the greater of the two sizes for maxLen 
                            int actualSize; 
                            int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize();
 
                            //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
                            if (mt.TDSType != TdsEnums.SQLUDT)
                                // getting the actualSize is expensive, cache here and use below
#if WINFSFunctionality 
                                actualSize = param.GetActualSize(_isWinFS);
#else 
                                actualSize = param.GetActualSize(); 
#endif
                            else 
                                actualSize = 0; //get this later

                            int codePageByteSize = 0;
                            int maxsize = 0; 

                            if (mt.IsAnsiType) { 
                                // Avoid the following code block if ANSI but unfilled LazyMat blob 
#if WINFSFunctionality
                                if (!isNull && (!IsWinFS || !param.IsNonFilledLazyMatInstance())) 
#else
                                if (!isNull)
#endif
 { 
                                    string s;
 
                                    if (isSqlVal) { 
                                        if (value is SqlString) {
                                            s = ((SqlString)value).Value; 
                                        }
                                        else {
                                            Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype");
                                            s = new String(((SqlChars)value).Value); 
                                        }
                                    } 
                                    else { 
                                        s = (string)value;
                                    } 

                                    codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding);
                                }
 
                                if (mt.IsPlp) {
                                    WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj); 
                                } 
                                else {
                                    maxsize = (size > codePageByteSize) ? size : codePageByteSize; 
                                    if (maxsize == 0) {
                                        // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                        if (mt.IsNCharType)
                                            maxsize = 2; 
                                        else
                                            maxsize = 1; 
                                    } 

                                    WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj); 
                                }
                            }
                            else {
                                // If type timestamp - treat as fixed type and always send over timestamp length, which is 8. 
                                // For fixed types, we either send null or fixed length for type length.  We want to match that
                                // behavior for timestamps.  However, in the case of null, we still must send 8 because if we 
                                // send null we will not receive a output val.  You can send null for fixed types and still 
                                // receive a output value, but not for variable types.  So, always send 8 for timestamp because
                                // while the user sees it as a fixed type, we are actually representing it as a bigbinary which 
                                // is variable.
                                if (mt.SqlDbType == SqlDbType.Timestamp) {
                                    WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj);
                                } 
                                else if (mt.SqlDbType == SqlDbType.Udt) {
                                    byte[] udtVal = null; 
                                    bool fNull = ADP.IsNull(value); 
                                    Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;
 
                                    Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!");

                                    if (!fNull) {
                                        udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize); 

                                        Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value"); 
                                        size = udtVal.Length; 

                                        //it may be legitimate, but we dont support it yet 
#if WINFSFunctionality
                                        if ((_isWinFS && size < -1) ||
                                           ((size < 0 || size >= UInt16.MaxValue) && !_isWinFS))
#else 
                                        if (size < 0 || (size >= UInt16.MaxValue && maxsize != -1))
#endif 
                                            throw new IndexOutOfRangeException(); 
                                    }
 
                                    //if this is NULL value, write special null value
                                    byte[] lenBytes = BitConverter.GetBytes((Int64)size);

                                    if (ADP.IsEmpty(param.UdtTypeName)) 
                                        throw SQL.MustSetUdtTypeNameForUdtParams();
 
                                    // Split the input name. TypeName is returned as single 3 part name during DeriveParameters. 
                                    // NOTE: ParseUdtTypeName throws if format is incorrect
                                    String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, true /* is UdtTypeName */); 
                                    if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) {
                                        throw ADP.ArgumentOutOfRange("names");
                                    }
                                    if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) { 
                                        throw ADP.ArgumentOutOfRange("names");
                                    } 
                                    if (TdsEnums.MAX_SERVERNAME < names[2].Length) { 
                                        throw ADP.ArgumentOutOfRange("names");
                                    } 

                                    WriteUDTMetaData(value, names[0], names[1], names[2], stateObj);

                                    // 
                                    if (!fNull) {
                                        WriteUnsignedLong((ulong)udtVal.Length, stateObj); // PLP length 
                                        if (udtVal.Length > 0) { // Only write chunk length if its value is greater than 0 
                                            WriteInt(udtVal.Length, stateObj);                  // Chunk length
                                            WriteByteArray(udtVal, udtVal.Length, 0, stateObj); // Value 
                                        }
                                        WriteInt(0, stateObj);                              // Terminator
                                    }
                                    else { 
                                        WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); // PLP Null.
                                    } 
                                    continue; // End of UDT - continue to next parameter. 
                                    //
                                } 
                                else if (mt.IsPlp) {
                                    if (mt.SqlDbType != SqlDbType.Xml)
                                        WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                                } 
                                else if (mt.IsVarTime || mt.SqlDbType == SqlDbType.Date) {
                                    // This block intentionally blank.  It exists to avoid the size being written out in 
                                    //  the default else block below. 
                                }
                                else { 
                                    maxsize = (size > actualSize) ? size : actualSize;
                                    if (maxsize == 0 && IsYukonOrNewer) {
                                        // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                        if (mt.IsNCharType) 
                                            maxsize = 2;
                                        else 
                                            maxsize = 1; 
                                    }
 
                                    WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
                                }
                            }
 
                            // scale and precision are only relevant for numeric and decimal types
                            if (mt.SqlDbType == SqlDbType.Decimal) { 
                                byte precision = param.GetActualPrecision(); 
                                byte scale = param.GetActualScale();
 
                                // bug 49512, make sure the value matches the scale the user enters
                                if (!isNull) {
                                    if (isSqlVal) {
                                        value = AdjustSqlDecimalScale((SqlDecimal)value, scale); 

                                        // If Precision is specified, verify value precision vs param precision 
                                        if (precision != 0) { 
                                            if (precision < ((SqlDecimal)value).Precision) {
                                                throw ADP.ParameterValueOutOfRange((SqlDecimal)value); 
                                            }
                                        }
                                    }
                                    else { 
                                        value = AdjustDecimalScale((Decimal)value, scale);
 
                                        SqlDecimal sqlValue = new SqlDecimal((Decimal)value); 

                                        // If Precision is specified, verify value precision vs param precision 
                                        if (precision != 0) {
                                            if (precision < sqlValue.Precision) {
                                                throw ADP.ParameterValueOutOfRange((Decimal)value);
                                            } 
                                        }
                                    } 
                                } 

                                if (0 == precision) { 
                                    if (_isShiloh)
                                        WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION, stateObj);
                                    else
                                        WriteByte(TdsEnums.SPHINX_DEFAULT_NUMERIC_PRECISION, stateObj); 
                                }
                                else 
                                    WriteByte(precision, stateObj); 

                                WriteByte(scale, stateObj); 
                            }
                            else if (mt.IsVarTime) {
                                WriteByte(param.GetActualScale(), stateObj);
                            } 

                            // write out collation or xml metadata 
 
                            if (_isYukon && (mt.SqlDbType == SqlDbType.Xml)) {
                                if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) || 
                                    ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) ||
                                    ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))) {
                                    WriteByte(1, stateObj);   //Schema present flag
 
                                    if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) {
                                        tempLen = (param.XmlSchemaCollectionDatabase).Length; 
                                        WriteByte((byte)(tempLen), stateObj); 
                                        WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
                                    } 
                                    else {
                                        WriteByte(0, stateObj);       // No dbname
                                    }
 
                                    if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) {
                                        tempLen = (param.XmlSchemaCollectionOwningSchema).Length; 
                                        WriteByte((byte)(tempLen), stateObj); 
                                        WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
                                    } 
                                    else {
                                        WriteByte(0, stateObj);      // no xml schema name
                                    }
                                    if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)) { 
                                        tempLen = (param.XmlSchemaCollectionName).Length;
                                        WriteShort((short)(tempLen), stateObj); 
                                        WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj); 
                                    }
                                    else { 
                                        WriteShort(0, stateObj);       // No xml schema collection name
                                    }

                                } 
                                else {
                                    WriteByte(0, stateObj);       // No schema 
                                } 
                            }
                            else if (_isShiloh && mt.IsCharType) { 
                                // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
                                SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation;
                                Debug.Assert(_defaultCollation != null, "_defaultCollation is null!");
 
                                WriteUnsignedInt(outCollation.info, stateObj);
                                WriteByte(outCollation.sortId, stateObj); 
                            } 

                            if (0 == codePageByteSize) 
                                WriteParameterVarLen(mt, actualSize, isNull, stateObj);
                            else
                                WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj);
 
                            // write the value now
                            if (!isNull) { 
                                if (isSqlVal) { 
                                    WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj);
                                } 
                                else {
                                    // for codePageEncoded types, WriteValue simply expects the number of characters
                                    // For plp types, we also need the encoded byte size
                                    WriteValue(value, mt, param.GetActualScale(), actualSize, codePageByteSize, param.Offset, stateObj); 
                                }
                            } 
                        } // parameter for loop 
#pragma warning restore 162
 
                        // If this is not the last RPC we are sending, add the batch flag
                        if (ii < (rpcArray.Length - 1)) {
                            if (_isYukon) {
                                WriteByte(TdsEnums.YUKON_RPCBATCHFLAG, stateObj); 

                            } 
                            else { 
                                WriteByte(TdsEnums.SHILOH_RPCBATCHFLAG, stateObj);
                            } 
                        }
                    } // rpc for loop

                    stateObj.ExecuteFlush(); 
                    stateObj.SniContext = SniContext.Snix_Read;
                } 
                catch (Exception e) { 
                    //
                    if (!ADP.IsCatchableExceptionType(e)) { 
                        throw;
                    }

                    FailureCleanup(stateObj, e); 

                    throw; 
                } 
                finally {
                    if (mustRelease) { 
                        Monitor.Exit(_physicalStateObj);
                    }
                }
            } 
        }
 
        private void WriteParameterName(string parameterName, TdsParserStateObject stateObj) { 
            // paramLen
            // paramName 
            if (!ADP.IsEmpty(parameterName)) {
                Debug.Assert( parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
                int tempLen = parameterName.Length & 0xff;
                WriteByte((byte)tempLen, stateObj); 
                WriteString(parameterName, tempLen, 0, stateObj);
            } 
            else { 
                WriteByte(0, stateObj);
            } 
        }

        private static readonly IEnumerable __tvpEmptyValue = new List().AsReadOnly();
        private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj) { 
            //
            // Determine Metadata 
            // 
            ParameterPeekAheadValue peekAhead;
            MSS.SmiParameterMetaData metaData = param.MetaDataForSmi(out peekAhead); 

            if (!_isKatmai) {
                MetaType mt = MetaType.GetMetaTypeFromSqlDbType(metaData.SqlDbType, metaData.IsMultiValued);
                throw ADP.VersionDoesNotSupportDataType(mt.TypeName); 
            }
 
            // 
            //  Determine value to send
            // 
            object value;
            MSS.ExtendedClrTypeCode typeCode;

            // if we have an output or default param, set the value to null so we do not send it across to the server 
            if (sendDefault) {
                // Value for TVP default is empty list, not NULL 
                if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued) { 
                    value = __tvpEmptyValue;
                    typeCode = MSS.ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; 
                }
                else {
                    // Need to send null value for default
                    value = null; 
                    typeCode = MSS.ExtendedClrTypeCode.DBNull;
                } 
            } 
            else if (param.Direction == ParameterDirection.Output) {
                bool isCLRType = param.ParamaterIsSqlType;  // We have to forward the TYPE info, we need to know what type we are returning.  Once we null the paramater we will no longer be able to distinguish what type were seeing. 
                param.Value = null;
                value = null;
                typeCode = MSS.ExtendedClrTypeCode.DBNull;
                param.ParamaterIsSqlType = isCLRType; 
            }
            else { 
#if WINFSFunctionality 
                value = param.GetCoercedValue(_isWinFS);
#else 
                value = param.GetCoercedValue();
#endif
                typeCode = MSS.MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(
                                                    metaData.SqlDbType, metaData.IsMultiValued, value, null, MSS.SmiContextFactory.KatmaiVersion); 
            }
 
            if (Bid.AdvancedOn) { 
                Bid.Trace(" %d#, Sending parameter '%ls', default flag=%d, metadata:\n", ObjectID, paramIndex, sendDefault?1:0);
                Bid.PutStr(metaData.TraceString(3)); 
                Bid.Trace("\n");
            }

            // 
            // Write parameter metadata
            // 
            WriteSmiParameterMetaData(metaData, sendDefault, stateObj); 

            // 
            // Now write the value
            //
            TdsParameterSetter paramSetter = new TdsParameterSetter(stateObj, metaData);
            MSS.ValueUtilsSmi.SetCompatibleValueV200( 
                                        new MSS.SmiEventSink_Default(),  // TDS Errors/events dealt with at lower level for now, just need an object for processing
                                        paramSetter, 
                                        0,          // ordinal.  TdsParameterSetter only handles one parameter at a time 
                                        metaData,
                                        value, 
                                        typeCode,
                                        param.Offset,
                                        0 < param.Size ? param.Size : -1,
                                        peekAhead); 
        }
 
        // Writes metadata portion of parameter stream from an SmiParameterMetaData object. 
        private void WriteSmiParameterMetaData(MSS.SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj) {
            // Determine status 
            byte status = 0;
            if (ParameterDirection.Output == metaData.Direction || ParameterDirection.InputOutput == metaData.Direction) {
                status |= TdsEnums.RPC_PARAM_BYREF;
            } 

            if (sendDefault) { 
                status |= TdsEnums.RPC_PARAM_DEFAULT; 
            }
 
            // Write everything out
            WriteParameterName(metaData.Name, stateObj);
            WriteByte(status, stateObj);
            WriteSmiTypeInfo(metaData, stateObj); 
        }
 
        // Write a TypeInfo stream 
        // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR,  SQLNVARCHAR, and SQLBIGVARBINARY
        private void WriteSmiTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { 
            switch(metaData.SqlDbType) {
                case SqlDbType.BigInt:
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Binary: 
                    WriteByte(TdsEnums.SQLBIGBINARY, stateObj); 
                    WriteUnsignedShort(checked((ushort)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.Bit:
                    WriteByte(TdsEnums.SQLBITN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.Char:
                    WriteByte(TdsEnums.SQLBIGCHAR, stateObj); 
                    WriteUnsignedShort(checked((ushort)(metaData.MaxLength)), stateObj); 
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj); 
                    break;
                case SqlDbType.DateTime:
                    WriteByte(TdsEnums.SQLDATETIMN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Decimal: 
                    WriteByte(TdsEnums.SQLNUMERICN, stateObj); 
                    WriteByte(checked((byte)MetaType.MetaDecimal.FixedLength), stateObj);   // SmiMetaData's length and actual wire format's length are different
                    WriteByte(0 == metaData.Precision ? (byte)1 : metaData.Precision, stateObj); 
                    WriteByte(metaData.Scale, stateObj);
                    break;
                case SqlDbType.Float:
                    WriteByte(TdsEnums.SQLFLTN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.Image: 
                    WriteByte(TdsEnums.SQLBIGVARBINARY, stateObj);
                    WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); 
                    break;
                case SqlDbType.Int:
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Money: 
                    WriteByte(TdsEnums.SQLMONEYN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.NChar:
                    WriteByte(TdsEnums.SQLNCHAR, stateObj);
                    WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
                    WriteUnsignedInt(_defaultCollation.info, stateObj); // 
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break; 
                case SqlDbType.NText: 
                    WriteByte(TdsEnums.SQLNVARCHAR, stateObj);
                    WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); 
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break;
                case SqlDbType.NVarChar: 
                    WriteByte(TdsEnums.SQLNVARCHAR, stateObj);
                    if (MSS.SmiMetaData.UnlimitedMaxLengthIndicator == metaData.MaxLength) { 
                        WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj); 
                    }
                    else { 
                        WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
                    }
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj); 
                    break;
                case SqlDbType.Real: 
                    WriteByte(TdsEnums.SQLFLTN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.UniqueIdentifier:
                    WriteByte(TdsEnums.SQLUNIQUEID, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break; 
                case SqlDbType.SmallDateTime:
                    WriteByte(TdsEnums.SQLDATETIMN, stateObj); 
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.SmallInt: 
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break;
                case SqlDbType.SmallMoney: 
                    WriteByte(TdsEnums.SQLMONEYN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj); 
                    break; 
                case SqlDbType.Text:
                    WriteByte(TdsEnums.SQLBIGVARCHAR, stateObj); 
                    WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break; 
                case SqlDbType.Timestamp:
                    WriteByte(TdsEnums.SQLBIGBINARY, stateObj); 
                    WriteShort(checked((int)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.TinyInt: 
                    WriteByte(TdsEnums.SQLINTN, stateObj);
                    WriteByte(checked((byte)metaData.MaxLength), stateObj);
                    break;
                case SqlDbType.VarBinary: 
                    WriteByte(TdsEnums.SQLBIGVARBINARY, stateObj);
                    WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj); 
                    break; 
                case SqlDbType.VarChar:
                    WriteByte(TdsEnums.SQLBIGVARCHAR, stateObj); 
                    WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj);
                    WriteUnsignedInt(_defaultCollation.info, stateObj); //
                    WriteByte(_defaultCollation.sortId, stateObj);
                    break; 
                case SqlDbType.Variant:
                    WriteByte(TdsEnums.SQLVARIANT, stateObj); 
                    WriteInt(checked((int)metaData.MaxLength), stateObj); 
                    break;
                case SqlDbType.Xml: 
                    WriteByte(TdsEnums.SQLXMLTYPE, stateObj);
                    // Is there a schema
                    if (ADP.IsEmpty(metaData.TypeSpecificNamePart1) && ADP.IsEmpty(metaData.TypeSpecificNamePart2) &&
                            ADP.IsEmpty(metaData.TypeSpecificNamePart3)) { 
                        WriteByte(0, stateObj);  // schema not present
                    } 
                    else { 
                        WriteByte(1, stateObj); // schema present
                        WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); 
                        WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
                        WriteIdentifierWithShortLength(metaData.TypeSpecificNamePart3, stateObj);
                    }
                    break; 
                case SqlDbType.Udt:
                    WriteByte(TdsEnums.SQLUDT, stateObj); 
                    WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj); 
                    WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
                    WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj); 
                    break;
                case SqlDbType.Structured:
                    if (metaData.IsMultiValued) {
                        WriteTvpTypeInfo(metaData, stateObj); 
                    }
                    else { 
                        Debug.Assert(false, "SUDTs not yet supported."); 
                    }
                    break; 
                case SqlDbType.Date:
                    WriteByte(TdsEnums.SQLDATE, stateObj);
                    break;
                case SqlDbType.Time: 
                    WriteByte(TdsEnums.SQLTIME, stateObj);
                    WriteByte(metaData.Scale, stateObj); 
                    break; 
                case SqlDbType.DateTime2:
                    WriteByte(TdsEnums.SQLDATETIME2, stateObj); 
                    WriteByte(metaData.Scale, stateObj);
                    break;
                case SqlDbType.DateTimeOffset:
                    WriteByte(TdsEnums.SQLDATETIMEOFFSET, stateObj); 
                    WriteByte(metaData.Scale, stateObj);
                    break; 
                default: 
                    Debug.Assert(false, "Unknown SqlDbType should have been caught earlier!");
                    break; 
            }
        }

        private void WriteTvpTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) { 
            Debug.Assert(SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued,
                        "Invalid metadata for TVPs. Type=" + metaData.SqlDbType); 
            // Type token 
            WriteByte((byte) TdsEnums.SQLTABLE, stateObj);
 
            // 3-part name (DB, Schema, TypeName)
            WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
            WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
            WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj); 

            // TVP_COLMETADATA 
            if (0 == metaData.FieldMetaData.Count) { 
                WriteUnsignedShort((ushort)TdsEnums.TVP_NOMETADATA_TOKEN, stateObj);
            } 
            else {
                // COUNT of columns
                WriteUnsignedShort(checked((ushort) metaData.FieldMetaData.Count), stateObj);
 
                // TvpColumnMetaData for each column (look for defaults in this loop
                MSS.SmiDefaultFieldsProperty defaults = (MSS.SmiDefaultFieldsProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.DefaultFields]; 
                for(int i=0; i's per-struct-instantiated memory costs. 
        private class TdsOrderUnique {
            internal short ColumnOrdinal;
            internal byte  Flags;
 
            internal TdsOrderUnique(short ordinal, byte flags) {
                ColumnOrdinal = ordinal; 
                Flags = flags; 
            }
        } 

        private void WriteTvpOrderUnique(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
            // TVP_ORDER_UNIQUE token (uniqueness and sort order)
 
            // Merge order and unique keys into a single token stream
 
            MSS.SmiOrderProperty orderProperty = (MSS.SmiOrderProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.SortOrder]; 
            MSS.SmiUniqueKeyProperty uniqueKeyProperty = (MSS.SmiUniqueKeyProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.UniqueKey];
 
            // Build list from
            List columnList = new List(metaData.FieldMetaData.Count);
            for(int i=0; i timeout) {
                    throw ADP.ArgumentOutOfRange("Timeout");
                } 

                Bid.NotificationsTrace(" NotificationRequest: userData: '%ls', options: '%ls', timeout: '%d'\n", notificationRequest.UserData, notificationRequest.Options, notificationRequest.Timeout); 
 
                // Total Length (uint) (included in MARS header written previously)
 
                // Header Length (uint) (included in size) (already written to output buffer)
                // Header Type (ushort)
                // NotifyID Length (ushort)
                // NotifyID UnicodeStream (unicode text) 
                // SSBDeployment Length (ushort)
                // SSBDeployment UnicodeStream (unicode text) 
                // Timeout (uint) -- optional 
                // WEBDATA 102263: Don't send timeout value if it is 0
 
                // This is equal to sum of comments above.
                int headerLength = 4 + 2 + 2 + (callbackId.Length * 2) + 2 + (service.Length * 2);

                if (timeout > 0) 
                    headerLength += 4;
 
                // Total length = sum of QueryNotification header plus sum of previous headers (MARS header). 
                int totalHeaderLength = headerLength + stateObj._outBytesUsed - TdsEnums.HEADER_LEN;
 
                // override previous headers (MARS) length field
                int save_outBytesUsed = stateObj._outBytesUsed;

                stateObj._outBytesUsed = TdsEnums.HEADER_LEN; 
                WriteInt(totalHeaderLength, stateObj);
                stateObj._outBytesUsed = save_outBytesUsed; 
                WriteInt(headerLength, stateObj); 

                WriteShort(1, stateObj);      // Query notifications Type 

                WriteShort(callbackId.Length * 2, stateObj); // Length in bytes
                WriteString(callbackId, stateObj);
 
                WriteShort(service.Length * 2, stateObj); // Length in bytes
                WriteString(service, stateObj); 
                if (timeout > 0) 
                    WriteInt(timeout, stateObj);
            } 
        }

#if STREAMINGPARAMETERS
        // 
        // Writes the length of the data before the data itself
        //      Handles partially length prefixed types correctly 
        // 
        private void WriteDataLength(SqlMetaDataPriv colmeta, ulong longlen, TdsParserStateObject stateObj) {
            // Handle Yukon specific tokens 
            if ((_isYukon) && (colmeta != null && colmeta.metaType.IsPlp)) {
                Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
                             colmeta.tdsType == TdsEnums.SQLBIGVARCHAR ||
                             colmeta.tdsType == TdsEnums.SQLBIGVARBINARY || 
                             colmeta.tdsType == TdsEnums.SQLNVARCHAR,
                             "GetDataLength:Invalid streaming datatype"); 
 
                WriteUnsignedLong(longlen, stateObj);
            } 
            else {
                Debug.Assert(longlen <= 0xffffffffL, "Bad length for data");
                WriteTokenLength(colmeta.tdsType, (int)longlen, stateObj);
            } 

            return; 
        } 
#endif //STREAMINGPARAMETERS
 
        //
        // Reverse function of GetTokenLength
        //
        private void WriteTokenLength(byte token, int length, TdsParserStateObject stateObj) { 
            int tokenLength = 0;
 
            Debug.Assert(token != 0, "0 length token!"); 

            // For Plp fields, this should only be used when writing to metadata header. 
            // For actual data length, WriteDataLength should be used.
            // For Xml fields, there is no token length field. For MAX fields it is 0xffff.
            if (_isYukon) {     // Handle Yukon specific exceptions
                if (TdsEnums.SQLUDT == token) { 
                    tokenLength = 8;
                } 
                else if (token == TdsEnums.SQLXMLTYPE) { 
                    tokenLength = 8;
                } 
            }

            if (tokenLength == 0) {
                switch (token & TdsEnums.SQLLenMask) { 
                    case TdsEnums.SQLFixedLen:
                        Debug.Assert(length == 0x01 << ((token & 0x0c) >> 2), "length does not match encoded length in token"); 
                        tokenLength = 0; 
                        break;
 
                    case TdsEnums.SQLZeroLen:
                        tokenLength = 0;
                        break;
 
                    case TdsEnums.SQLVarLen:
                    case TdsEnums.SQLVarCnt: 
                        if (0 != (token & 0x80)) 
                            tokenLength = 2;
                        else if (0 == (token & 0x0c)) 

                            //
                            tokenLength = 4;
                        else 
                            tokenLength = 1;
 
                        break; 

                    default: 
                        Debug.Assert(false, "Unknown token length!");
                        break;
                }
 
                switch (tokenLength) {
                    case 1: 
                        WriteByte((byte)length, stateObj); 
                        break;
 
                    case 2:
                        WriteShort(length, stateObj);
                        break;
 
                    case 4:
                        WriteInt(length, stateObj); 
                        break; 

                    case 8: 
                        // In the metadata case we write 0xffff for partial length prefixed types.
                        //  For actual data length preceding data, WriteDataLength should be used.
                        WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                        break; 
                } // end switch
            } 
        } 

        // Returns true if BOM byte mark is needed for an XML value 
        private bool IsBOMNeeded(MetaType type, object value) {
            if (type.NullableType == TdsEnums.SQLXMLTYPE) {
                Type currentType = value.GetType();
 
                if(currentType == typeof(SqlString)) {
                    if (!((SqlString)value).IsNull && ((((SqlString)value).Value).Length > 0)) { 
                        if ((((SqlString)value).Value[0] & 0xff) != 0xff) 
                            return true;
                    } 
                }
                else if ((currentType == typeof(String)) && (((String)value).Length > 0)) {
                    if ((value != null) && (((String)value)[0] & 0xff) != 0xff)
                        return true; 
                }
                else if (currentType == typeof(SqlXml)) { 
                    if (!((SqlXml)value).IsNull) 
                        return true;
                } 
                else if (currentType ==typeof(XmlReader)) {
                    return true;             // Values will eventually converted to unicode string here
                }
            } 
            return false;
        } 
 
        // For MAX types, this method can only write everything in one big chunk. If multiple
        // chunk writes needed, please use WritePlpBytes/WritePlpChars 
        private void WriteSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) {
            Debug.Assert(((type.NullableType == TdsEnums.SQLXMLTYPE) ||
                   (value is INullable && !((INullable)value).IsNull)),
                   "unexpected null SqlType!"); 

            // parameters are always sent over as BIG or N types 
            switch (type.NullableType) { 
                case TdsEnums.SQLFLTN:
                    if (type.FixedLength == 4) 
                        WriteFloat(((SqlSingle)value).Value, stateObj);
                    else {
                        Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
                        WriteDouble(((SqlDouble)value).Value, stateObj); 
                    }
 
                    break; 

                case TdsEnums.SQLBIGBINARY: 
                case TdsEnums.SQLBIGVARBINARY:
                case TdsEnums.SQLIMAGE:
                    {
                        if (type.IsPlp) { 
                            WriteInt(actualLength, stateObj);               // chunk length
                        } 
 
                        if (value is SqlBinary) {
                            WriteByteArray(((SqlBinary)value).Value, actualLength, offset, stateObj); 
                        }
                        else {
                            Debug.Assert(value is SqlBytes);
                            WriteByteArray(((SqlBytes)value).Value, actualLength, offset, stateObj); 
                        }
                        break; 
                    } 

                case TdsEnums.SQLUNIQUEID: 
                    {
                        byte[] b = ((SqlGuid)value).ToByteArray();

                        Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object"); 
                        WriteByteArray(b, actualLength, 0, stateObj);
                        break; 
                    } 

                case TdsEnums.SQLBITN: 
                    {
                        Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
                        if (((SqlBoolean)value).Value == true)
                            WriteByte(1, stateObj); 
                        else
                            WriteByte(0, stateObj); 
 
                        break;
                    } 

                case TdsEnums.SQLINTN:
                    if (type.FixedLength == 1)
                        WriteByte(((SqlByte)value).Value, stateObj); 
                    else
                    if (type.FixedLength == 2) 
                        WriteShort(((SqlInt16)value).Value, stateObj); 
                    else
                        if (type.FixedLength == 4) 
                        WriteInt(((SqlInt32)value).Value, stateObj);
                    else {
                        Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
                        WriteLong(((SqlInt64)value).Value, stateObj); 
                    }
 
                    break; 

                case TdsEnums.SQLBIGCHAR: 
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT:
                    if (type.IsPlp) {
                        WriteInt(codePageByteSize, stateObj);               // chunk length 
                    }
                    if (value is SqlChars) { 
                        String sch = new String(((SqlChars)value).Value); 

                        WriteEncodingChar(sch, actualLength, offset, _defaultEncoding, stateObj); 
                    }
                    else {
                        Debug.Assert(value is SqlString);
                        WriteEncodingChar(((SqlString)value).Value, actualLength, offset, _defaultEncoding, stateObj); 
                    }
 
                    break; 

                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR:
                case TdsEnums.SQLNTEXT:
                case TdsEnums.SQLXMLTYPE:
 
                    if (type.IsPlp) {
                        if(IsBOMNeeded(type, value)) { 
                            WriteInt(actualLength+2, stateObj);               // chunk length 
                            WriteShort(TdsEnums.XMLUNICODEBOM , stateObj);
                        } else { 
                            WriteInt(actualLength, stateObj);               // chunk length
                        }
                    }
 
                    // convert to cchars instead of cbytes
                    // Xml type is already converted to string through GetCoercedValue 
                    if (actualLength != 0) 
                        actualLength >>= 1;
 
                    if (value is SqlChars) {
                        WriteCharArray(((SqlChars)value).Value, actualLength, offset, stateObj);
                    }
                    else { 
                        Debug.Assert(value is SqlString);
                        WriteString(((SqlString)value).Value, actualLength, offset, stateObj); 
                    } 
                    break;
 
                case TdsEnums.SQLNUMERICN:
                    Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
                    WriteSqlDecimal((SqlDecimal)value, stateObj);
                    break; 

                case TdsEnums.SQLDATETIMN: 
                    SqlDateTime dt = (SqlDateTime)value; 

                    if (type.FixedLength == 4) { 
                        if (0 > dt.DayTicks || dt.DayTicks > UInt16.MaxValue)
                            throw SQL.SmallDateTimeOverflow(dt.ToString());

                        WriteShort(dt.DayTicks, stateObj); 
                        WriteShort(dt.TimeTicks / SqlDateTime.SQLTicksPerMinute, stateObj);
                    } 
                    else { 
                        WriteInt(dt.DayTicks, stateObj);
                        WriteInt(dt.TimeTicks, stateObj); 
                    }

                    break;
 
                case TdsEnums.SQLMONEYN:
                    { 
                        WriteSqlMoney((SqlMoney)value, type.FixedLength, stateObj); 
                        break;
                    } 

                case TdsEnums.SQLUDT:
                    Debug.Assert(false, "Called WriteSqlValue on UDT param.Should have already been handled");
                    throw SQL.UDTUnexpectedResult(value.GetType().AssemblyQualifiedName); 

                default: 
                    Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); 
                    break;
            } // switch 

            if (type.IsPlp && (actualLength > 0)) {
                WriteInt(0, stateObj);               // chunk terminator
            } 
            // Debug.WriteLine("value:  " + value.ToString(CultureInfo.InvariantCulture));
        } 
 
        // For MAX types, this method can only write everything in one big chunk. If multiple
        // chunk writes needed, please use WritePlpBytes/WritePlpChars 
        private void WriteValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj) {
            Debug.Assert((null != value) && (DBNull.Value != value), "unexpected missing or empty object");

            // parameters are always sent over as BIG or N types 
            switch (type.NullableType) {
                case TdsEnums.SQLFLTN: 
                    if (type.FixedLength == 4) 
                        WriteFloat((Single)value, stateObj);
                    else { 
                        Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
                        WriteDouble((Double)value, stateObj);
                    }
 
                    break;
 
                case TdsEnums.SQLBIGBINARY: 
                case TdsEnums.SQLBIGVARBINARY:
                case TdsEnums.SQLIMAGE: 
                case TdsEnums.SQLUDT: {
                        // An array should be in the object
                        Debug.Assert(value.GetType() == typeof(byte[]), "Value should be an array of bytes");
 
                        Byte[] b = (byte[])value;
 
                        if (type.IsPlp) { 
                            WriteInt(actualLength, stateObj);               // chunk length
                        } 
                        WriteByteArray(b, actualLength, offset, stateObj);
                        break;
                    }
 
                case TdsEnums.SQLUNIQUEID: {
                        System.Guid guid = (System.Guid)value; 
                        byte[] b = guid.ToByteArray(); 

                        Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object"); 
                        WriteByteArray(b, actualLength, 0, stateObj);
                        break;
                    }
 
                case TdsEnums.SQLBITN: {
                        Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type"); 
                        if ((bool)value == true) 
                            WriteByte(1, stateObj);
                        else 
                            WriteByte(0, stateObj);

                        break;
                    } 

                case TdsEnums.SQLINTN: 
                    if (type.FixedLength == 1) 
                        WriteByte((byte)value, stateObj);
                    else if (type.FixedLength == 2) 
                        WriteShort((Int16)value, stateObj);
                    else if (type.FixedLength == 4)
                        WriteInt((Int32)value, stateObj);
                    else { 
                        Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
                        WriteLong((Int64)value, stateObj); 
                    } 

                    break; 

                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT: 
                    if (type.IsPlp) {
                        WriteInt(encodingByteSize, stateObj);               // chunk length 
                    } 
                    if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
                        WriteByteArray((byte[]) value, actualLength, 0, stateObj); 
                    }
                    else {
                        WriteEncodingChar((string)value, actualLength, offset, _defaultEncoding, stateObj);
                    } 
                    break;
 
                case TdsEnums.SQLNCHAR: 
                case TdsEnums.SQLNVARCHAR:
                case TdsEnums.SQLNTEXT: 
                case TdsEnums.SQLXMLTYPE:
                    if (type.IsPlp) {
                        if(IsBOMNeeded(type, value)) {
                            WriteInt(actualLength+2, stateObj);               // chunk length 
                            WriteShort(TdsEnums.XMLUNICODEBOM , stateObj);
                        } else { 
                            WriteInt(actualLength, stateObj);               // chunk length 
                        }
                    } 
                    if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
                        WriteByteArray((byte[]) value, actualLength, 0, stateObj);
                    }
                    else { 
                        // convert to cchars instead of cbytes
                        actualLength >>= 1; 
                        WriteString((string)value, actualLength, offset, stateObj); 
                    }
                    break; 

                case TdsEnums.SQLNUMERICN:
                    Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
                    WriteDecimal((Decimal)value, stateObj); 
                    break;
 
                case TdsEnums.SQLDATETIMN: 
                    Debug.Assert(type.FixedLength <= 0xff, "Invalid Fixed Length");
 
                    TdsDateTime dt = MetaType.FromDateTime((DateTime)value, (byte)type.FixedLength);

                    if (type.FixedLength == 4) {
                        if (0 > dt.days || dt.days > UInt16.MaxValue) 
                            throw SQL.SmallDateTimeOverflow(MetaType.ToDateTime(dt.days, dt.time, 4).ToString(CultureInfo.InvariantCulture));
 
                        WriteShort(dt.days, stateObj); 
                        WriteShort(dt.time, stateObj);
                    } 
                    else {
                        WriteInt(dt.days, stateObj);
                        WriteInt(dt.time, stateObj);
                    } 

                    break; 
 
                case TdsEnums.SQLMONEYN: {
                        WriteCurrency((Decimal)value, type.FixedLength, stateObj); 
                        break;
                    }

                case TdsEnums.SQLDATE: { 
                        WriteDate((DateTime)value, stateObj);
                        break; 
                    } 

                case TdsEnums.SQLTIME: 
                        if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
                            throw SQL.TimeScaleValueOutOfRange(scale);
                        }
                        WriteTime((TimeSpan)value, scale, actualLength, stateObj); 
                        break;
 
                case TdsEnums.SQLDATETIME2: 
                        if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
                            throw SQL.TimeScaleValueOutOfRange(scale); 
                        }
                        WriteDateTime2((DateTime)value, scale, actualLength, stateObj);
                        break;
 
                case TdsEnums.SQLDATETIMEOFFSET:
                        WriteDateTimeOffset((DateTimeOffset)value, scale, actualLength, stateObj); 
                        break; 

                default: 
                    Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null));
                    break;
            } // switch
            if (type.IsPlp && (actualLength > 0)) { 
                WriteInt(0, stateObj);               // chunk terminator
            } 
 
            // Debug.WriteLine("value:  " + value.ToString(CultureInfo.InvariantCulture));
        } 

        //
        // we always send over nullable types for parameters so we always write the varlen fields
        // 

        internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsParserStateObject stateObj) { 
            if (type.IsLong) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths 
                if (isNull) {
                    if (type.IsPlp) { 
                        WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
                    }
                    else {
                        WriteInt(unchecked((int)TdsEnums.VARLONGNULL), stateObj); 
                    }
                } 
                else if (type.NullableType == TdsEnums.SQLXMLTYPE) { 
                    WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
                } 
                else if (type.IsPlp) {
                    // Non-xml plp types
                    WriteLong((long)size, stateObj);
                } 
                else {
                    WriteInt(size, stateObj); 
                } 
            }
            else if (type.IsVarTime) { 
                if (isNull) {
                    WriteByte(TdsEnums.FIXEDNULL, stateObj);
                }
                else { 
                    WriteByte((byte)size, stateObj);
                } 
            } 
            else if (false == type.IsFixed) { // non-long but variable length column, must be a BIG* type: 2 byte length
                if (isNull) { 
                    WriteShort(TdsEnums.VARNULL, stateObj);
                }
                else {
                    WriteShort(size, stateObj); 
                }
            } 
            else { 
                if (isNull) {
                    WriteByte(TdsEnums.FIXEDNULL, stateObj); 
                }
                else {
                    Debug.Assert(type.FixedLength <= 0xff, "WriteParameterVarLen: invalid one byte length!");
                    WriteByte((byte)(type.FixedLength & 0xff), stateObj); // 1 byte for everything else 
                }
            } 
        } 

        // Reads the next chunk in a nvarchar(max) data stream. 
        // This call must be preceeded by a call to ReadPlpLength or ReadDataLength.
        // Will not start reading into the next chunk if bytes requested is larger than
        // the current chunk length. Do another ReadPlpLength, ReadPlpUnicodeChars in that case.
        // Returns the actual chars read 
        private int ReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsParserStateObject stateObj) {
            int charsRead; 
 
            Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
            Debug.Assert((stateObj._longlen != 0) && (stateObj._longlen != TdsEnums.SQL_PLP_NULL), 
                        "Out of [....] plp read request");
            if (stateObj._longlenleft == 0) {
                Debug.Assert(false, "Out of [....] read request");
                return 0; 
            }
 
            charsRead = len; 

            // stateObj._longlenleft is in bytes 
            if ((stateObj._longlenleft >> 1) < (ulong)len)
                charsRead = (int)(stateObj._longlenleft >> 1);

            for (int ii = 0; ii < charsRead; ii++) 
                buff[offst + ii] = stateObj.ReadChar();
 
            stateObj._longlenleft -= ((ulong)charsRead << 1); 
            return charsRead;
        } 

        // Reads the requested number of chars from a plp data stream, or the entire data if
        // requested length is -1 or larger than the actual length of data. First call to this method
        //  should be preceeded by a call to ReadPlpLength or ReadDataLength. 
        // Returns the actual chars read.
        internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj) { 
            int charsRead = 0; 
            int charsLeft = 0;
            int totalcharsRead = 0; 
            char[] newbuf;

            if (stateObj._longlen == 0) {
                Debug.Assert(stateObj._longlenleft == 0); 
                return 0;       // No data
            } 
 
            Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
                    "Out of [....] plp read request"); 

            Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
            charsLeft = len;
 
            // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time
            if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) { 
                buff = new char[(int)Math.Min((int)stateObj._longlen, len)]; 
            }
 
            if (stateObj._longlenleft == 0) {
                stateObj.ReadPlpLength(false);
                if (stateObj._longlenleft == 0)  // Data read complete
                    return 0; 
            }
 
            while (charsLeft > 0) { 
                charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
                if ((buff == null) || (buff.Length < (offst + charsRead))) { 
                    // Grow the array
                    newbuf = new char[offst + charsRead];
                    if (buff != null) {
                        Buffer.BlockCopy(buff, 0, newbuf, 0, offst*2); 
                    }
                    buff = newbuf; 
                } 
                if (charsRead > 0) {
                    charsRead = ReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj); 
                    charsLeft -= charsRead;
                    offst += charsRead;
                    totalcharsRead += charsRead;
                } 
                // Special case single byte left
                if (stateObj._longlenleft == 1 && (charsLeft > 0)) { 
                    byte  b1 = stateObj.ReadByte(); 
                    stateObj._longlenleft--;
                    stateObj.ReadPlpLength(false); 
                    Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
                    byte b2 = stateObj.ReadByte();
                    stateObj._longlenleft--;
                    // Put it at the end of the array. At this point we know we have an extra byte. 
                    buff[offst] = (char)(((b2 & 0xff) << 8) + (b1 & 0xff));
                    offst = checked((int)offst + 1); 
                    charsRead++; 
                    charsLeft--;
                    totalcharsRead++; 
                }
                if (stateObj._longlenleft == 0)  // Read the next chunk or cleanup state if hit the end
                    stateObj.ReadPlpLength(false);
 
                if (stateObj._longlenleft == 0)   // Data read complete
                    break; 
            } 
            return (totalcharsRead);
        } 

        internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj) {
            int charsRead = 0;
            int charsLeft = 0; 
            int bytesRead = 0;
            int totalcharsRead = 0; 
 
            if (stateObj._longlen == 0) {
                Debug.Assert(stateObj._longlenleft == 0); 
                return 0;       // No data
            }

            Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), 
                    "Out of [....] plp read request");
 
            Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpAnsiChars()!"); 
            charsLeft = len;
 
            if (stateObj._longlenleft == 0) {
                stateObj.ReadPlpLength(false);
                if (stateObj._longlenleft == 0)  // Data read complete
                    return 0; 
            }
 
            Encoding enc = metadata.encoding; 

            if (enc == null) { 
                if (null == _defaultEncoding) {
                    ThrowUnsupportedCollationEncountered(stateObj);
                }
 
                enc = _defaultEncoding;
            } 
 
            while (charsLeft > 0) {
                bytesRead = (int)Math.Min(stateObj._longlenleft, (ulong)charsLeft); 
                if ((stateObj._bTmp == null) || (stateObj._bTmp.Length < bytesRead)) {
                    // Grow the array
                    stateObj._bTmp = new byte[bytesRead];
                } 

                bytesRead = stateObj.ReadPlpBytesChunk(stateObj._bTmp, 0, bytesRead); 
 
                //
                charsRead = enc.GetChars(stateObj._bTmp, 0, bytesRead, buff, offst); 
                charsLeft -= charsRead;
                offst += charsRead;
                totalcharsRead += charsRead;
                if (stateObj._longlenleft == 0)  // Read the next chunk or cleanup state if hit the end 
                    stateObj.ReadPlpLength(false);
 
                if (stateObj._longlenleft == 0)   // Data read complete 
                    break;
            } 
            return (totalcharsRead);
        }

        internal ulong SkipPlpValue(ulong cb, TdsParserStateObject stateObj) { 
            // Read and skip cb bytes or until  ReadPlpLength returns 0.
            int bytesSkipped; 
            ulong totalBytesSkipped = 0; 

            if (stateObj._longlenleft == 0) 
                stateObj.ReadPlpLength(false);

            while ((totalBytesSkipped < cb) &&
                    (stateObj._longlenleft > 0)) { 
                if (stateObj._longlenleft > Int32.MaxValue)
                    bytesSkipped = Int32.MaxValue; 
                else 
                    bytesSkipped = (int)stateObj._longlenleft;
                bytesSkipped = ((cb - totalBytesSkipped) < (ulong) bytesSkipped) ? (int)(cb - totalBytesSkipped) : bytesSkipped; 

                SkipBytes(bytesSkipped, stateObj);
                stateObj._longlenleft -= (ulong)bytesSkipped;
                totalBytesSkipped += (ulong)bytesSkipped; 

                if (stateObj._longlenleft == 0) 
                    stateObj.ReadPlpLength(false); 
            }
 
            return totalBytesSkipped;
        }

        internal ulong PlpBytesLeft(TdsParserStateObject stateObj) { 
            if ((stateObj._longlen != 0) && (stateObj._longlenleft == 0))
                stateObj.ReadPlpLength(false); 
 
            return stateObj._longlenleft;
        } 

        private const ulong _indeterminateSize = 0xffffffffffffffff;        // Represents unknown size

        internal ulong PlpBytesTotalLength(TdsParserStateObject stateObj) { 
            if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN)
                return _indeterminateSize; 
            else if (stateObj._longlen == TdsEnums.SQL_PLP_NULL) 
                return 0;
 
            return stateObj._longlen;
        }

#if STREAMINGPARAMETERS 
        // Writes the plp data byte stream into tds buffer
        // Needs to know if this is the first or last chunk data 
        // 
        internal void WritePlpBytes(byte[] buff, int offst, int len, bool isLastWrite, bool isFirstWrite, TdsParserStateObject stateObj) {
            int bytesLeft; 
            int bytesToWrite;

            Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to WritePlpBytes()!");
            Debug.Assert(stateObj._longlenleft == 0, "Out of [....] plp write request"); 
            if (isFirstWrite) {
                // This is the first write, so we send the PLP length type first 
                if (isLastWrite) { 
                    WriteLong((long)len, stateObj);
                    stateObj._longlen = 0; 
                    bytesLeft = len;
                }
                else if (len == 0) {
                    WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); 
                    stateObj._longlen = 0;
                    bytesLeft = 0; 
                } 
                else {
                    // Write in chunks 
                    stateObj._longlen = TdsEnums.SQL_PLP_UNKNOWNLEN;
                    WriteUnsignedLong(stateObj._longlen, stateObj);
                    bytesLeft = len;
                } 
            }
            else { 
                Debug.Assert(stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN, "Invalid Plp data stream state"); 
                bytesLeft = len;
            } 

            while (bytesLeft > 0) {
                bytesToWrite = bytesLeft;
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN) { 
                    if (bytesToWrite > (int)TdsEnums.SQL_USHORTVARMAXLEN)
                        bytesToWrite = (int)TdsEnums.SQL_USHORTVARMAXLEN; 
 
                    WriteShort((short)bytesToWrite, stateObj);
                } 

                WriteByteArray(buff, bytesToWrite, offst, stateObj);
                offst += bytesToWrite;
                bytesLeft -= bytesToWrite; 
            }
 
            if (isLastWrite) { 
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN)
                    WriteShort(TdsEnums.SQL_PLP_CHUNK_TERMINATOR, stateObj); 

                stateObj._longlen = 0;
            }
        } 

        // Writes the plp data char stream into tds buffer 
        internal void WritePlpChars(char[] buff, int offst, int len, bool isLastWrite, bool isFirstWrite, TdsParserStateObject stateObj) { 
            int charsLeft;
            int charsToWrite; 
            byte[] byteData = null;

            Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to WritePlpBytes()!");
            Debug.Assert(stateObj._longlenleft == 0, "Out of [....] plp write request"); 
            if (isFirstWrite) {
                // This is the first write, so we send the PLP length type first 
                if (isLastWrite) { 
                    WriteLong((long)len, stateObj);
                    stateObj._longlen = 0; 
                    charsLeft = len;
                }
                else if (len == 0) {
                    WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); 
                    stateObj._longlen = 0;
                    charsLeft = 0; 
                } 
                else {
                    // Write in chunks 
                    stateObj._longlen = TdsEnums.SQL_PLP_UNKNOWNLEN;
                    WriteUnsignedLong(stateObj._longlen, stateObj);
                    charsLeft = len;
                } 
            }
            else { 
                Debug.Assert(stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN, "Invalid Plp data stream state"); 
                charsLeft = len;
            } 

            while (charsLeft > 0) {
                charsToWrite = charsLeft;
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN) { 
                    if (charsToWrite > (int)TdsEnums.SQL_USHORTVARMAXLEN)
                        charsToWrite = (int)TdsEnums.SQL_USHORTVARMAXLEN; 
 
                    WriteShort((short)charsToWrite, stateObj);
                } 

                if (byteData == null) {
                    byteData = new byte[ADP.CharSize * charsToWrite];
                } 

                CopyCharsToBytes(buff, offst, byteData, 0, charsToWrite); 
                WriteByteArray(byteData, ADP.CharSize * charsToWrite, 0, stateObj); 
                offst += charsToWrite;
                charsLeft -= charsToWrite; 
            }

            if (isLastWrite) {
                if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN) 
                    WriteShort(TdsEnums.SQL_PLP_CHUNK_TERMINATOR, stateObj);
 
                stateObj._longlen = 0; 
            }
        } 
#endif //STREAMINGPARAMETERS

        const string StateTraceFormatString = "\n\t"
                                        + "         _physicalStateObj = {0}\n\t" 
                                        + "         _pMarsPhysicalConObj = {1}\n\t"
                                        + "         _state = {2}\n\t" 
                                        + "         _server = {3}\n\t" 
                                        + "         _fResetConnection = {4}\n\t"
                                        + "         _defaultCollation = {5}\n\t" 
                                        + "         _defaultCodePage = {6}\n\t"
                                        + "         _defaultLCID = {7}\n\t"
                                        + "         _defaultEncoding = {8}\n\t"
                                        + "         _encryptionOption = {10}\n\t" 
                                        + "         _currentTransaction = {11}\n\t"
                                        + "         _pendingTransaction = {12}\n\t" 
                                        + "         _retainedTransactionId = {13}\n\t" 
                                        + "         _nonTransactedOpenResultCount = {14}\n\t"
                                        + "         _connHandler = {15}\n\t" 
                                        + "         _fAsync = {16}\n\t"
                                        + "         _fMARS = {17}\n\t"
                                        + "         _fAwaitingPreLogin = {18}\n\t"
                                        + "         _fPreLoginErrorOccurred = {19}\n\t" 
                                        + "         _sessionPool = {20}\n\t"
                                        + "         _isShiloh = {21}\n\t" 
                                        + "         _isShilohSP1 = {22}\n\t" 
                                        + "         _isYukon = {23}\n\t"
                                        + "         _sniServerUserName = {24}\n\t" 
                                        + "         _errors = {25}\n\t"
                                        + "         _warnings = {26}\n\t"
                                        + "         _attentionErrors = {27}\n\t"
                                        + "         _attentionWarnings = {28}\n\t" 
                                        + "         _statistics = {29}\n\t"
                                        + "         _statisticsIsInTransaction = {30}\n\t" 
                                        + "         _fPreserveTransaction = {31}" 
                                        ;
        internal string TraceString() { 
            return String.Format(/*IFormatProvider*/ null,
                            StateTraceFormatString,
                            null == _physicalStateObj,
                            null == _pMarsPhysicalConObj, 
                            _state,
                            _server, 
                            _fResetConnection, 
                            null == _defaultCollation ? "(null)" : _defaultCollation.TraceString(),
                            _defaultCodePage, 
                            _defaultLCID,
                            TraceObjectClass(_defaultEncoding),
                            "",
                            _encryptionOption, 
                            null == _currentTransaction ? "(null)" : _currentTransaction.TraceString(),
                            null == _pendingTransaction ? "(null)" : _pendingTransaction.TraceString(), 
                            _retainedTransactionId, 
                            _nonTransactedOpenResultCount,
                            null == _connHandler ? "(null)" : _connHandler.ObjectID.ToString((IFormatProvider)null), 
                            _fAsync,
                            _fMARS,
                            _fAwaitingPreLogin,
                            _fPreLoginErrorOccurred, 
                            null == _sessionPool ? "(null)" : _sessionPool.TraceString(),
                            _isShiloh, 
                            _isShilohSP1, 
                            _isYukon,
                            null == _sniServerUserName ? "(null)" : _sniServerUserName.Length.ToString((IFormatProvider)null), 
                            null == _errors ? "(null)" : _errors.Count.ToString((IFormatProvider)null),
                            null == _warnings ? "(null)" : _warnings.Count.ToString((IFormatProvider)null),
                            null == _attentionErrors ? "(null)" : _attentionErrors.Count.ToString((IFormatProvider)null),
                            null == _attentionWarnings ? "(null)" : _attentionWarnings.Count.ToString((IFormatProvider)null), 
                            null == _statistics,
                            _statisticsIsInTransaction, 
                            _fPreserveTransaction); 
        }
 
        private string TraceObjectClass(object instance) {
            if (null == instance) {
                return "(null)";
            } 
            else {
                return instance.GetType().ToString(); 
            } 
        }
    }    // tdsparser 
}//namespace


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK