Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / TdsParser.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="TdsParser.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.SqlClient {
10     using System;
11     using System.Collections.Generic;
12     using System.Data.Common;
13     using System.Data.ProviderBase;
14     using System.Data.Sql;
15     using System.Data.SqlTypes;
16     using System.Diagnostics;
17     using System.Globalization;
18     using System.IO;
19     using System.Runtime.CompilerServices;
20     using System.Runtime.InteropServices;
21     using System.Text;
22     using System.Threading;
23     using System.Threading.Tasks;
24     using System.Xml;
25
26     using MSS = Microsoft.SqlServer.Server;
27
28     // The TdsParser Object controls reading/writing to the netlib, parsing the tds,
29     // and surfacing objects to the user.
30     sealed internal class TdsParser {
31         private static int _objectTypeCount; // Bid counter
32         internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
33
34         static Task completedTask;
35         static Task CompletedTask {
36             get {
37                 if (completedTask == null) {
38                     completedTask = Task.FromResult<object>(null);
39                 }
40                 return completedTask;
41             }
42         }
43
44         internal int ObjectID {
45             get {
46                 return _objectID;
47             }
48         }
49
50
51         // ReliabilitySection Usage:
52         //
53         // #if DEBUG
54         //        TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
55         //
56         //        RuntimeHelpers.PrepareConstrainedRegions();
57         //        try {
58         //            tdsReliabilitySection.Start();
59         // #else
60         //        {
61         // #endif //DEBUG
62         //
63         //        // code that requires reliability
64         //
65         //        }
66         // #if DEBUG
67         //        finally {
68         //            tdsReliabilitySection.Stop();
69         //        }
70         //  #endif //DEBUG
71
72         internal struct ReliabilitySection {
73 #if DEBUG
74             // do not allocate TLS data in RETAIL bits
75             [ThreadStatic]
76             private static int s_reliabilityCount; // initialized to 0 by CLR
77
78             private bool m_started;  // initialized to false (not started) by CLR
79 #endif //DEBUG
80
81             [Conditional("DEBUG")]
82             internal void Start() {
83 #if DEBUG
84                 Debug.Assert(!m_started);
85                 
86                 RuntimeHelpers.PrepareConstrainedRegions();
87                 try {
88                 }
89                 finally {
90                     ++s_reliabilityCount;
91                     m_started = true;
92                 }
93 #endif //DEBUG
94             }
95
96             [Conditional("DEBUG")]
97             internal void Stop() {
98 #if DEBUG
99                 // cannot assert m_started - ThreadAbortException can be raised before Start is called
100                 
101                 if (m_started) {
102                     Debug.Assert(s_reliabilityCount > 0);
103                     
104                     RuntimeHelpers.PrepareConstrainedRegions();
105                     try {
106                     }
107                     finally {
108                         --s_reliabilityCount;
109                         m_started = false;
110                     }
111                 }
112 #endif //DEBUG
113             }
114
115             // you need to setup for a thread abort somewhere before you call this method
116             [Conditional("DEBUG")]
117             internal static void Assert(string message) {
118 #if DEBUG
119                 Debug.Assert(s_reliabilityCount > 0, message);
120 #endif //DEBUG
121             }
122         }
123
124         // Default state object for parser
125         internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI.
126
127         // Also, default logical stateObj and connection for MARS over SNI.
128         internal TdsParserStateObject _pMarsPhysicalConObj = null; // With MARS enabled, cached physical stateObj and connection.
129
130         // Must keep this around - especially for callbacks on pre-MARS
131         // ReadAsync which will return if physical connection broken!
132         //
133         // Per Instance TDS Parser variables
134         //
135
136         // Constants
137         const int constBinBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream
138         const int constTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader
139
140         // State variables
141         internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
142
143         private string _server = "";                            // name of server that the parser connects to
144
145         internal volatile bool _fResetConnection = false;                 // flag to denote whether we are needing to call sp_reset
146         internal volatile bool _fPreserveTransaction = false;             // flag to denote whether we need to preserve the transaction when reseting
147
148         private SqlCollation   _defaultCollation;                         // default collation from the server
149
150         private int _defaultCodePage;
151
152         private int _defaultLCID;
153
154         internal Encoding _defaultEncoding = null;                  // for sql character data
155
156         private static EncryptionOptions    _sniSupportedEncryptionOption = SNILoadHandle.SingletonInstance.Options;
157
158         private EncryptionOptions           _encryptionOption             = _sniSupportedEncryptionOption;
159
160         private SqlInternalTransaction      _currentTransaction;
161         private SqlInternalTransaction      _pendingTransaction;    // pending transaction for Yukon and beyond.
162         // SQLHOT 483
163         //  need to hold on to the transaction id if distributed transaction merely rolls back without defecting.
164         private long                       _retainedTransactionId        = SqlInternalTransaction.NullTransactionId;
165
166         // This counter is used for the entire connection to track the open result count for all
167         // operations not under a transaction.
168         private int _nonTransactedOpenResultCount = 0;
169
170         // Connection reference
171         private SqlInternalConnectionTds    _connHandler;
172
173         // Async/Mars variables
174         private bool _fMARS = false;
175
176         internal bool _loginWithFailover = false; // set to true while connect in failover mode so parser state object can adjust its logic
177
178         internal AutoResetEvent         _resetConnectionEvent = null;  // Used to serialize executes and call reset on first execute only.
179
180         internal TdsParserSessionPool _sessionPool = null;  // initialized only when we're a MARS parser.
181
182         // Version variables
183         private bool _isShiloh = false; // set to true if we connect to a 8.0 server (SQL 2000) or later
184
185         private bool _isShilohSP1 = false; // set to true if speaking to Shiloh SP1 or later
186
187         private bool _isYukon = false; // set to true if speaking to Yukon or later
188
189         private bool _isKatmai = false;
190
191         private bool _isDenali = false;
192
193         private byte[] _sniSpnBuffer = null;
194
195         // 
196         
197         // SqlStatistics
198         private SqlStatistics _statistics = null;
199
200         private bool _statisticsIsInTransaction = false;
201
202         //
203         // STATIC TDS Parser variables
204         //
205         
206         // NIC address caching
207         private static byte[] s_nicAddress;             // cache the NIC address from the registry
208
209         // SSPI variables
210         private static bool s_fSSPILoaded = false; // bool to indicate whether library has been loaded
211
212         private volatile static UInt32 s_maxSSPILength = 0;     // variable to hold max SSPI data size, keep for token from server
213
214         // ADAL variables
215         private static bool s_fADALLoaded = false; // bool to indicate whether ADAL library has been loaded
216
217         // textptr sequence
218         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 };
219
220         private static object s_tdsParserLock = new object();
221
222         // Various other statics
223         private const int ATTENTION_TIMEOUT = 5000;  // internal attention timeout, in ticks
224
225         // XML metadata substitue sequence
226         private static readonly byte[] s_xmlMetadataSubstituteSequence = { 0xe7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 };
227
228         // size of Guid  (e.g. _clientConnectionId, ActivityId.Id)
229         private const int GUID_SIZE = 16;
230
231         // NOTE: You must take the internal connection's _parserLock before modifying this
232         internal bool _asyncWrite = false;
233
234         // TCE supported flag, used to determine if new TDS fields are present. This is 
235         // useful when talking to downlevel/uplevel server.
236         private bool _serverSupportsColumnEncryption = false;
237
238         /// <summary>
239         /// Get or set if column encryption is supported by the server.
240         /// </summary>
241         internal bool IsColumnEncryptionSupported {
242             get {
243                 return _serverSupportsColumnEncryption;
244             }
245             set {
246                 _serverSupportsColumnEncryption = value;
247             }
248         }
249
250         internal TdsParser(bool MARS, bool fAsynchronous) {
251             _fMARS = MARS; // may change during Connect to pre Yukon servers
252             _physicalStateObj = new TdsParserStateObject(this);
253         }
254
255         internal SqlInternalConnectionTds Connection {
256             get {
257                 return _connHandler;
258             }
259         }
260       
261         internal SqlInternalTransaction CurrentTransaction {
262             get {
263                 return _currentTransaction;
264             }
265             set {
266                 Debug.Assert(value == _currentTransaction
267                           || null == _currentTransaction
268                           || null == value
269                           || (null != _currentTransaction && !_currentTransaction.IsLocal), "attempting to change current transaction?");
270
271                 // If there is currently a transaction active, we don't want to
272                 // change it; this can occur when there is a delegated transaction
273                 // and the user attempts to do an API begin transaction; in these
274                 // cases, it's safe to ignore the set.
275                 if ((null == _currentTransaction && null != value)
276                   ||(null != _currentTransaction && null == value)) {
277                     _currentTransaction = value;
278                 }
279             }
280         }
281
282         internal int DefaultLCID {
283             get {
284                 return _defaultLCID;
285             }
286         }
287
288         internal EncryptionOptions EncryptionOptions {
289             get {
290                 return _encryptionOption;
291             }
292             set {
293                 _encryptionOption = value;
294             }
295         }
296         
297         internal bool IsYukonOrNewer {
298             get {
299                 return _isYukon;
300             }
301         }
302
303         internal bool IsKatmaiOrNewer {
304             get {
305                 return _isKatmai;
306             }
307         }
308
309         internal bool MARSOn {
310             get {
311                 return _fMARS;
312             }
313         }
314
315         internal SqlInternalTransaction PendingTransaction {
316             get {
317                 return _pendingTransaction;
318             }
319             set {
320                 Debug.Assert (null != value, "setting a non-null PendingTransaction?");
321                 _pendingTransaction = value;
322             }
323         }
324
325         internal string Server {
326             get {
327                 return _server;
328             }
329         }
330
331         internal TdsParserState State {
332             get {
333                 return _state;
334             }
335             set {
336                 _state = value;
337             }
338         }
339
340         internal SqlStatistics Statistics {
341             get {
342                 return _statistics;
343             }
344             set {
345                 _statistics = value;
346             }
347         }
348
349         private bool IncludeTraceHeader {
350             get {
351                 return (_isDenali && Bid.TraceOn && Bid.IsOn(ActivityCorrelator.CorrelationTracePoints));
352             }
353
354         }
355
356
357         internal int IncrementNonTransactedOpenResultCount() {
358             // IMPORTANT - this increments the connection wide open result count for all
359             // operations not under a transaction!  Do not call if you intend to modify the 
360             // count for a transaction!
361             Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
362             int result = Interlocked.Increment(ref _nonTransactedOpenResultCount);
363             return result;
364         }
365
366         internal void DecrementNonTransactedOpenResultCount() {
367             // IMPORTANT - this decrements the connection wide open result count for all
368             // operations not under a transaction!  Do not call if you intend to modify the 
369             // count for a transaction!
370             Interlocked.Decrement(ref _nonTransactedOpenResultCount);
371             Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
372         }
373
374         internal void ProcessPendingAck(TdsParserStateObject stateObj) {
375             if (stateObj._attentionSent) {
376                 ProcessAttention(stateObj);
377             }
378         }
379
380         internal void Connect(ServerInfo serverInfo,
381                               SqlInternalConnectionTds connHandler,
382                               bool ignoreSniOpenTimeout,
383                               long timerExpire,
384                               bool encrypt,
385                               bool trustServerCert,
386                               bool integratedSecurity,
387                               bool withFailover,
388                               bool isFirstTransparentAttempt,
389                               SqlAuthenticationMethod authType,
390                               bool disableTnir) {
391             if (_state != TdsParserState.Closed) {
392                 Debug.Assert(false, "TdsParser.Connect called on non-closed connection!");
393                 return;
394             }
395
396             _connHandler = connHandler;
397             _loginWithFailover = withFailover;
398
399             UInt32 sniStatus = SNILoadHandle.SingletonInstance.SNIStatus;
400             if (sniStatus != TdsEnums.SNI_SUCCESS) {
401                 _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
402                 _physicalStateObj.Dispose();
403                 ThrowExceptionAndWarning(_physicalStateObj);
404                 Debug.Assert(false, "SNI returned status != success, but no error thrown?");
405             }
406
407             //Create LocalDB instance if necessary             
408             if (connHandler.ConnectionOptions.LocalDBInstance != null)
409                 LocalDBAPI.CreateLocalDBInstance(connHandler.ConnectionOptions.LocalDBInstance);
410
411             if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
412                 LoadSSPILibrary();
413                 // now allocate proper length of buffer
414                 _sniSpnBuffer = new byte[SNINativeMethodWrapper.SniMaxComposedSpnLength];
415                 Bid.Trace("<sc.TdsParser.Connect|SEC> SSPI or Active Directory Authentication Library for SQL Server based integrated authentication\n");
416             }
417             else {
418                 _sniSpnBuffer = null;
419                 if (authType == SqlAuthenticationMethod.ActiveDirectoryPassword) {
420                     Bid.Trace("<sc.TdsParser.Connect|SEC> Active Directory Password authentication\n");
421                 }
422                 else if (authType == SqlAuthenticationMethod.SqlPassword) {
423                     Bid.Trace("<sc.TdsParser.Connect|SEC> SQL Password authentication\n");
424                 }
425                 else{
426                     Bid.Trace("<sc.TdsParser.Connect|SEC> SQL authentication\n");
427                 }
428             }
429
430             byte[] instanceName = null;
431
432             Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
433             _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.PreLoginBegin);
434             _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.InitializeConnection);
435
436             bool fParallel = _connHandler.ConnectionOptions.MultiSubnetFailover;
437
438             TransparentNetworkResolutionState transparentNetworkResolutionState;
439             if (_connHandler.ConnectionOptions.TransparentNetworkIPResolution && !disableTnir)
440             {
441                 if(isFirstTransparentAttempt)
442                     transparentNetworkResolutionState = TransparentNetworkResolutionState.SequentialMode;
443                 else
444                     transparentNetworkResolutionState = TransparentNetworkResolutionState.ParallelMode;
445             }
446             else 
447                 transparentNetworkResolutionState = TransparentNetworkResolutionState.DisabledMode;
448
449             int  totalTimeout = _connHandler.ConnectionOptions.ConnectTimeout;
450
451             _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire,
452                         out instanceName, _sniSpnBuffer, false, true, fParallel, transparentNetworkResolutionState, totalTimeout);
453
454             if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) {
455                 _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
456
457                 // Since connect failed, free the unmanaged connection memory.
458                 // HOWEVER - only free this after the netlib error was processed - if you
459                 // don't, the memory for the connection object might not be accurate and thus
460                 // a bad error could be returned (as it was when it was freed to early for me).
461                 _physicalStateObj.Dispose();
462                 Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n");
463                 ThrowExceptionAndWarning(_physicalStateObj);
464                 Debug.Assert(false, "SNI returned status != success, but no error thrown?");
465             }
466
467             _server = serverInfo.ResolvedServerName;
468
469             if (null != connHandler.PoolGroupProviderInfo) {
470                 // If we are pooling, check to see if we were processing an
471                 // alias which has changed, which means we need to clean out
472                 // the pool. See Webdata 104293.
473                 // This should not apply to routing, as it is not an alias change, routed connection 
474                 // should still use VNN of AlwaysOn cluster as server for pooling purposes.
475                 connHandler.PoolGroupProviderInfo.AliasCheck(serverInfo.PreRoutingServerName==null ?
476                     serverInfo.ResolvedServerName: serverInfo.PreRoutingServerName);
477             }
478             _state = TdsParserState.OpenNotLoggedIn;
479             _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfullWrite; // SQL BU DT 376766
480             _physicalStateObj.TimeoutTime = timerExpire;
481
482             bool marsCapable = false;
483
484             _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.InitializeConnection);
485             _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake);
486
487             UInt32 result = SNINativeMethodWrapper.SniGetConnectionId(_physicalStateObj.Handle, ref _connHandler._clientConnectionId);
488             Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
489
490             // 
491             Bid.Trace("<sc.TdsParser.Connect|SEC> Sending prelogin handshake\n");
492             SendPreLoginHandshake(instanceName, encrypt);
493
494             _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake);
495             _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake);
496
497             _physicalStateObj.SniContext = SniContext.Snix_PreLogin;
498
499             Bid.Trace("<sc.TdsParser.Connect|SEC> Consuming prelogin handshake\n");
500             PreLoginHandshakeStatus status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, out marsCapable,
501                                                                       out _connHandler._fedAuthRequired);
502
503             if (status == PreLoginHandshakeStatus.InstanceFailure) {
504                 Bid.Trace("<sc.TdsParser.Connect|SEC> Prelogin handshake unsuccessful. Reattempting prelogin handshake\n");
505                 _physicalStateObj.Dispose(); // Close previous connection
506
507                 // On Instance failure re-connect and flush SNI named instance cache.
508                 _physicalStateObj.SniContext=SniContext.Snix_Connect;
509                 _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout);
510
511                 if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) {
512                     _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
513                     Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n");
514                     ThrowExceptionAndWarning(_physicalStateObj);
515                 }
516
517                 UInt32 retCode = SNINativeMethodWrapper.SniGetConnectionId(_physicalStateObj.Handle, ref _connHandler._clientConnectionId);
518                 Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
519                 
520                 Bid.Trace("<sc.TdsParser.Connect|SEC> Sending prelogin handshake\n");
521                 SendPreLoginHandshake(instanceName, encrypt);
522                 status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, out marsCapable,
523                                                   out _connHandler._fedAuthRequired);
524
525                 // Don't need to check for Sphinx failure, since we've already consumed
526                 // one pre-login packet and know we are connecting to Shiloh.
527                 if (status == PreLoginHandshakeStatus.InstanceFailure) {
528                     Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Prelogin handshake unsuccessful. Login failure\n");
529                     throw SQL.InstanceFailure();
530                 }
531             }
532             Bid.Trace("<sc.TdsParser.Connect|SEC> Prelogin handshake successful\n");
533
534             if (_fMARS && marsCapable) {
535                 // if user explictly disables mars or mars not supported, don't create the session pool
536                 _sessionPool = new TdsParserSessionPool(this);
537             }
538             else {
539                 _fMARS = false;
540             }
541
542             if (authType == SqlAuthenticationMethod.ActiveDirectoryPassword || (authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _connHandler._fedAuthRequired)) {
543                 Debug.Assert(!integratedSecurity, "The legacy Integrated Security connection string option cannot be true when using Active Directory Authentication Library for SQL Server Based workflows.");
544
545                 LoadADALLibrary();
546                 if (Bid.AdvancedOn) {
547                     Bid.Trace("<sc.TdsParser.Connect|SEC> Active directory authentication.Loaded Active Directory Authentication Library for SQL Server\n");
548                 }
549             }
550
551             return;
552         }
553
554         internal void RemoveEncryption() {
555             Debug.Assert(_encryptionOption == EncryptionOptions.LOGIN, "Invalid encryption option state");
556
557             UInt32 error = 0;
558
559             // Remove SSL (Encryption) SNI provider since we only wanted to encrypt login.
560             error = SNINativeMethodWrapper.SNIRemoveProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV);
561             if (error != TdsEnums.SNI_SUCCESS) {
562                 _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
563                 ThrowExceptionAndWarning(_physicalStateObj);
564             }
565             // create a new packet encryption changes the internal packet size Bug# 228403
566             try {}   // EmptyTry/Finally to avoid FXCop violation
567             finally {
568                 _physicalStateObj.ClearAllWritePackets();
569             }
570         }
571
572         internal void EnableMars() {
573             if (_fMARS) {
574                 // Cache physical stateObj and connection.
575                 _pMarsPhysicalConObj = _physicalStateObj;
576
577                 UInt32 error = 0;
578                 UInt32 info = 0;
579
580                 // Add SMUX (MARS) SNI provider.
581                 error = SNINativeMethodWrapper.SNIAddProvider(_pMarsPhysicalConObj.Handle, SNINativeMethodWrapper.ProviderEnum.SMUX_PROV, ref info);
582
583                 if (error != TdsEnums.SNI_SUCCESS) {
584                     _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
585                     ThrowExceptionAndWarning(_physicalStateObj);
586                 }
587
588                 // HACK HACK HACK - for Async only
589                 // Have to post read to intialize MARS - will get callback on this when connection goes
590                 // down or is closed.
591
592                 IntPtr temp = IntPtr.Zero;
593
594                 RuntimeHelpers.PrepareConstrainedRegions();
595                 try {} finally {
596                     _pMarsPhysicalConObj.IncrementPendingCallbacks();
597
598                     error = SNINativeMethodWrapper.SNIReadAsync(_pMarsPhysicalConObj.Handle, ref temp);
599
600                     if (temp != IntPtr.Zero) {
601                         // Be sure to release packet, otherwise it will be leaked by native.
602                         SNINativeMethodWrapper.SNIPacketRelease(temp);
603                     }    
604                 }
605                 Debug.Assert(IntPtr.Zero == temp, "unexpected syncReadPacket without corresponding SNIPacketRelease");
606                 if (TdsEnums.SNI_SUCCESS_IO_PENDING != error) {
607                     Debug.Assert(TdsEnums.SNI_SUCCESS != error, "Unexpected successfull read async on physical connection before enabling MARS!");
608                     _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
609                     ThrowExceptionAndWarning(_physicalStateObj);
610                 }
611
612                 _physicalStateObj = CreateSession(); // Create and open default MARS stateObj and connection.
613             }
614         }
615
616         internal TdsParserStateObject CreateSession() {
617             TdsParserStateObject session = new TdsParserStateObject(this, (SNIHandle)_pMarsPhysicalConObj.Handle, true);
618             if (Bid.AdvancedOn) {
619                 Bid.Trace("<sc.TdsParser.CreateSession|ADV> %d# created session %d\n", ObjectID, session.ObjectID);
620             }
621             return session;
622         }
623
624         internal TdsParserStateObject GetSession(object owner) {
625             TdsParserStateObject session = null;
626
627             // 
628
629             if (MARSOn) {
630                 session = _sessionPool.GetSession(owner);
631
632                 Debug.Assert(!session._pendingData, "pending data on a pooled MARS session");
633                 if (Bid.AdvancedOn) {
634                     Bid.Trace("<sc.TdsParser.GetSession|ADV> %d# getting session %d from pool\n", ObjectID, session.ObjectID);
635                 }
636             }
637             else {
638                 session = _physicalStateObj;
639                 if (Bid.AdvancedOn) {
640                     Bid.Trace("<sc.TdsParser.GetSession|ADV> %d# getting physical session %d\n", ObjectID, session.ObjectID);
641                 }
642             }
643             Debug.Assert(session._outputPacketNumber==1, "The packet number is expected to be 1");
644             return session;
645         }
646
647         internal void PutSession(TdsParserStateObject session) {
648             session.AssertStateIsClean();
649
650             if (MARSOn) {
651                 // This will take care of disposing if the parser is closed
652                 _sessionPool.PutSession(session);
653             }
654             else if ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken)) {
655                 // Parser is closed\broken - dispose the stateObj
656                 Debug.Assert(session == _physicalStateObj, "MARS is off, but session to close is not the _physicalStateObj");
657                 _physicalStateObj.SniContext = SniContext.Snix_Close;
658 #if DEBUG
659                 _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
660 #endif
661                 _physicalStateObj.Dispose();
662             }
663             else {
664                 // Non-MARS, and session is ok - remove its owner
665                 _physicalStateObj.Owner = null;
666             }
667         }
668
669         // This is called from a ThreadAbort - ensure that it can be run from a CER Catch
670         internal void BestEffortCleanup() {
671             _state = TdsParserState.Broken;
672
673             var stateObj = _physicalStateObj;
674             if (stateObj != null) {
675                 var stateObjHandle = stateObj.Handle;
676                 if (stateObjHandle != null) {
677                     stateObjHandle.Dispose();
678                 }
679             }
680
681             if (_fMARS) {
682                 var sessionPool = _sessionPool;
683                 if (sessionPool != null) {
684                     sessionPool.BestEffortCleanup();
685                 }
686
687                 var marsStateObj = _pMarsPhysicalConObj;
688                 if (marsStateObj != null) {
689                     var marsStateObjHandle = marsStateObj.Handle;
690                     if (marsStateObjHandle != null) {
691                         marsStateObjHandle.Dispose();
692                     }
693                 }
694             }
695         }
696
697         private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) {
698             // PreLoginHandshake buffer consists of:
699             // 1) Standard header, with type = MT_PRELOGIN
700             // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length)
701             // 3) Consecutive data blocks for each option
702
703             // NOTE: packet data needs to be big endian - not the standard little endian used by
704             // the rest of the parser.
705
706             _physicalStateObj._outputMessageType = TdsEnums.MT_PRELOGIN;
707
708             // Initialize option offset into payload buffer
709             // 5 bytes for each option (1 byte length, 2 byte offset, 2 byte payload length)
710             int offset = (int)PreLoginOptions.NUMOPT * 5 + 1;
711
712             byte[] payload = new byte[(int)PreLoginOptions.NUMOPT * 5 + TdsEnums.MAX_PRELOGIN_PAYLOAD_LENGTH];
713             int payloadLength = 0;
714
715             // 
716
717
718             for (int option = (int)PreLoginOptions.VERSION; option < (int)PreLoginOptions.NUMOPT; option++) {
719                 int optionDataSize = 0;
720
721                 // Fill in the option
722                 _physicalStateObj.WriteByte((byte)option);
723
724                 // Fill in the offset of the option data
725                 _physicalStateObj.WriteByte((byte)((offset & 0xff00) >> 8)); // send upper order byte
726                 _physicalStateObj.WriteByte((byte)(offset & 0x00ff)); // send lower order byte
727
728                 switch (option) {
729                     case (int)PreLoginOptions.VERSION:
730                         Version systemDataVersion = ADP.GetAssemblyVersion();
731
732                         // Major and minor
733                         payload[payloadLength++] = (byte)(systemDataVersion.Major & 0xff);
734                         payload[payloadLength++] = (byte)(systemDataVersion.Minor & 0xff);
735
736                         // Build (Big Endian)
737                         payload[payloadLength++] = (byte)((systemDataVersion.Build & 0xff00) >> 8);
738                         payload[payloadLength++] = (byte)(systemDataVersion.Build & 0xff);
739
740                         // Sub-build (Little Endian)
741                         payload[payloadLength++] = (byte)(systemDataVersion.Revision & 0xff);
742                         payload[payloadLength++] = (byte)((systemDataVersion.Revision & 0xff00) >> 8);
743                         offset += 6;
744                         optionDataSize = 6;
745                         break;
746
747                     case (int)PreLoginOptions.ENCRYPT:
748                         if (_encryptionOption == EncryptionOptions.NOT_SUP) {
749                             // If OS doesn't support encryption, inform server not supported.
750                             payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
751                         }
752                         else {
753                             // Else, inform server of user request.
754                             if (encrypt) {
755                                 payload[payloadLength] = (byte)EncryptionOptions.ON;
756                                 _encryptionOption = EncryptionOptions.ON;
757                             }
758                             else {
759                                 payload[payloadLength] = (byte)EncryptionOptions.OFF;
760                                 _encryptionOption = EncryptionOptions.OFF;
761                             }
762                         }
763
764                         payloadLength += 1;
765                         offset += 1;
766                         optionDataSize = 1;
767                         break;
768
769                     case (int)PreLoginOptions.INSTANCE:
770                         int i = 0;
771
772                         while (instanceName[i] != 0) {
773                             payload[payloadLength] = instanceName[i];
774                             payloadLength++;
775                             i++;
776                         }
777
778                         payload[payloadLength] = 0; // null terminate
779                         payloadLength++;
780                         i++;
781
782                         offset += i;
783                         optionDataSize = i;
784                         break;
785
786                     case (int)PreLoginOptions.THREADID:
787                         Int32 threadID = TdsParserStaticMethods.GetCurrentThreadIdForTdsLoginOnly();
788
789                         payload[payloadLength++] = (byte)((0xff000000 & threadID) >> 24);
790                         payload[payloadLength++] = (byte)((0x00ff0000 & threadID) >> 16);
791                         payload[payloadLength++] = (byte)((0x0000ff00 & threadID) >> 8);
792                         payload[payloadLength++] = (byte)(0x000000ff & threadID);
793                         offset += 4;
794                         optionDataSize = 4;
795                         break;
796
797                     case (int)PreLoginOptions.MARS:
798                         payload[payloadLength++] = (byte)(_fMARS ? 1 : 0);
799                         offset += 1;
800                         optionDataSize += 1;
801                         break;
802
803                     case (int)PreLoginOptions.TRACEID:
804                         byte[] connectionIdBytes = _connHandler._clientConnectionId.ToByteArray();
805                         Debug.Assert(GUID_SIZE == connectionIdBytes.Length);
806                         Buffer.BlockCopy(connectionIdBytes, 0, payload, payloadLength, GUID_SIZE);
807                         payloadLength += GUID_SIZE;
808                         offset += GUID_SIZE;
809                         optionDataSize = GUID_SIZE;
810                        
811                         ActivityCorrelator.ActivityId actId = ActivityCorrelator.Next();
812                         connectionIdBytes = actId.Id.ToByteArray();
813                         Buffer.BlockCopy(connectionIdBytes, 0, payload, payloadLength, GUID_SIZE);
814                         payloadLength += GUID_SIZE;
815                         payload[payloadLength++] = (byte)(0x000000ff & actId.Sequence); 
816                         payload[payloadLength++] = (byte)((0x0000ff00 & actId.Sequence) >> 8);
817                         payload[payloadLength++] = (byte)((0x00ff0000 & actId.Sequence) >> 16);
818                         payload[payloadLength++] = (byte)((0xff000000 & actId.Sequence) >> 24);
819                         int actIdSize = GUID_SIZE + sizeof(UInt32);
820                         offset += actIdSize;
821                         optionDataSize += actIdSize;
822                         Bid.Trace("<sc.TdsParser.SendPreLoginHandshake|INFO> ClientConnectionID %ls, ActivityID %ls\n", _connHandler._clientConnectionId.ToString(), actId.ToString());
823                         break;
824
825                     case (int)PreLoginOptions.FEDAUTHREQUIRED:
826                         payload[payloadLength++] = 0x01;
827                         offset += 1;
828                         optionDataSize += 1;
829                         break;
830
831                     default:
832                         Debug.Assert(false, "UNKNOWN option in SendPreLoginHandshake");
833                         break;
834                 }
835
836                 // Write data length
837                 _physicalStateObj.WriteByte((byte)((optionDataSize & 0xff00) >> 8));
838                 _physicalStateObj.WriteByte((byte)(optionDataSize & 0x00ff));
839             }
840
841             // Write out last option - to let server know the second part of packet completed
842             _physicalStateObj.WriteByte((byte)PreLoginOptions.LASTOPT);
843
844             // Write out payload
845             _physicalStateObj.WriteByteArray(payload, payloadLength, 0);
846
847             // Flush packet
848             _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
849         }
850
851         private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod authType, bool encrypt, bool trustServerCert, bool integratedSecurity, out bool marsCapable, out bool fedAuthRequired) {
852
853             // Assign default values
854             marsCapable = _fMARS;
855             fedAuthRequired = false;
856
857             bool isYukonOrLater = false;
858
859             // 
860
861             Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
862             bool result = _physicalStateObj.TryReadNetworkPacket();
863             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
864
865             if (_physicalStateObj._inBytesRead == 0) {
866                 // If the server did not respond then something has gone wrong and we need to close the connection
867                 _physicalStateObj.AddError(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.PreloginError(), "", 0));
868                 _physicalStateObj.Dispose();
869                 ThrowExceptionAndWarning(_physicalStateObj);
870             }
871
872             // SEC 
873             byte[] payload = new byte[_physicalStateObj._inBytesRead - _physicalStateObj._inBytesUsed - _physicalStateObj._inputHeaderLen];
874
875             Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
876             result = _physicalStateObj.TryReadByteArray(payload, 0, payload.Length);
877             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
878
879             if (payload[0] == 0xaa) {
880                 // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which
881                 // is not supported.  SQL BU DT 296425
882                 throw SQL.InvalidSQLServerVersionUnknown();
883             }
884
885             int offset = 0;
886             int payloadOffset = 0;
887             int payloadLength = 0;
888             int option = payload[offset++];
889
890             while (option != (byte)PreLoginOptions.LASTOPT) {
891                 switch (option) {
892                     case (int)PreLoginOptions.VERSION:
893                         payloadOffset = payload[offset++] << 8 | payload[offset++];
894                         payloadLength = payload[offset++] << 8 | payload[offset++];
895
896                         byte majorVersion = payload[payloadOffset];
897                         byte minorVersion = payload[payloadOffset + 1];
898                         int level = (payload[payloadOffset + 2] << 8) |
899                                              payload[payloadOffset + 3];
900
901                         isYukonOrLater = majorVersion >= 9;
902                         if (!isYukonOrLater) {
903                             marsCapable = false;            // If pre-Yukon, MARS not supported.
904                         }
905
906                         break;
907
908                     case (int)PreLoginOptions.ENCRYPT:
909                         payloadOffset = payload[offset++] << 8 | payload[offset++];
910                         payloadLength = payload[offset++] << 8 | payload[offset++];
911
912                         EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset];
913
914                         /* internal enum EncryptionOptions {
915                             OFF,
916                             ON,
917                             NOT_SUP,
918                             REQ,
919                             LOGIN
920                         } */
921
922                         switch (_encryptionOption) {
923                             case (EncryptionOptions.ON):
924                                 if (serverOption == EncryptionOptions.NOT_SUP) {
925                                     _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
926                                     _physicalStateObj.Dispose();
927                                     ThrowExceptionAndWarning(_physicalStateObj);
928                                 }
929
930                                 break;
931
932                             case (EncryptionOptions.OFF):
933                                 if (serverOption == EncryptionOptions.OFF) {
934                                     // Only encrypt login.
935                                     _encryptionOption = EncryptionOptions.LOGIN;
936                                 }
937                                 else if (serverOption == EncryptionOptions.REQ) {
938                                     // Encrypt all.
939                                     _encryptionOption = EncryptionOptions.ON;
940                                 }
941
942                                 break;
943
944                             case (EncryptionOptions.NOT_SUP):
945                                 if (serverOption == EncryptionOptions.REQ) {
946                                     _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
947                                     _physicalStateObj.Dispose();
948                                     ThrowExceptionAndWarning(_physicalStateObj);
949                                 }
950
951                                 break;
952
953                             default:
954                                 Debug.Assert(false, "Invalid client encryption option detected");
955                                 break;
956                         }
957
958                         if (_encryptionOption == EncryptionOptions.ON ||
959                             _encryptionOption == EncryptionOptions.LOGIN) {
960                             UInt32 error = 0;
961                             
962                             // If we're using legacy server certificate validation behavior (Authentication keyword not provided and not using access token), then validate if
963                             //     Encrypt=true and Trust Sever Certificate = false.
964                             // If using Authentication keyword or access token, validate if Trust Server Certificate=false.
965                             bool shouldValidateServerCert = (encrypt && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert);
966
967                             UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
968                                 | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);
969
970                             if (encrypt && !integratedSecurity) {
971                                 // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI 
972                                 // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context.
973                                 info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS;
974                             }
975
976                             // Add SSL (Encryption) SNI provider.
977                             error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref info);
978
979                             if (error != TdsEnums.SNI_SUCCESS) {
980                                 _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
981                                 ThrowExceptionAndWarning(_physicalStateObj);
982                             }
983
984                             // in the case where an async connection is made, encryption is used and Windows Authentication is used, 
985                             // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its 
986                             // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete 
987                             // before calling SNISecGenClientContext).
988                             error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining());
989                             if (error != TdsEnums.SNI_SUCCESS)
990                             {
991                                 _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
992                                 ThrowExceptionAndWarning(_physicalStateObj);
993                             }
994
995                             // create a new packet encryption changes the internal packet size Bug# 228403
996                             try {} // EmptyTry/Finally to avoid FXCop violation
997                             finally {
998                                 _physicalStateObj.ClearAllWritePackets();
999                              }
1000                         }
1001
1002                         break;
1003
1004                     case (int)PreLoginOptions.INSTANCE:
1005                         payloadOffset = payload[offset++] << 8 | payload[offset++];
1006                         payloadLength = payload[offset++] << 8 | payload[offset++];
1007
1008                         byte ERROR_INST = 0x1;
1009                         byte instanceResult = payload[payloadOffset];
1010
1011                         if (instanceResult == ERROR_INST) {
1012                             // Check if server says ERROR_INST. That either means the cached info
1013                             // we used to connect is not valid or we connected to a named instance
1014                             // listening on default params.
1015                             return PreLoginHandshakeStatus.InstanceFailure;
1016                         }
1017
1018                         break;
1019
1020                     case (int)PreLoginOptions.THREADID:
1021                         // DO NOTHING FOR THREADID
1022                         offset += 4;
1023                         break;
1024
1025                     case (int)PreLoginOptions.MARS:
1026                         payloadOffset = payload[offset++] << 8 | payload[offset++];
1027                         payloadLength = payload[offset++] << 8 | payload[offset++];
1028
1029                         marsCapable = (payload[payloadOffset] == 0 ? false : true);
1030
1031                         Debug.Assert(payload[payloadOffset] == 0 || payload[payloadOffset] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
1032                         break;
1033
1034                     case (int)PreLoginOptions.TRACEID:
1035                         // DO NOTHING FOR TRACEID
1036                         offset += 4;
1037                         break;
1038
1039                     case (int)PreLoginOptions.FEDAUTHREQUIRED:
1040                         payloadOffset = payload[offset++] << 8 | payload[offset++];
1041                         payloadLength = payload[offset++] << 8 | payload[offset++];
1042
1043                         // Only 0x00 and 0x01 are accepted values from the server.
1044                         if (payload[payloadOffset] != 0x00 && payload[payloadOffset] != 0x01) {
1045                             Bid.Trace("<sc.TdsParser.ConsumePreLoginHandshake|ERR> %d#, Server sent an unexpected value for FedAuthRequired PreLogin Option. Value was %d.\n", ObjectID, (int)payload[payloadOffset]);
1046                             throw SQL.ParsingErrorValue(ParsingErrorState.FedAuthRequiredPreLoginResponseInvalidValue, (int)payload[payloadOffset]);
1047                         }
1048
1049                         // We must NOT use the response for the FEDAUTHREQUIRED PreLogin option, if the connection string option
1050                         // was not using the new Authentication keyword or in other words, if Authentication=NotSpecified
1051                         // Or AccessToken is not null, mean token based authentication is used.
1052                         if ((_connHandler.ConnectionOptions != null
1053                             && _connHandler.ConnectionOptions.Authentication != SqlAuthenticationMethod.NotSpecified)
1054                             || _connHandler._accessTokenInBytes != null) 
1055                         {
1056                             fedAuthRequired = payload[payloadOffset] == 0x01 ? true : false;
1057                         }
1058                         break;
1059
1060                     default:
1061                         Debug.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option);
1062
1063                         // DO NOTHING FOR THESE UNKNOWN OPTIONS
1064                         offset += 4;
1065
1066                         break;
1067                 }
1068
1069                 if (offset < payload.Length) {
1070                     option = payload[offset++];
1071                 }
1072                 else {
1073                     break;
1074                 }
1075             }
1076
1077             return PreLoginHandshakeStatus.Successful;
1078         }
1079
1080         internal void Deactivate(bool connectionIsDoomed) {
1081             // Called when the connection that owns us is deactivated.
1082
1083             if (Bid.AdvancedOn) {
1084                 Bid.Trace("<sc.TdsParser.Deactivate|ADV> %d# deactivating\n", ObjectID);
1085             }
1086
1087             if (Bid.IsOn(Bid.ApiGroup.StateDump)) {
1088                 Bid.Trace("<sc.TdsParser.Deactivate|STATE> %d#, %ls\n", ObjectID, TraceString());
1089             }
1090
1091             if (MARSOn) {
1092                 _sessionPool.Deactivate();
1093             }
1094
1095             Debug.Assert(connectionIsDoomed || null == _pendingTransaction, "pending transaction at disconnect?");
1096
1097             if (!connectionIsDoomed && null != _physicalStateObj) {
1098                 if (_physicalStateObj._pendingData) {
1099                     DrainData(_physicalStateObj);
1100                 }
1101
1102                 if (_physicalStateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
1103                     _physicalStateObj.DecrementOpenResultCount();
1104                 }
1105             }
1106
1107             // Any active, non-distributed transaction must be rolled back.  We
1108             // need to wait for distributed transactions to be completed by the
1109             // transaction manager -- we don't want to automatically roll them
1110             // back.
1111             //
1112             // Note that when there is a transaction delegated to this connection,
1113             // we will defer the deactivation of this connection until the
1114             // transaction manager completes the transaction.
1115             SqlInternalTransaction currentTransaction = CurrentTransaction;
1116
1117             if (null != currentTransaction && currentTransaction.HasParentTransaction) {
1118                 currentTransaction.CloseFromConnection();
1119                 Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
1120             }
1121
1122             Statistics = null; // must come after CleanWire or we won't count the stuff that happens there...
1123         }
1124
1125         // Used to close the connection and then free the memory allocated for the netlib connection.
1126         internal void Disconnect() {
1127             if (null != _sessionPool) {
1128                 // MARSOn may be true, but _sessionPool not yet created
1129                 _sessionPool.Dispose();
1130             }
1131
1132             // Can close the connection if its open or broken
1133             if (_state != TdsParserState.Closed) {
1134                 //benign assert - the user could close the connection before consuming all the data
1135                 //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
1136
1137                 _state = TdsParserState.Closed;
1138
1139                 try {
1140                     // If the _physicalStateObj has an owner, we will delay the disposal until the owner is finished with it
1141                     if (!_physicalStateObj.HasOwner) {
1142                         _physicalStateObj.SniContext = SniContext.Snix_Close;
1143 #if DEBUG
1144                         _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
1145 #endif
1146                         _physicalStateObj.Dispose();
1147                     }
1148                     else {
1149                         // Remove the "initial" callback (this will allow the stateObj to be GC collected if need be)
1150                         _physicalStateObj.DecrementPendingCallbacks(false);
1151                     }
1152
1153                     // Not allocated until MARS is actually enabled in SNI.
1154                     if (null != _pMarsPhysicalConObj) {
1155                         _pMarsPhysicalConObj.Dispose();
1156                     }
1157                 }
1158                 finally {
1159                     _pMarsPhysicalConObj = null;
1160                 }
1161             }
1162         }
1163
1164         // Fires a single InfoMessageEvent
1165         private void FireInfoMessageEvent(SqlConnection connection, TdsParserStateObject stateObj, SqlError error) {
1166
1167             string serverVersion = null;
1168
1169             Debug.Assert(connection != null && _connHandler.Connection == connection);
1170
1171             if (_state == TdsParserState.OpenLoggedIn) {
1172                 serverVersion = _connHandler.ServerVersion;
1173             }
1174
1175             SqlErrorCollection sqlErs = new SqlErrorCollection();
1176
1177             sqlErs.Add(error);
1178
1179             SqlException exc = SqlException.CreateException(sqlErs, serverVersion, _connHandler);
1180
1181             bool notified;
1182             connection.OnInfoMessage(new SqlInfoMessageEventArgs(exc), out notified);
1183             if (notified) {
1184                 // observable side-effects, no retry
1185                 stateObj._syncOverAsync = true;
1186             }
1187             return;
1188         }
1189
1190         internal void DisconnectTransaction(SqlInternalTransaction internalTransaction) {
1191             Debug.Assert(_currentTransaction != null && _currentTransaction == internalTransaction, "disconnecting different transaction");
1192
1193             if (_currentTransaction != null && _currentTransaction == internalTransaction) {
1194                 _currentTransaction = null;
1195             }
1196         }
1197
1198         internal void RollbackOrphanedAPITransactions() {
1199             // Any active, non-distributed transaction must be rolled back.
1200             SqlInternalTransaction currentTransaction = CurrentTransaction;
1201
1202             if (null != currentTransaction && currentTransaction.HasParentTransaction && currentTransaction.IsOrphaned) {
1203                 currentTransaction.CloseFromConnection();
1204                 Debug.Assert(null == CurrentTransaction, "rollback didn't clear current transaction?");
1205             }
1206         }
1207
1208         internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool callerHasConnectionLock = false, bool asyncClose = false) {
1209             Debug.Assert(!callerHasConnectionLock || _connHandler._parserLock.ThreadMayHaveLock(), "Caller claims to have lock, but connection lock is not taken");
1210
1211             SqlException exception = null;
1212             bool breakConnection;
1213
1214             // This function should only be called when there was an error or warning.  If there aren't any
1215             // errors, the handler will be called for the warning(s).  If there was an error, the warning(s) will
1216             // be copied to the end of the error collection so that the user may see all the errors and also the
1217             // warnings that occurred.
1218             // can be deleted)
1219             SqlErrorCollection temp = stateObj.GetFullErrorAndWarningCollection(out breakConnection);
1220
1221             Debug.Assert(temp.Count > 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
1222             if (temp.Count == 0)
1223             {
1224                 Bid.Trace("<sc.TdsParser.ThrowExceptionAndWarning|ERR> Potential multi-threaded misuse of connection, unexpectedly empty warnings/errors under lock %d#\n", ObjectID);
1225             }
1226             Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!");
1227             
1228             // Don't break the connection if it is already closed
1229             breakConnection &= (TdsParserState.Closed != _state);
1230             if (breakConnection) {
1231                 if ((_state == TdsParserState.OpenNotLoggedIn) && (_connHandler.ConnectionOptions.TransparentNetworkIPResolution || _connHandler.ConnectionOptions.MultiSubnetFailover || _loginWithFailover) && (temp.Count == 1) && ((temp[0].Number == TdsEnums.TIMEOUT_EXPIRED) || (temp[0].Number == TdsEnums.SNI_WAIT_TIMEOUT))) {
1232                     // DevDiv2 Bug 459546: With "MultiSubnetFailover=yes" in the Connection String, SQLClient incorrectly throws a Timeout using shorter time slice (3-4 seconds), not honoring the actual 'Connect Timeout'
1233                     // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/459546
1234                     // For Multisubnet Failover we slice the timeout to make reconnecting faster (with the assumption that the server will not failover instantaneously)
1235                     // However, when timeout occurs we need to not doom the internal connection and also to mark the TdsParser as closed such that the login will be will retried
1236                     breakConnection = false;
1237                     Disconnect();
1238                 }
1239                 else {
1240                     _state = TdsParserState.Broken;
1241                 }
1242             }
1243             
1244             Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection");
1245             if (temp != null && temp.Count > 0) {
1246                 // Construct the exception now that we've collected all the errors
1247                 string serverVersion = null;
1248                 if (_state == TdsParserState.OpenLoggedIn) {
1249                     serverVersion = _connHandler.ServerVersion;
1250                 }
1251                 exception = SqlException.CreateException(temp, serverVersion, _connHandler);
1252                 if (exception.Procedure == TdsEnums.INIT_ADAL_PACKAGE || exception.Procedure == TdsEnums.INIT_SSPI_PACKAGE) {
1253                     exception._doNotReconnect = true;
1254                 }
1255             }
1256
1257             // call OnError outside of _ErrorCollectionLock to avoid deadlock
1258             if (exception != null) {
1259                 if (breakConnection) {
1260                     // report exception to pending async operation
1261                     // before OnConnectionClosed overrides the exception
1262                     // due to connection close notification through references
1263                     var taskSource = stateObj._networkPacketTaskSource;
1264                     if (taskSource != null) {
1265                         taskSource.TrySetException(ADP.ExceptionWithStackTrace(exception));
1266                     }
1267                 }
1268
1269                 if (asyncClose) {
1270                     // Wait until we have the parser lock, then try to close
1271                     var connHandler = _connHandler;
1272                     Action<Action> wrapCloseAction = closeAction => {
1273                         Task.Factory.StartNew(() => {
1274                             connHandler._parserLock.Wait(canReleaseFromAnyThread: false);
1275                             connHandler.ThreadHasParserLockForClose = true;
1276                             try {
1277                                 closeAction();
1278                             }
1279                             finally {
1280                                 connHandler.ThreadHasParserLockForClose = false;
1281                                 connHandler._parserLock.Release();
1282                             }
1283                         });
1284                     };
1285
1286                     _connHandler.OnError(exception, breakConnection, wrapCloseAction);
1287                 }
1288                 else {
1289                     // Let close know that we already have the _parserLock
1290                     bool threadAlreadyHadParserLockForClose = _connHandler.ThreadHasParserLockForClose;
1291                     if (callerHasConnectionLock) {
1292                         _connHandler.ThreadHasParserLockForClose = true;
1293                     }
1294                     try {
1295                         // the following handler will throw an exception or generate a warning event   
1296                         _connHandler.OnError(exception, breakConnection);
1297                     }
1298                     finally {
1299                         if (callerHasConnectionLock) {
1300                             _connHandler.ThreadHasParserLockForClose = threadAlreadyHadParserLockForClose;
1301                         }
1302                     }
1303                 }
1304             }
1305         }
1306         
1307         internal SqlError ProcessSNIError(TdsParserStateObject stateObj) {
1308 #if DEBUG
1309             // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error
1310             Debug.Assert(SniContext.Undefined!=stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None");
1311 #endif
1312             SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error();
1313             SNINativeMethodWrapper.SNIGetLastError(sniError);
1314
1315             if (sniError.sniError != 0) {
1316
1317                 // handle special SNI error codes that are converted into exception which is not a SqlException.
1318                 switch (sniError.sniError) {
1319                     case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithMoreThan64IPs:
1320                         // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
1321                         throw SQL.MultiSubnetFailoverWithMoreThan64IPs();
1322                     
1323                     case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithInstanceSpecified:
1324                         // Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported.
1325                         throw SQL.MultiSubnetFailoverWithInstanceSpecified();
1326
1327                     case (int)SNINativeMethodWrapper.SniSpecialErrors.MultiSubnetFailoverWithNonTcpProtocol:
1328                         // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
1329                         throw SQL.MultiSubnetFailoverWithNonTcpProtocol();
1330
1331                     // continue building SqlError instance
1332                 }
1333             }
1334
1335             // error.errorMessage is null terminated with garbage beyond that, since fixed length
1336             string errorMessage;
1337             int MessageLength = Array.IndexOf(sniError.errorMessage, '\0');
1338             if (MessageLength == -1) {
1339                 errorMessage = String.Empty;  // If we don't see the expected null return nothing.
1340             } else {            
1341                 errorMessage = new String(sniError.errorMessage, 0, MessageLength);
1342             }
1343
1344             //  Format SNI errors and add Context Information
1345             //
1346             //  General syntax is:
1347             //  <sqlclient message>
1348             //  (provider:<SNIx provider>, error: <SNIx error code> - <SNIx error message>)
1349             //
1350             // errorMessage | sniError |
1351             // -------------------------------------------
1352             // ==null       | x        | must never happen
1353             // !=null       | != 0     | retrieve corresponding errorMessage from resources
1354             // !=null       | == 0     | replace text left of errorMessage
1355             //
1356
1357             Debug.Assert(!ADP.IsEmpty(errorMessage),"Empty error message received from SNI");
1358
1359             string sqlContextInfo = Res.GetString(Enum.GetName(typeof(SniContext), stateObj.SniContext));
1360
1361             string providerRid = String.Format((IFormatProvider)null,"SNI_PN{0}", (int)sniError.provider);
1362             string providerName = Res.GetString(providerRid);
1363             Debug.Assert(!ADP.IsEmpty(providerName), String.Format((IFormatProvider)null,"invalid providerResourceId '{0}'", providerRid));
1364             uint win32ErrorCode = sniError.nativeError;
1365
1366             if (sniError.sniError == 0) {
1367                 // Provider error. The message from provider is preceeded with non-localizable info from SNI
1368                 // strip provider info from SNI
1369                 //
1370                 int iColon = errorMessage.IndexOf(':');
1371                 Debug.Assert(0<=iColon, "':' character missing in sni errorMessage");
1372                 Debug.Assert(errorMessage.Length>iColon+1 && errorMessage[iColon+1]==' ', "Expecting a space after the ':' character");
1373
1374                 // extract the message excluding the colon and trailing cr/lf chars
1375                 if (0<=iColon) {
1376                     int len = errorMessage.Length;
1377                     len -=2;    // exclude "\r\n" sequence
1378                     iColon+=2;  // skip over ": " sequence
1379                     len-=iColon;
1380                     /*
1381                         The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
1382                         Fix Bug 370686, if the message is recieved on a Win9x OS, the error message will not contain MESSAGE TEXT 
1383                         per Bug: 269574.  If we get a errormessage with no message text, just return the entire message otherwise 
1384                         return just the message text.
1385                     */
1386                     if (len > 0) { 
1387                         errorMessage = errorMessage.Substring(iColon, len); 
1388                     }
1389                 }
1390             }
1391             else {
1392                 // SNI error. Replace the entire message
1393                 //
1394                 errorMessage = SQL.GetSNIErrorMessage((int)sniError.sniError);
1395
1396                 // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
1397                 if (sniError.sniError == (int)SNINativeMethodWrapper.SniSpecialErrors.LocalDBErrorCode) {
1398                     errorMessage += LocalDBAPI.GetLocalDBMessage((int)sniError.nativeError);
1399                     win32ErrorCode = 0;
1400                 }
1401             }
1402             errorMessage = String.Format((IFormatProvider)null, "{0} (provider: {1}, error: {2} - {3})",
1403                 sqlContextInfo, providerName, (int)sniError.sniError, errorMessage);
1404
1405             return new SqlError((int)sniError.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS,
1406                                 _server, errorMessage, sniError.function, (int)sniError.lineNumber, win32ErrorCode);
1407         }
1408
1409         internal void CheckResetConnection(TdsParserStateObject stateObj) {
1410             if (_fResetConnection && !stateObj._fResetConnectionSent) {
1411                 Debug.Assert(stateObj._outputPacketNumber == 1 || stateObj._outputPacketNumber == 2, "In ResetConnection logic unexpectedly!");
1412                 try {
1413                     if (_fMARS && !stateObj._fResetEventOwned) {
1414                         // If using Async & MARS and we do not own ResetEvent - grab it.  We need to not grab lock here
1415                         // for case where multiple packets are sent to server from one execute.
1416                         stateObj._fResetEventOwned = _resetConnectionEvent.WaitOne(stateObj.GetTimeoutRemaining(), false);
1417
1418                         if (stateObj._fResetEventOwned) {
1419                             if (stateObj.TimeoutHasExpired) {
1420                                 // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining.
1421                                 stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
1422                                 stateObj.TimeoutTime = 0;
1423                             }
1424                         }
1425
1426                         if (!stateObj._fResetEventOwned) {
1427                             // We timed out waiting for ResetEvent.  Throw timeout exception and reset
1428                             // the buffer.  Nothing else to do since we did not actually send anything
1429                             // to the server.
1430                             stateObj.ResetBuffer();
1431                             Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
1432                             stateObj.AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _server, _connHandler.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT));
1433                             Debug.Assert(_connHandler._parserLock.ThreadMayHaveLock(), "Thread is writing without taking the connection lock");
1434                             ThrowExceptionAndWarning(stateObj, callerHasConnectionLock: true);
1435                         }
1436                     }
1437
1438                     if (_fResetConnection) {
1439                         // Check again to see if we need to send reset.
1440
1441                         Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state for sending reset connection");
1442                         Debug.Assert(_isShiloh, "TdsParser.cs: Error!  _fResetConnection true when not going against Shiloh or later!");
1443
1444                         if (_fPreserveTransaction) {
1445                             // if we are reseting, set bit in header by or'ing with other value
1446                             stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION);
1447                         }
1448                         else {
1449                             // if we are reseting, set bit in header by or'ing with other value
1450                             stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION);
1451                         }
1452
1453                         if (!_fMARS) {
1454                             _fResetConnection = false; // If not MARS, can turn off flag now.
1455                             _fPreserveTransaction = false;
1456                         }
1457                         else {
1458                             stateObj._fResetConnectionSent = true; // Otherwise set flag so we don't resend on multiple packet execute.
1459                         }
1460                     }
1461                     else if (_fMARS && stateObj._fResetEventOwned) {
1462                         Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection");
1463
1464                         // Otherwise if Yukon and we grabbed the event, free it.  Another execute grabbed the event and
1465                         // took care of sending the reset.
1466                         stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
1467                         Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!");
1468                     }
1469                 }
1470                 catch (Exception) {
1471                     if (_fMARS && stateObj._fResetEventOwned) {
1472                         // If exception thrown, and we are on Yukon and own the event, release it!
1473                         stateObj._fResetConnectionSent = false;
1474                         stateObj._fResetEventOwned = !_resetConnectionEvent.Set();
1475                         Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!");
1476                     }
1477
1478                     throw;
1479                 }
1480             }
1481 #if DEBUG
1482             else {
1483                 Debug.Assert(!_fResetConnection ||
1484                              (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned),
1485                              "Unexpected state on else ResetConnection block in WritePacket");
1486             }
1487 #endif
1488         }
1489
1490         //
1491         // Takes a 16 bit short and writes it.
1492         //
1493         internal byte[] SerializeShort(int v, TdsParserStateObject stateObj) {
1494             if (null == stateObj._bShortBytes) {
1495                 stateObj._bShortBytes = new byte[2];
1496             }
1497             else {
1498                 Debug.Assert(2 == stateObj._bShortBytes.Length);
1499                 }
1500
1501             byte[] bytes = stateObj._bShortBytes;
1502             int current = 0;
1503             bytes[current++] = (byte)(v & 0xff);
1504             bytes[current++] = (byte)((v >> 8) & 0xff);
1505             return bytes;
1506         }
1507
1508         internal void WriteShort(int v, TdsParserStateObject stateObj) {
1509             ReliabilitySection.Assert("unreliable call to WriteShort");  // you need to setup for a thread abort somewhere before you call this method
1510
1511             if ((stateObj._outBytesUsed + 2) > stateObj._outBuff.Length) {
1512                 // if all of the short doesn't fit into the buffer
1513                 stateObj.WriteByte((byte)(v & 0xff));
1514                 stateObj.WriteByte((byte)((v >> 8) & 0xff));
1515             }
1516             else {
1517                 // all of the short fits into the buffer
1518                 stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff);
1519                 stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff);
1520                 stateObj._outBytesUsed += 2;
1521             }
1522         }
1523
1524         internal void WriteUnsignedShort(ushort us, TdsParserStateObject stateObj) {
1525             WriteShort((short)us, stateObj);
1526         }
1527
1528         //
1529         // Takes a long and writes out an unsigned int
1530         //
1531         internal byte[] SerializeUnsignedInt(uint i, TdsParserStateObject stateObj) {
1532             return SerializeInt((int)i, stateObj);
1533         }
1534
1535         internal void WriteUnsignedInt(uint i, TdsParserStateObject stateObj) {
1536             WriteInt((int)i, stateObj);
1537         }
1538
1539         //
1540         // Takes an int and writes it as an int.
1541         //
1542         internal byte[] SerializeInt(int v, TdsParserStateObject stateObj) {
1543             if (null == stateObj._bIntBytes) {
1544                 stateObj._bIntBytes = new byte[4];
1545             }
1546             else {
1547                 Debug.Assert (4 == stateObj._bIntBytes.Length);
1548             }
1549
1550             int current = 0;
1551             byte[] bytes = stateObj._bIntBytes;
1552             bytes[current++] = (byte)(v & 0xff);
1553             bytes[current++] = (byte)((v >> 8) & 0xff);
1554             bytes[current++] = (byte)((v >> 16) & 0xff);
1555             bytes[current++] = (byte)((v >> 24) & 0xff);
1556             return bytes;
1557         }
1558
1559         internal void WriteInt(int v, TdsParserStateObject stateObj) {
1560             ReliabilitySection.Assert("unreliable call to WriteInt");  // you need to setup for a thread abort somewhere before you call this method
1561
1562             if ((stateObj._outBytesUsed + 4) > stateObj._outBuff.Length) {
1563                 // if all of the int doesn't fit into the buffer
1564                 for (int shiftValue = 0; shiftValue < sizeof(int) * 8; shiftValue += 8) {
1565                     stateObj.WriteByte((byte)((v >> shiftValue) & 0xff));
1566                 }
1567             }
1568             else {
1569                 // all of the int fits into the buffer
1570                 // NOTE: We don't use a loop here for performance
1571                 stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff);
1572                 stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff);
1573                 stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff);
1574                 stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff);
1575                 stateObj._outBytesUsed += 4;
1576             }
1577         }
1578
1579         //
1580         // Takes a float and writes it as a 32 bit float.
1581         //
1582         internal byte[] SerializeFloat(float v) {
1583             if (Single.IsInfinity(v) || Single.IsNaN(v)) { 
1584                 throw ADP.ParameterValueOutOfRange(v.ToString());
1585             }
1586
1587             return BitConverter.GetBytes(v);
1588         }
1589
1590         internal void WriteFloat(float v, TdsParserStateObject stateObj) {
1591             byte[] bytes = BitConverter.GetBytes(v);
1592
1593             stateObj.WriteByteArray(bytes, bytes.Length, 0);
1594         }
1595
1596         //
1597         // Takes a long and writes it as a long.
1598         //
1599         internal byte[] SerializeLong(long v, TdsParserStateObject stateObj) {
1600             int current = 0;
1601             if (null == stateObj._bLongBytes) {
1602                 stateObj._bLongBytes = new byte[8];
1603             }
1604
1605             byte[] bytes = stateObj._bLongBytes;
1606             Debug.Assert (8 == bytes.Length, "Cached buffer has wrong size");
1607
1608             bytes[current++] = (byte)(v & 0xff);
1609             bytes[current++] = (byte)((v >> 8) & 0xff);
1610             bytes[current++] = (byte)((v >> 16) & 0xff);
1611             bytes[current++] = (byte)((v >> 24) & 0xff);
1612             bytes[current++] = (byte)((v >> 32) & 0xff);
1613             bytes[current++] = (byte)((v >> 40) & 0xff);
1614             bytes[current++] = (byte)((v >> 48) & 0xff);
1615             bytes[current++] = (byte)((v >> 56) & 0xff);
1616
1617             return bytes;
1618         }
1619
1620         internal void WriteLong(long v, TdsParserStateObject stateObj) {
1621             ReliabilitySection.Assert("unreliable call to WriteLong");  // you need to setup for a thread abort somewhere before you call this method
1622
1623             if ((stateObj._outBytesUsed + 8) > stateObj._outBuff.Length) {
1624                 // if all of the long doesn't fit into the buffer
1625                 for (int shiftValue = 0; shiftValue < sizeof(long) * 8; shiftValue += 8) {
1626                     stateObj.WriteByte((byte)((v >> shiftValue) & 0xff));
1627                 }
1628             }
1629             else {
1630                 // all of the long fits into the buffer
1631                 // NOTE: We don't use a loop here for performance
1632                 stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff);
1633                 stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff);
1634                 stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff);
1635                 stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff);
1636                 stateObj._outBuff[stateObj._outBytesUsed + 4] = (byte)((v >> 32) & 0xff);
1637                 stateObj._outBuff[stateObj._outBytesUsed + 5] = (byte)((v >> 40) & 0xff);
1638                 stateObj._outBuff[stateObj._outBytesUsed + 6] = (byte)((v >> 48) & 0xff);
1639                 stateObj._outBuff[stateObj._outBytesUsed + 7] = (byte)((v >> 56) & 0xff);
1640                 stateObj._outBytesUsed += 8;
1641             }
1642         }
1643
1644         //
1645         // Takes a long and writes part of it
1646         //
1647         internal byte[] SerializePartialLong(long v, int length) {
1648             Debug.Assert(length <= 8, "Length specified is longer than the size of a long");
1649             Debug.Assert(length >= 0, "Length should not be negative");
1650
1651             byte[] bytes = new byte[length];
1652
1653             // all of the long fits into the buffer
1654             for (int index = 0; index < length; index++) {
1655                 bytes[index] = (byte)((v >> (index * 8)) & 0xff);
1656             }
1657
1658             return bytes;
1659         }
1660
1661         internal void WritePartialLong(long v, int length, TdsParserStateObject stateObj) {
1662             ReliabilitySection.Assert("unreliable call to WritePartialLong");  // you need to setup for a thread abort somewhere before you call this method
1663             Debug.Assert(length <= 8, "Length specified is longer than the size of a long");
1664             Debug.Assert(length >= 0, "Length should not be negative");
1665
1666             if ((stateObj._outBytesUsed + length) > stateObj._outBuff.Length) {
1667                 // if all of the long doesn't fit into the buffer
1668                 for (int shiftValue = 0; shiftValue < length * 8; shiftValue += 8) {
1669                     stateObj.WriteByte((byte)((v >> shiftValue) & 0xff));
1670                 }
1671             }
1672             else {
1673                 // all of the long fits into the buffer
1674                 for (int index = 0; index < length; index++) {
1675                     stateObj._outBuff[stateObj._outBytesUsed + index] = (byte)((v >> (index * 8)) & 0xff);
1676                 }
1677                 stateObj._outBytesUsed += length;
1678             }
1679         }
1680
1681         //
1682         // Takes a ulong and writes it as a ulong.
1683         //
1684         internal void WriteUnsignedLong(ulong uv, TdsParserStateObject stateObj) {
1685             WriteLong((long)uv, stateObj);
1686         }
1687
1688         //
1689         // Takes a double and writes it as a 64 bit double.
1690         //
1691         internal byte[] SerializeDouble(double v) {
1692             if (Double.IsInfinity(v) || Double.IsNaN(v)) {
1693                 throw ADP.ParameterValueOutOfRange(v.ToString());
1694             }
1695
1696             return BitConverter.GetBytes(v);
1697         }
1698
1699         internal void WriteDouble(double v, TdsParserStateObject stateObj) {
1700             byte[] bytes = BitConverter.GetBytes(v);
1701
1702             stateObj.WriteByteArray(bytes, bytes.Length, 0);
1703         }
1704
1705         internal void PrepareResetConnection(bool preserveTransaction) {
1706             // Set flag to reset connection upon next use - only for use on shiloh!
1707             _fResetConnection = true;
1708             _fPreserveTransaction = preserveTransaction;
1709         }
1710
1711         internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) {
1712             RuntimeHelpers.PrepareConstrainedRegions();
1713             try {
1714 #if DEBUG
1715                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1716                 RuntimeHelpers.PrepareConstrainedRegions();
1717                 try {
1718                     tdsReliabilitySection.Start();
1719 #endif //DEBUG
1720                     return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj);
1721 #if DEBUG
1722                 }
1723                 finally {
1724                     tdsReliabilitySection.Stop();
1725                 }
1726 #endif //DEBUG
1727             }
1728             catch (OutOfMemoryException) {
1729                 _connHandler.DoomThisConnection();
1730                 throw;
1731             }
1732             catch (StackOverflowException) {
1733                 _connHandler.DoomThisConnection();
1734                 throw;
1735             }
1736             catch (ThreadAbortException) {
1737                 _connHandler.DoomThisConnection();
1738                 throw;
1739             }
1740         }
1741         
1742
1743         internal bool Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) {
1744             bool syncOverAsync = stateObj._syncOverAsync;
1745             try
1746             {
1747                 stateObj._syncOverAsync = true;
1748
1749                 bool dataReady;
1750                 bool result = TryRun(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj, out dataReady);
1751                 Debug.Assert(result == true, "Should never return false when _syncOverAsync is set");
1752                 return dataReady;
1753             }
1754             finally
1755             {
1756                 stateObj._syncOverAsync = syncOverAsync;
1757             }
1758         }
1759
1760         /// <summary>
1761         /// Checks if the given token is a valid TDS token
1762         /// </summary>
1763         /// <param name="token">Token to check</param>
1764         /// <returns>True if the token is a valid TDS token, otherwise false</returns>
1765         internal static bool IsValidTdsToken(byte token) {
1766             return (
1767                 token == TdsEnums.SQLERROR ||
1768                 token == TdsEnums.SQLINFO ||
1769                 token == TdsEnums.SQLLOGINACK ||
1770                 token == TdsEnums.SQLENVCHANGE ||
1771                 token == TdsEnums.SQLRETURNVALUE ||
1772                 token == TdsEnums.SQLRETURNSTATUS ||
1773                 token == TdsEnums.SQLCOLNAME ||
1774                 token == TdsEnums.SQLCOLFMT ||
1775                 token == TdsEnums.SQLCOLMETADATA ||
1776                 token == TdsEnums.SQLALTMETADATA ||
1777                 token == TdsEnums.SQLTABNAME ||
1778                 token == TdsEnums.SQLCOLINFO ||
1779                 token == TdsEnums.SQLORDER ||
1780                 token == TdsEnums.SQLALTROW ||
1781                 token == TdsEnums.SQLROW ||
1782                 token == TdsEnums.SQLNBCROW ||
1783                 token == TdsEnums.SQLDONE ||
1784                 token == TdsEnums.SQLDONEPROC ||
1785                 token == TdsEnums.SQLDONEINPROC ||
1786                 token == TdsEnums.SQLROWCRC ||
1787                 token == TdsEnums.SQLSECLEVEL ||
1788                 token == TdsEnums.SQLPROCID ||
1789                 token == TdsEnums.SQLOFFSET ||
1790                 token == TdsEnums.SQLSSPI ||
1791                 token == TdsEnums.SQLFEATUREEXTACK ||
1792                 token == TdsEnums.SQLSESSIONSTATE ||
1793                 token == TdsEnums.SQLFEDAUTHINFO);
1794         }
1795
1796         // Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces
1797         internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, out bool dataReady) {
1798             ReliabilitySection.Assert("unreliable call to Run");  // you need to setup for a thread abort somewhere before you call this method
1799             Debug.Assert((SniContext.Undefined != stateObj.SniContext) &&       // SniContext must not be Undefined
1800                 ((stateObj._attentionSent) || ((SniContext.Snix_Execute != stateObj.SniContext) && (SniContext.Snix_SendRows != stateObj.SniContext))),  // SniContext should not be Execute or SendRows unless attention was sent (and, therefore, we are looking for an ACK)
1801                         String.Format("Unexpected SniContext on call to TryRun; SniContext={0}", stateObj.SniContext));
1802
1803             if (TdsParserState.Broken == State || TdsParserState.Closed == State ) {
1804                 dataReady = true;
1805                 return true; // Just in case this is called in a loop, expecting data to be returned.
1806             }
1807             
1808             dataReady = false;
1809             
1810             do {
1811                 // If there is data ready, but we didn't exit the loop, then something is wrong
1812                 Debug.Assert(!dataReady, "dataReady not expected - did we forget to skip the row?");
1813
1814                 if (stateObj._internalTimeout) {
1815                     runBehavior = RunBehavior.Attention;
1816                 }
1817
1818                 if (TdsParserState.Broken == State || TdsParserState.Closed == State)
1819                     break; // jump out of the loop if the state is already broken or closed.
1820
1821                 if (!stateObj._accumulateInfoEvents && (stateObj._pendingInfoEvents != null)) {
1822                     if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) {
1823                         SqlConnection connection = null;
1824                         if (_connHandler != null)
1825                             connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref
1826                         // We are omitting checks for error.Class in the code below (see processing of INFO) since we know (and assert) that error class
1827                         // error.Class < TdsEnums.MIN_ERROR_CLASS for info message. 
1828                         // Also we know that TdsEnums.MIN_ERROR_CLASS<TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS
1829                         if ((connection != null) && connection.FireInfoMessageEventOnUserErrors)
1830                         {
1831                             foreach (SqlError error in stateObj._pendingInfoEvents)
1832                                 FireInfoMessageEvent(connection, stateObj, error);
1833                         }
1834                         else
1835                             foreach (SqlError error in stateObj._pendingInfoEvents)
1836                                 stateObj.AddWarning(error);
1837                             
1838                     }
1839                     stateObj._pendingInfoEvents=null;
1840                 }
1841
1842                 byte token;
1843                 if (!stateObj.TryReadByte(out token)) {
1844                     return false;
1845                 }
1846
1847                 if (!IsValidTdsToken(token)) {
1848                     Debug.Assert(false, String.Format((IFormatProvider)null, "unexpected token; token = {0,-2:X2}", token));
1849                     _state = TdsParserState.Broken;
1850                     _connHandler.BreakConnection();
1851                     Bid.Trace("<sc.TdsParser.Run|ERR> Potential multi-threaded misuse of connection, unexpected TDS token found %d#\n", ObjectID);
1852                     throw SQL.ParsingErrorToken(ParsingErrorState.InvalidTdsTokenReceived, token); // MDAC 82443
1853                 }
1854
1855                 int tokenLength;
1856                 if (!TryGetTokenLength(token, stateObj, out tokenLength)) {
1857                     return false;
1858                 }
1859
1860                 switch (token) {
1861                 case TdsEnums.SQLERROR:
1862                 case TdsEnums.SQLINFO:
1863                 {
1864                     if (token == TdsEnums.SQLERROR) {
1865                         stateObj._errorTokenReceived = true; // Keep track of the fact error token was received - for Done processing.
1866                     }
1867
1868                     SqlError error;
1869                     if (!TryProcessError(token, stateObj, out error)) {
1870                         return false;
1871                     }
1872
1873                     if (token == TdsEnums.SQLINFO && stateObj._accumulateInfoEvents)
1874                     {
1875                         Debug.Assert(error.Class < TdsEnums.MIN_ERROR_CLASS, "INFO with class > TdsEnums.MIN_ERROR_CLASS");
1876
1877                         if (stateObj._pendingInfoEvents == null)
1878                             stateObj._pendingInfoEvents = new List<SqlError>();
1879                         stateObj._pendingInfoEvents.Add(error);
1880                         stateObj._syncOverAsync = true;
1881                         break;
1882                     }
1883
1884                     if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) {
1885                         // If FireInfoMessageEventOnUserErrors is true, we have to fire event without waiting.
1886                         // Otherwise we can go ahead and add it to errors/warnings collection.
1887                         SqlConnection connection = null;
1888                         if (_connHandler != null)
1889                             connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref
1890
1891                         if ((connection != null) && 
1892                             (connection.FireInfoMessageEventOnUserErrors == true) &&
1893                             (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS)) {
1894                             // Fire SqlInfoMessage here
1895                             FireInfoMessageEvent(connection, stateObj, error);
1896                         }
1897                         else {
1898                             // insert error/info into the appropriate exception - warning if info, exception if error
1899                             if (error.Class < TdsEnums.MIN_ERROR_CLASS) {
1900                                 stateObj.AddWarning(error);
1901                             }
1902                             else if (error.Class < TdsEnums.FATAL_ERROR_CLASS) {
1903                                 // VSTFDEVDIV 479643: continue results processing for all non-fatal errors (<20)
1904
1905                                 stateObj.AddError(error);
1906
1907                                 // Add it to collection - but do NOT change run behavior UNLESS
1908                                 // we are in an ExecuteReader call - at which time we will be throwing
1909                                 // anyways so we need to consume all errors.  This is not the case
1910                                 // if we have already given out a reader.  If we have already given out
1911                                 // a reader we need to throw the error but not halt further processing.  We used to
1912                                 // halt processing and that was a bug preventing the user from
1913                                 // processing subsequent results.
1914
1915                                 if (null != dataStream) { // Webdata 104560
1916                                     if (!dataStream.IsInitialized) {
1917                                         runBehavior = RunBehavior.UntilDone;
1918                                     }
1919                                 }
1920                             }
1921                             else {
1922                                 stateObj.AddError(error);
1923
1924                                 // Else we have a fatal error and we need to change the behavior
1925                                 // since we want the complete error information in the exception.
1926                                 // Besides - no further results will be received.
1927                                 runBehavior = RunBehavior.UntilDone;
1928                             }
1929                         }
1930                     }
1931                     else if (error.Class >= TdsEnums.FATAL_ERROR_CLASS) {
1932                         stateObj.AddError(error);
1933                     }
1934                     break;
1935                 }
1936
1937                 case TdsEnums.SQLCOLINFO:
1938                 {
1939                     if (null != dataStream) {
1940                         _SqlMetaDataSet metaDataSet;
1941                         if (!TryProcessColInfo(dataStream.MetaData, dataStream, stateObj, out metaDataSet)) {
1942                             return false;
1943                         }
1944                         if (!dataStream.TrySetMetaData(metaDataSet, false)) {
1945                             return false;
1946                         }
1947                         dataStream.BrowseModeInfoConsumed = true;
1948                     }
1949                     else { // no dataStream
1950                         if (!stateObj.TrySkipBytes(tokenLength)) {
1951                             return false;
1952                         }
1953                     }
1954                     break;
1955                 }
1956
1957                 case TdsEnums.SQLDONE:
1958                 case TdsEnums.SQLDONEPROC:
1959                 case TdsEnums.SQLDONEINPROC:
1960                 {
1961                     // RunBehavior can be modified - see SQL BU DT 269516 & 290090
1962                     if (!TryProcessDone(cmdHandler, dataStream, ref runBehavior, stateObj)) {
1963                         return false;
1964                     }
1965                     if ((token == TdsEnums.SQLDONEPROC) &&  (cmdHandler != null)) {
1966                         // If the current parse/read is for the results of describe parameter encryption RPC requests,
1967                         // call a different handler which will update the describe parameter encryption RPC structures
1968                         // with the results, instead of the actual user RPC requests.
1969                         if (cmdHandler.IsDescribeParameterEncryptionRPCCurrentlyInProgress) {
1970                             cmdHandler.OnDoneDescribeParameterEncryptionProc(stateObj);
1971                         }
1972                         else {
1973                             cmdHandler.OnDoneProc();
1974                         }
1975                     }
1976
1977                     break;
1978                 }
1979
1980                 case TdsEnums.SQLORDER:
1981                 {
1982                     // don't do anything with the order token so read off the pipe
1983                     if (!stateObj.TrySkipBytes(tokenLength)) {
1984                         return false;
1985                     }
1986                     break;
1987                 }
1988
1989                 case TdsEnums.SQLALTMETADATA:
1990                 {
1991                     stateObj.CloneCleanupAltMetaDataSetArray();
1992
1993                     if (stateObj._cleanupAltMetaDataSetArray == null) {  
1994                         // create object on demand (lazy creation)
1995                         stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection();
1996                     }
1997
1998                     _SqlMetaDataSet cleanupAltMetaDataSet;
1999                     if (!TryProcessAltMetaData(tokenLength, stateObj, out cleanupAltMetaDataSet)) {
2000                         return false;
2001                     }
2002
2003                     stateObj._cleanupAltMetaDataSetArray.SetAltMetaData(cleanupAltMetaDataSet);
2004                     if (null != dataStream) {
2005                         byte metadataConsumedByte;
2006                         if (!stateObj.TryPeekByte(out metadataConsumedByte)) {
2007                             return false;
2008                         }
2009                         if (!dataStream.TrySetAltMetaDataSet(cleanupAltMetaDataSet, (TdsEnums.SQLALTMETADATA != metadataConsumedByte))) {
2010                             return false;
2011                         }
2012                     }
2013
2014                     break;
2015                 }
2016
2017                 case TdsEnums.SQLALTROW:
2018                 {
2019                     if (!stateObj.TryStartNewRow(isNullCompressed:false)) { // altrows are not currently null compressed
2020                         return false;
2021                     }
2022
2023                     // read will call run until dataReady. Must not read any data if returnimmetiately set
2024                     if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) {
2025                         ushort altRowId;
2026                         if (!stateObj.TryReadUInt16(out altRowId)) { // get altRowId
2027                             return false;
2028                         }
2029
2030                         if (!TrySkipRow(stateObj._cleanupAltMetaDataSetArray.GetAltMetaData(altRowId), stateObj)) { // skip altRow
2031                             return false;
2032                         }
2033                     }
2034                     else {
2035                         dataReady = true;
2036                     }
2037
2038                     break;
2039                 }
2040
2041                 case TdsEnums.SQLENVCHANGE:
2042                 {
2043                     // ENVCHANGE must be processed synchronously (since it can modify the state of many objects)
2044                     stateObj._syncOverAsync = true;
2045
2046                     SqlEnvChange[] env;
2047                     if (!TryProcessEnvChange(tokenLength, stateObj, out env)) {
2048                         return false;
2049                     }
2050
2051                     for (int ii = 0; ii < env.Length; ii++) {
2052                         if (env[ii] != null && !this.Connection.IgnoreEnvChange) {
2053                             switch (env[ii].type) {
2054                             case TdsEnums.ENV_BEGINTRAN:
2055                             case TdsEnums.ENV_ENLISTDTC:
2056                                 // When we get notification from the server of a new
2057                                 // transaction, we move any pending transaction over to
2058                                 // the current transaction, then we store the token in it.
2059                                 // if there isn't a pending transaction, then it's either
2060                                 // a TSQL transaction or a distributed transaction.
2061                                 Debug.Assert(null == _currentTransaction, "non-null current transaction with an ENV Change");
2062                                 _currentTransaction = _pendingTransaction;
2063                                 _pendingTransaction = null;
2064
2065                                 if (null != _currentTransaction) {
2066                                     _currentTransaction.TransactionId = env[ii].newLongValue;   // this is defined as a ULongLong in the server and in the TDS Spec.
2067                                 }
2068                                 else {
2069                                     TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env[ii].type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed;
2070                                     _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue);
2071                                 }
2072                                 if (null != _statistics && !_statisticsIsInTransaction) {
2073                                     _statistics.SafeIncrement(ref _statistics._transactions);
2074                                 }
2075                                 _statisticsIsInTransaction = true;
2076                                 _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
2077                                 break;
2078                             case TdsEnums.ENV_DEFECTDTC:
2079                             case TdsEnums.ENV_TRANSACTIONENDED:
2080                             case TdsEnums.ENV_COMMITTRAN:
2081                                 // SQLHOT 483
2082                                 //  Must clear the retain id if the server-side transaction ends by anything other
2083                                 //  than rollback.
2084                                 _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
2085                                 goto case TdsEnums.ENV_ROLLBACKTRAN;
2086                             case TdsEnums.ENV_ROLLBACKTRAN:
2087                                 // When we get notification of a completed transaction
2088                                 // we null out the current transaction.
2089                                 if (null != _currentTransaction) {
2090 #if DEBUG                                    
2091                                     // Check null for case where Begin and Rollback obtained in the same message.
2092                                     if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) {
2093                                         Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!");
2094                                     }
2095 #endif
2096
2097                                     if (TdsEnums.ENV_COMMITTRAN == env[ii].type) {
2098                                         _currentTransaction.Completed(TransactionState.Committed);
2099                                     }
2100                                     else if (TdsEnums.ENV_ROLLBACKTRAN == env[ii].type) {
2101                                         //  Hold onto transaction id if distributed tran is rolled back.  This must
2102                                         //  be sent to the server on subsequent executions even though the transaction
2103                                         //  is considered to be rolled back.
2104                                         if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) {
2105                                             _retainedTransactionId = env[ii].oldLongValue;
2106                                         }
2107                                         _currentTransaction.Completed(TransactionState.Aborted);
2108                                     }
2109                                     else {
2110                                         // 
2111                                         _currentTransaction.Completed(TransactionState.Unknown);
2112                                     }
2113                                     _currentTransaction = null;
2114                                 }
2115                                 _statisticsIsInTransaction = false;
2116                                 break;
2117                             default:
2118                                 _connHandler.OnEnvChange(env[ii]);
2119                                 break;
2120                             }
2121                         }
2122                     }
2123                     break;
2124                 }
2125                 case TdsEnums.SQLLOGINACK:
2126                 {
2127                     Bid.Trace("<sc.TdsParser.TryRun|SEC> Received login acknowledgement token\n");
2128                     SqlLoginAck ack;
2129                     if (!TryProcessLoginAck(stateObj, out ack)) {
2130                         return false;
2131                     }
2132
2133                     _connHandler.OnLoginAck(ack);
2134                     break;
2135                 }
2136                 case TdsEnums.SQLFEATUREEXTACK: 
2137                 {                    
2138                     if (!TryProcessFeatureExtAck(stateObj)) {
2139                         return false;
2140                     }
2141                     break;
2142                 }
2143                 case TdsEnums.SQLFEDAUTHINFO:
2144                 {
2145                     _connHandler._federatedAuthenticationInfoReceived = true;
2146                     SqlFedAuthInfo info;
2147                     Bid.Trace("<sc.TdsParser.TryRun|SEC> Received federated authentication info token\n");
2148                     if (!TryProcessFedAuthInfo(stateObj, tokenLength, out info)) {
2149                         return false;
2150                     }
2151                     _connHandler.OnFedAuthInfo(info);
2152                     break;
2153                 }
2154                 case TdsEnums.SQLSESSIONSTATE: 
2155                 {
2156                     if (!TryProcessSessionState(stateObj, tokenLength,  _connHandler._currentSessionData)) {
2157                         return false;
2158                     }
2159                     break;
2160                 }
2161                 case TdsEnums.SQLCOLMETADATA:
2162                 {
2163                     if (tokenLength != TdsEnums.VARNULL) {
2164                         _SqlMetaDataSet metadata;
2165                         if (!TryProcessMetaData(tokenLength, stateObj, out metadata, 
2166                                                 cmdHandler != null ? cmdHandler.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting)) {
2167                             return false;
2168                         }
2169                         stateObj._cleanupMetaData = metadata;
2170                     }
2171                     else {
2172                         if (cmdHandler != null) {
2173                             stateObj._cleanupMetaData = cmdHandler.MetaData;
2174                         }
2175                     }
2176
2177                     if (null != dataStream) {
2178                         byte peekedToken;
2179                         if (!stateObj.TryPeekByte(out peekedToken)) { // temporarily cache next byte
2180                             return false;
2181                         }
2182
2183                         if (!dataStream.TrySetMetaData(stateObj._cleanupMetaData, (TdsEnums.SQLTABNAME == peekedToken || TdsEnums.SQLCOLINFO == peekedToken))) {
2184                             return false;
2185                         }
2186                     }
2187                     else if (null != bulkCopyHandler) {
2188                         bulkCopyHandler.SetMetaData(stateObj._cleanupMetaData);
2189                     }
2190                     break;
2191                 }
2192                 case TdsEnums.SQLROW:
2193                 case TdsEnums.SQLNBCROW:
2194                 {
2195                     Debug.Assert(stateObj._cleanupMetaData != null, "Reading a row, but the metadata is null");
2196
2197                     if (token == TdsEnums.SQLNBCROW) {
2198                         if (!stateObj.TryStartNewRow(isNullCompressed: true, nullBitmapColumnsCount: stateObj._cleanupMetaData.Length)) {
2199                             return false;
2200                         }
2201                     }
2202                     else {
2203                         if (!stateObj.TryStartNewRow(isNullCompressed:false)) {
2204                             return false;
2205                         }
2206                     }
2207
2208                     if (null != bulkCopyHandler) {
2209                         // 
2210                         if (!TryProcessRow(stateObj._cleanupMetaData, bulkCopyHandler.CreateRowBuffer(), bulkCopyHandler.CreateIndexMap(), stateObj)) {
2211                             return false;
2212                         }
2213                     }
2214                     else if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) {
2215                         if (!TrySkipRow(stateObj._cleanupMetaData, stateObj)) { // skip rows
2216                             return false;
2217                         }
2218                     }
2219                     else {
2220                         dataReady = true;
2221                     }
2222
2223                     if (_statistics != null) {
2224                         _statistics.WaitForDoneAfterRow = true;
2225                     }
2226                     break;
2227                 }
2228                 case TdsEnums.SQLRETURNSTATUS:
2229                     int status;
2230                     if (!stateObj.TryReadInt32(out status)) {
2231                         return false;
2232                     }
2233                     if (cmdHandler != null) {
2234                         cmdHandler.OnReturnStatus(status);
2235                     }
2236                     break;
2237                 case TdsEnums.SQLRETURNVALUE:
2238                 {
2239                     SqlReturnValue returnValue;
2240                     if (!TryProcessReturnValue(tokenLength, stateObj, out returnValue, 
2241                                                cmdHandler != null ? cmdHandler.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting)) {
2242                         return false;
2243                     }
2244                     if (cmdHandler != null) {
2245                         cmdHandler.OnReturnValue(returnValue, stateObj);
2246                     }
2247                     break;
2248                 }
2249                 case TdsEnums.SQLSSPI:
2250                 {
2251                     // token length is length of SSPI data - call ProcessSSPI with it
2252
2253                     Debug.Assert(stateObj._syncOverAsync, "ProcessSSPI does not support retry, do not attempt asynchronously");
2254                     stateObj._syncOverAsync = true;
2255                     
2256                     ProcessSSPI(tokenLength);
2257                     break;
2258                 }
2259                 case TdsEnums.SQLTABNAME:
2260                 {
2261                     if (null != dataStream) {
2262                         MultiPartTableName[] tableNames;
2263                         if (!TryProcessTableName(tokenLength, stateObj, out tableNames)) {
2264                             return false;
2265                         }
2266                         dataStream.TableNames = tableNames;
2267                     }
2268                     else {
2269                         if (!stateObj.TrySkipBytes(tokenLength)) {
2270                             return false;
2271                         }
2272                     }
2273                     break;
2274                 }
2275
2276                 default:
2277                     Debug.Assert(false, "Unhandled token:  " + token.ToString(CultureInfo.InvariantCulture));
2278                     break;
2279             }
2280
2281             Debug.Assert(stateObj._pendingData || !dataReady, "dataReady is set, but there is no pending data");
2282             }
2283
2284             // Loop while data pending & runbehavior not return immediately, OR
2285             // if in attention case, loop while no more pending data & attention has not yet been
2286             // received.
2287             while ((stateObj._pendingData &&
2288                     (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior))) ||
2289                 (!stateObj._pendingData && stateObj._attentionSent && !stateObj._attentionReceived));
2290             
2291 #if DEBUG
2292             if ((stateObj._pendingData) && (!dataReady)) {
2293                 byte token;
2294                 if (!stateObj.TryPeekByte(out token)) {
2295                     return false;
2296                 }
2297                 Debug.Assert(IsValidTdsToken(token), string.Format("DataReady is false, but next token is not valid: {0,-2:X2}", token));
2298             }
2299 #endif
2300
2301             if (!stateObj._pendingData) {
2302                 if (null != CurrentTransaction) {
2303                     CurrentTransaction.Activate();
2304                 }
2305             }
2306
2307             // if we recieved an attention (but this thread didn't send it) then
2308             // we throw an Operation Cancelled error
2309             if (stateObj._attentionReceived) {
2310                 // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close
2311                 // Spin until SendAttention has cleared _attentionSending, this prevents a race condition between receiving the attention ACK and setting _attentionSent
2312                 SpinWait.SpinUntil(() => !stateObj._attentionSending);
2313
2314                 Debug.Assert(stateObj._attentionSent, "Attention ACK has been received without attention sent");
2315                 if (stateObj._attentionSent) {
2316                     // Reset attention state.
2317                     stateObj._attentionSent = false;
2318                     stateObj._attentionReceived = false;
2319
2320                     if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior) && !stateObj._internalTimeout) {
2321                         // Add attention error to collection - if not RunBehavior.Clean!
2322                         stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0));
2323                     }
2324                 }
2325             }
2326
2327             if (stateObj.HasErrorOrWarning) {
2328                 ThrowExceptionAndWarning(stateObj);
2329             }
2330             return true;
2331         }
2332
2333         private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, out SqlEnvChange[] sqlEnvChange) {
2334             // There could be multiple environment change messages following this token.
2335             byte byteLength;
2336             int processedLength = 0;
2337             int nvalues = 0;
2338             SqlEnvChange[] envarray = new SqlEnvChange[3];  // Why is this hardcoded to 3?
2339
2340             sqlEnvChange = null;
2341
2342             while (tokenLength > processedLength) {
2343
2344                 if (nvalues >= envarray.Length) {
2345                     // This is a rare path. Most of the time we will have 1 or 2 envchange data streams.
2346                     SqlEnvChange[] newenvarray = new SqlEnvChange[envarray.Length + 3];
2347
2348                     for (int ii = 0; ii < envarray.Length; ii++)
2349                         newenvarray[ii] = envarray[ii];
2350
2351                     envarray = newenvarray;
2352                 }
2353
2354                 SqlEnvChange env = new SqlEnvChange();
2355
2356                 if (!stateObj.TryReadByte(out env.type)) {
2357                     return false;
2358                 }
2359
2360                 envarray[nvalues] = env;
2361                 nvalues++;
2362
2363                 switch (env.type) {
2364                     case TdsEnums.ENV_DATABASE:
2365                     case TdsEnums.ENV_LANG:
2366                         if (!TryReadTwoStringFields(env, stateObj)) {
2367                             return false;
2368                         }
2369                         break;
2370
2371                     case TdsEnums.ENV_CHARSET:
2372                         // we copied this behavior directly from luxor - see charset envchange
2373                         // section from sqlctokn.c
2374                         Debug.Assert(!_isShiloh, "Received ENV_CHARSET on non 7.0 server!");
2375                         if (!TryReadTwoStringFields(env, stateObj)) {
2376                             return false;
2377                         }
2378                         if (env.newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) {
2379                             _defaultCodePage = TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE;
2380                             _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
2381                         }
2382                         else {
2383                             Debug.Assert(env.newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10");
2384
2385                             string stringCodePage = env.newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET);
2386
2387                             _defaultCodePage = Int32.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture);
2388                             _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
2389                         }
2390
2391                         break;
2392
2393                     case TdsEnums.ENV_PACKETSIZE:
2394                         // take care of packet size right here
2395                         Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
2396                         if (!TryReadTwoStringFields(env, stateObj)) {
2397                             // Changing packet size does not support retry, should not pend"
2398                             throw SQL.SynchronousCallMayNotPend(); 
2399                         }
2400                         // Only set on physical state object - this should only occur on LoginAck prior
2401                         // to MARS initialization!
2402                         Int32 packetSize = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
2403
2404                         if (_physicalStateObj.SetPacketSize(packetSize)) {
2405                             // If packet size changed, we need to release our SNIPackets since
2406                             // those are tied to packet size of connection.
2407                             _physicalStateObj.ClearAllWritePackets();
2408
2409                             // Update SNI ConsumerInfo value to be resulting packet size
2410                             UInt32 unsignedPacketSize = (UInt32) packetSize;
2411                             UInt32 result = SNINativeMethodWrapper.SNISetInfo(_physicalStateObj.Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize);
2412
2413                             Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SNISetInfo");
2414                         }
2415
2416                         break;
2417
2418                     case TdsEnums.ENV_LOCALEID:
2419                         // 
2420
2421                         if (!TryReadTwoStringFields(env, stateObj)) {
2422                             return false;
2423                         }
2424                         _defaultLCID = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
2425                         break;
2426
2427                     case TdsEnums.ENV_COMPFLAGS:
2428                         if (!TryReadTwoStringFields(env, stateObj)) {
2429                             return false;
2430                         }
2431                         break;
2432
2433                     case TdsEnums.ENV_COLLATION:
2434                         Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!");
2435                         if (!stateObj.TryReadByte(out byteLength)) {
2436                             return false;
2437                         }
2438                         env.newLength = byteLength;
2439                         if (env.newLength == 5) {
2440                             if (!TryProcessCollation(stateObj, out env.newCollation)) {
2441                                 return false;
2442                             }
2443
2444                             // give the parser the new collation values in case parameters don't specify one
2445                             _defaultCollation = env.newCollation;
2446                             int newCodePage = GetCodePage(env.newCollation, stateObj);
2447                             if (newCodePage != _defaultCodePage) {
2448                                 _defaultCodePage = newCodePage;
2449                                 _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage);
2450                             }
2451                             _defaultLCID = env.newCollation.LCID;
2452                         }
2453
2454                         if (!stateObj.TryReadByte(out byteLength)) {
2455                             return false;
2456                         }
2457                         env.oldLength = byteLength;
2458                         Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!");
2459                         if (env.oldLength == 5) {
2460                             if (!TryProcessCollation(stateObj, out env.oldCollation)) {
2461                                 return false;
2462                             }
2463                         }
2464
2465                         env.length = 3 + env.newLength + env.oldLength;
2466                         break;
2467
2468                     case TdsEnums.ENV_BEGINTRAN:
2469                     case TdsEnums.ENV_COMMITTRAN:
2470                     case TdsEnums.ENV_ROLLBACKTRAN:
2471                     case TdsEnums.ENV_ENLISTDTC:
2472                     case TdsEnums.ENV_DEFECTDTC:
2473                     case TdsEnums.ENV_TRANSACTIONENDED:
2474                         Debug.Assert(_isYukon, "Received new ENVCHANGE transaction/DTC token on pre 9.0 server!");
2475
2476                         if (!stateObj.TryReadByte(out byteLength)) {
2477                             return false;
2478                         }
2479                         env.newLength = byteLength;
2480                         Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!");
2481
2482                         if (env.newLength > 0) {
2483                             if (!stateObj.TryReadInt64(out env.newLongValue)) {
2484                                 return false;
2485                             }
2486                             Debug.Assert(env.newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
2487                         }
2488                         else {
2489                             env.newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id.
2490                         }
2491                         
2492                         if (!stateObj.TryReadByte(out byteLength)) {
2493                             return false;
2494                         }
2495                         env.oldLength = byteLength;
2496                         Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!");
2497
2498                         if (env.oldLength > 0) {
2499                             if (!stateObj.TryReadInt64(out env.oldLongValue)) {
2500                                 return false;
2501                             }
2502                             Debug.Assert(env.oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
2503                         }
2504                         else {
2505                             env.oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id.
2506                         }
2507
2508                         // env.length includes 1 byte type token
2509                         env.length = 3 + env.newLength + env.oldLength;
2510                         break;
2511
2512                     case TdsEnums.ENV_LOGSHIPNODE:
2513                         // env.newBinValue is secondary node, env.oldBinValue is witness node
2514                         // comes before LoginAck so we can't assert this
2515                         if (!TryReadTwoStringFields(env, stateObj)) {
2516                             return false;
2517                         }
2518                         break;
2519
2520                     case TdsEnums.ENV_PROMOTETRANSACTION:
2521                         Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!");
2522
2523                         if (!stateObj.TryReadInt32(out env.newLength)) { // new value has 4 byte length
2524                             return false;
2525                         }
2526                         env.newBinValue = new byte[env.newLength];
2527                         if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) { // read new value with 4 byte length
2528                             return false;
2529                         }
2530
2531                         if (!stateObj.TryReadByte(out byteLength)) {
2532                             return false;
2533                         }
2534                         env.oldLength = byteLength;
2535                         Debug.Assert(0 == env.oldLength, "old length should be zero");
2536
2537                         // env.length includes 1 byte for type token
2538                         env.length = 5 + env.newLength;
2539                         break;
2540
2541                     case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS:
2542                     case TdsEnums.ENV_SPRESETCONNECTIONACK:
2543                         // 
2544                         Debug.Assert(_isYukon, "Received new ENVCHANGE tokens on pre 9.0 server!");
2545                         if (!TryReadTwoBinaryFields(env, stateObj)) {
2546                             return false;
2547                         }
2548                         break;
2549                         
2550                     case TdsEnums.ENV_USERINSTANCE:
2551                         Debug.Assert(!_isYukon, "Received ENV_USERINSTANCE on non 9.0 server!");
2552                         if (!TryReadTwoStringFields(env, stateObj)) {
2553                             return false;
2554                         }
2555                         break;
2556                         
2557                     case TdsEnums.ENV_ROUTING:
2558                         ushort newLength;
2559                         if (!stateObj.TryReadUInt16(out newLength)) {
2560                             return false;
2561                         }
2562                         env.newLength = newLength;
2563                         byte protocol;
2564                         if (!stateObj.TryReadByte(out protocol)) {
2565                             return false;
2566                         }
2567                         ushort port;
2568                         if (!stateObj.TryReadUInt16(out port)) {
2569                             return false;
2570                         }
2571                         UInt16 serverLen;
2572                         if (!stateObj.TryReadUInt16(out serverLen)) {
2573                             return false;
2574                         }
2575                         string serverName;
2576                         if (!stateObj.TryReadString(serverLen, out serverName)) {
2577                             return false;
2578                         }
2579                         env.newRoutingInfo = new RoutingInfo(protocol, port, serverName);
2580                         UInt16 oldLength;
2581                         if (!stateObj.TryReadUInt16(out oldLength)) {
2582                             return false;
2583                         }
2584                         if (!stateObj.TrySkipBytes(oldLength)) {
2585                             return false;
2586                         }
2587                         env.length = env.newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
2588                         break;                        
2589
2590                     default:
2591                         Debug.Assert(false, "Unknown environment change token: " + env.type);
2592                         break;
2593                 }
2594                 processedLength += env.length;
2595             }
2596
2597             sqlEnvChange = envarray;
2598             return true;
2599         }
2600
2601         private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject stateObj) {
2602             // Used by ProcessEnvChangeToken
2603             byte byteLength;
2604             if (!stateObj.TryReadByte(out byteLength)) {
2605                 return false;
2606             }
2607             env.newLength = byteLength;
2608             env.newBinValue = new byte[env.newLength];
2609             if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) {
2610                 return false;
2611             }
2612             if (!stateObj.TryReadByte(out byteLength)) {
2613                 return false;
2614             }
2615             env.oldLength = byteLength;
2616             env.oldBinValue = new byte[env.oldLength];
2617             if (!stateObj.TryReadByteArray(env.oldBinValue, 0, env.oldLength)) {
2618                 return false;
2619             }
2620
2621             // env.length includes 1 byte type token
2622             env.length = 3 + env.newLength + env.oldLength;
2623             return true;
2624         }
2625
2626         private bool TryReadTwoStringFields(SqlEnvChange env, TdsParserStateObject stateObj) {
2627             // Used by ProcessEnvChangeToken
2628             byte newLength, oldLength;
2629             string newValue, oldValue;
2630             if (!stateObj.TryReadByte(out newLength)) {
2631                 return false;
2632             }
2633             if (!stateObj.TryReadString(newLength, out newValue)) {
2634                 return false;
2635             }
2636             if (!stateObj.TryReadByte(out oldLength)) {
2637                 return false;
2638             }
2639             if (!stateObj.TryReadString(oldLength, out oldValue)) {
2640                 return false;
2641             }
2642             
2643             env.newLength = newLength;
2644             env.newValue = newValue;
2645             env.oldLength = oldLength;
2646             env.oldValue = oldValue;
2647
2648             // env.length includes 1 byte type token
2649             env.length = 3 + env.newLength * 2 + env.oldLength * 2;
2650             return true;
2651         }
2652
2653         private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavior run, TdsParserStateObject stateObj) {
2654             ushort curCmd;
2655             ushort status;
2656             int count;
2657
2658             // Can't retry TryProcessDone
2659             stateObj._syncOverAsync = true;
2660
2661             // status
2662             // command
2663             // rowcount (valid only if DONE_COUNT bit is set)
2664
2665             if (!stateObj.TryReadUInt16(out status)) {
2666                 return false;
2667             }
2668             if (!stateObj.TryReadUInt16(out curCmd)) {
2669                 return false;
2670             }
2671
2672             if (_isYukon) {
2673                 long longCount;
2674                 if (!stateObj.TryReadInt64(out longCount)) {
2675                     return false;
2676                 }
2677                 count = (int) longCount;
2678             }
2679             else {
2680                 if (!stateObj.TryReadInt32(out count)) {
2681                     return false;
2682                 }
2683                 // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
2684                 // In that case we still have to read another 4 bytes
2685                 // But don't try to read beyond the TDS stream in this case, because it generates errors if login failed.
2686                 if ( _state == TdsParserState.OpenNotLoggedIn) {
2687                     // Login incomplete, if we are reading from Yukon we need to read another int
2688                     if (stateObj._inBytesRead > stateObj._inBytesUsed) {
2689                         byte b;
2690                         if (!stateObj.TryPeekByte(out b)) {
2691                             return false;
2692                         }
2693                         if (b == 0) {
2694                             // This is an invalid token value
2695                             if (!stateObj.TryReadInt32(out count)) {
2696                                 return false;
2697                             }
2698                         }
2699                     }
2700                 }
2701             }
2702
2703             // We get a done token with the attention bit set
2704             if (TdsEnums.DONE_ATTN == (status & TdsEnums.DONE_ATTN)) {
2705                 Debug.Assert(TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE),"Not expecting DONE_MORE when receiving DONE_ATTN");
2706                 Debug.Assert(stateObj._attentionSent, "Received attention done without sending one!");
2707                 stateObj._attentionReceived = true;
2708                 Debug.Assert(stateObj._inBytesUsed == stateObj._inBytesRead && stateObj._inBytesPacket == 0, "DONE_ATTN received with more data left on wire");
2709             }
2710             if ((null != cmd) && (TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) {
2711                 if (curCmd != TdsEnums.SELECT) {
2712                     if (cmd.IsDescribeParameterEncryptionRPCCurrentlyInProgress) {
2713                         // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
2714                         cmd.RowsAffectedByDescribeParameterEncryption = count;
2715                     }
2716                     else {
2717                         cmd.InternalRecordsAffected = count;
2718                     }
2719                 }
2720                 // Skip the bogus DONE counts sent by the server
2721                 if (stateObj._receivedColMetaData || (curCmd != TdsEnums.SELECT)) {
2722                     cmd.OnStatementCompleted(count);
2723                 }
2724             }
2725
2726             stateObj._receivedColMetaData = false;
2727
2728             // Surface exception for DONE_ERROR in the case we did not receive an error token
2729             // in the stream, but an error occurred.  In these cases, we throw a general server error.  The
2730             // situations where this can occur are: an invalid buffer received from client, login error
2731             // and the server refused our connection, and the case where we are trying to log in but
2732             // the server has reached its max connection limit.  Bottom line, we need to throw general
2733             // error in the cases where we did not receive a error token along with the DONE_ERROR.
2734             if ((TdsEnums.DONE_ERROR == (TdsEnums.DONE_ERROR & status)) && stateObj.ErrorCount == 0 &&
2735                   stateObj._errorTokenReceived == false && (RunBehavior.Clean != (RunBehavior.Clean & run))) {
2736                 stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0));
2737
2738                 if (null != reader) { // SQL BU DT 269516
2739                     if (!reader.IsInitialized) {
2740                         run = RunBehavior.UntilDone;
2741                     }
2742                 }
2743              }
2744
2745             // Similar to above, only with a more severe error.  In this case, if we received
2746             // the done_srverror, this exception will be added to the collection regardless.
2747             // MDAC #93896.  Also, per Ashwin, the server will always break the connection in this case.
2748             if ((TdsEnums.DONE_SRVERROR == (TdsEnums.DONE_SRVERROR & status)) && (RunBehavior.Clean != (RunBehavior.Clean & run))) {
2749                 stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0));
2750
2751                 if (null != reader) { // SQL BU DT 269516
2752                     if (!reader.IsInitialized) {
2753                         run = RunBehavior.UntilDone;
2754                     }
2755                 }
2756             }
2757
2758             ProcessSqlStatistics(curCmd, status, count);
2759
2760             // stop if the DONE_MORE bit isn't set (see above for attention handling)
2761             if (TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE)) {
2762                 stateObj._errorTokenReceived = false;
2763                 if (stateObj._inBytesUsed >= stateObj._inBytesRead) {
2764                     stateObj._pendingData = false;
2765                 }
2766             }
2767
2768             // _pendingData set by e.g. 'TdsExecuteSQLBatch'
2769             // _hasOpenResult always set to true by 'WriteMarsHeader'
2770             //
2771             if (!stateObj._pendingData && stateObj._hasOpenResult) {
2772 /*
2773                 Debug.Assert(!((sqlTransaction != null               && _distributedTransaction != null) ||
2774                                (_userStartedLocalTransaction != null && _distributedTransaction != null))
2775                               , "ProcessDone - have both distributed and local transactions not null!");
2776 */ // WebData 112722
2777
2778                 stateObj.DecrementOpenResultCount();
2779             }
2780
2781             return true;
2782         }
2783
2784         private void ProcessSqlStatistics(ushort curCmd, ushort status, int count) {
2785             // SqlStatistics bookkeeping stuff
2786             //
2787             if (null != _statistics) {
2788                 // any done after row(s) counts as a resultset
2789                 if (_statistics.WaitForDoneAfterRow) {
2790                     _statistics.SafeIncrement(ref _statistics._sumResultSets);
2791                     _statistics.WaitForDoneAfterRow = false;
2792                 }
2793
2794                 // clear row count DONE_COUNT flag is not set
2795                 if (!(TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) {
2796                     count = 0;
2797                 }
2798
2799                 switch (curCmd) {
2800                     case TdsEnums.INSERT:
2801                     case TdsEnums.DELETE:
2802                     case TdsEnums.UPDATE:
2803                     case TdsEnums.MERGE:
2804                         _statistics.SafeIncrement(ref _statistics._iduCount);
2805                         _statistics.SafeAdd(ref _statistics._iduRows, count);
2806                         if (!_statisticsIsInTransaction) {
2807                             _statistics.SafeIncrement(ref _statistics._transactions);
2808                         }
2809
2810                         break;
2811
2812                     case TdsEnums.SELECT:
2813                         _statistics.SafeIncrement(ref _statistics._selectCount);
2814                         _statistics.SafeAdd(ref _statistics._selectRows, count);
2815                         break;
2816
2817                     case TdsEnums.BEGINXACT:
2818                         if (!_statisticsIsInTransaction) {
2819                             _statistics.SafeIncrement(ref _statistics._transactions);
2820                         }
2821                         _statisticsIsInTransaction = true;
2822                         break;
2823
2824                     case TdsEnums.OPENCURSOR:
2825                         _statistics.SafeIncrement(ref _statistics._cursorOpens);
2826                         break;
2827
2828                     case TdsEnums.ABORT:
2829                         _statisticsIsInTransaction = false;
2830                         break;
2831
2832                     case TdsEnums.ENDXACT:
2833                         _statisticsIsInTransaction = false;
2834                         break;
2835                 } // switch
2836             }
2837             else {
2838                 switch (curCmd) {
2839                     case TdsEnums.BEGINXACT:
2840                         _statisticsIsInTransaction = true;
2841                         break;
2842
2843                     case TdsEnums.ABORT:
2844                     case TdsEnums.ENDXACT:
2845                         _statisticsIsInTransaction = false;
2846                         break;
2847                 }
2848             }
2849         }
2850
2851         private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) {
2852             // read feature ID
2853             byte featureId;
2854             do {
2855                 if (!stateObj.TryReadByte(out featureId)) {
2856                     return false;
2857                 }
2858                 if (featureId != TdsEnums.FEATUREEXT_TERMINATOR) {
2859                     UInt32 dataLen;
2860                     if (!stateObj.TryReadUInt32(out dataLen)) {
2861                         return false;
2862                     }                    
2863                     byte[] data = new byte[dataLen];
2864                     if (dataLen > 0) {
2865                         if (!stateObj.TryReadByteArray(data, 0, checked ((int)dataLen))) {
2866                             return false;
2867                         }
2868                     }
2869                     _connHandler.OnFeatureExtAck(featureId, data);
2870                 }
2871             } while (featureId != TdsEnums.FEATUREEXT_TERMINATOR);
2872
2873             // Check if column encryption was on and feature wasn't acknowledged.
2874             if (_connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled && !IsColumnEncryptionSupported) {
2875                 throw SQL.TceNotSupported ();
2876             }
2877
2878             return true;
2879         }
2880
2881         private bool TryProcessSessionState(TdsParserStateObject stateObj, int length, SessionData sdata) {
2882             if (length < 5) {
2883                 throw SQL.ParsingErrorLength(ParsingErrorState.SessionStateLengthTooShort, length);
2884             }
2885             UInt32 seqNum;
2886             if (!stateObj.TryReadUInt32(out seqNum)) {
2887                 return false;
2888             }
2889             if (seqNum == UInt32.MaxValue) {
2890                 _connHandler.DoNotPoolThisConnection();
2891             }
2892             byte status;
2893             if (!stateObj.TryReadByte(out status)) {
2894                 return false;
2895             }
2896             if (status > 1) {
2897                 throw SQL.ParsingErrorStatus(ParsingErrorState.SessionStateInvalidStatus, status);
2898             }
2899             bool recoverable = status != 0;
2900             length -= 5;           
2901             while (length > 0) {
2902                 byte stateId;
2903                 if (!stateObj.TryReadByte(out stateId)) {
2904                     return false;
2905                 }
2906                 int stateLen;
2907                 byte stateLenByte;
2908                 if (!stateObj.TryReadByte(out stateLenByte)) {
2909                     return false;
2910                 }
2911                 if (stateLenByte < 0xFF) {
2912                     stateLen = stateLenByte;
2913                 }
2914                 else {
2915                     if (!stateObj.TryReadInt32(out stateLen)) {
2916                         return false;
2917                     }
2918                 }
2919                 byte[] buffer = null;
2920                 lock (sdata._delta) {
2921                     if (sdata._delta[stateId] == null) {
2922                         buffer = new byte[stateLen];
2923                         sdata._delta[stateId] = new SessionStateRecord { _version = seqNum, _dataLength = stateLen, _data = buffer, _recoverable = recoverable };
2924                         sdata._deltaDirty = true;
2925                         if (!recoverable) {
2926                             checked { sdata._unrecoverableStatesCount++; }
2927                         }
2928                     }
2929                     else {
2930                         if (sdata._delta[stateId]._version <= seqNum) {
2931                             SessionStateRecord sv = sdata._delta[stateId];
2932                             sv._version = seqNum;
2933                             sv._dataLength = stateLen;
2934                             if (sv._recoverable != recoverable) {
2935                                 if (recoverable) {
2936                                     Debug.Assert(sdata._unrecoverableStatesCount > 0, "Unrecoverable states count >0");
2937                                     sdata._unrecoverableStatesCount--;
2938                                 }
2939                                 else {
2940                                     checked { sdata._unrecoverableStatesCount++; }
2941                                 }
2942                                 sv._recoverable = recoverable;
2943                             }
2944                             buffer = sv._data;
2945                             if (buffer.Length < stateLen) {
2946                                 buffer = new byte[stateLen];
2947                                 sv._data = buffer;
2948                             }
2949                         }
2950                     }
2951                 }
2952                 if (buffer != null) {
2953                     if (!stateObj.TryReadByteArray(buffer, 0, stateLen)) {
2954                         return false;
2955                     }
2956                 }
2957                 else {
2958                     if (!stateObj.TrySkipBytes(stateLen))
2959                         return false;
2960                 }
2961
2962                 if (stateLenByte < 0xFF) {
2963                     length -= 2 + stateLen;
2964                 }
2965                 else {
2966                     length -= 6 + stateLen;
2967                 }
2968             }
2969             sdata.AssertUnrecoverableStateCountIsCorrect();
2970
2971             return true;
2972         }
2973
2974         private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck sqlLoginAck) {
2975             SqlLoginAck a = new SqlLoginAck();
2976
2977             sqlLoginAck = null;
2978
2979             // read past interface type and version
2980             if (!stateObj.TrySkipBytes(1)) {
2981                 return false;
2982             }
2983
2984             byte[] b = new byte[TdsEnums.VERSION_SIZE];
2985             if (!stateObj.TryReadByteArray(b, 0, b.Length)) {
2986                 return false;
2987             }
2988             a.tdsVersion = (UInt32)((((((b[0]<<8)|b[1])<<8)|b[2])<<8)|b[3]); // bytes are in motorola order (high byte first)
2989             UInt32 majorMinor = a.tdsVersion & 0xff00ffff;
2990             UInt32 increment  = (a.tdsVersion >> 16) & 0xff;
2991
2992             // Server responds:
2993             // 0x07000000 -> Sphinx         // Notice server response format is different for bwd compat
2994             // 0x07010000 -> Shiloh RTM     // Notice server response format is different for bwd compat
2995             // 0x71000001 -> Shiloh SP1
2996             // 0x72xx0002 -> Yukon RTM
2997             // information provided by S. Ashwin
2998
2999             switch (majorMinor) {
3000                 case TdsEnums.SPHINXORSHILOH_MAJOR<<24|TdsEnums.DEFAULT_MINOR:    // Sphinx & Shiloh RTM
3001                     // note that sphinx and shiloh_rtm can only be distinguished by the increment
3002                     switch (increment) {
3003                         case TdsEnums.SHILOH_INCREMENT:
3004                             _isShiloh = true;
3005                             break;
3006                         case TdsEnums.SPHINX_INCREMENT:
3007                             // no flag will be set
3008                             break;
3009                         default:
3010                             throw SQL.InvalidTDSVersion();
3011                     }
3012                     break;
3013                 case TdsEnums.SHILOHSP1_MAJOR<<24|TdsEnums.SHILOHSP1_MINOR: // Shiloh SP1
3014                     if (increment != TdsEnums.SHILOHSP1_INCREMENT) { throw SQL.InvalidTDSVersion(); }
3015                     _isShilohSP1 = true;
3016                     break;
3017                 case TdsEnums.YUKON_MAJOR<<24|TdsEnums.YUKON_RTM_MINOR:     // Yukon
3018                     if (increment != TdsEnums.YUKON_INCREMENT) { throw SQL.InvalidTDSVersion(); }
3019                     _isYukon = true;
3020                     break;
3021                 case TdsEnums.KATMAI_MAJOR<<24|TdsEnums.KATMAI_MINOR:
3022                     if (increment != TdsEnums.KATMAI_INCREMENT) { throw SQL.InvalidTDSVersion(); }
3023                     _isKatmai = true;
3024                     break;
3025                 case TdsEnums.DENALI_MAJOR << 24|TdsEnums.DENALI_MINOR:
3026                     if (increment != TdsEnums.DENALI_INCREMENT) { throw SQL.InvalidTDSVersion(); }
3027                     _isDenali = true;
3028                     break;
3029                 default:
3030                     throw SQL.InvalidTDSVersion();
3031             }
3032
3033             _isKatmai       |= _isDenali;
3034             _isYukon        |= _isKatmai;
3035             _isShilohSP1    |= _isYukon;            // includes all lower versions
3036             _isShiloh       |= _isShilohSP1;        //
3037
3038             a.isVersion8 = _isShiloh;
3039
3040             stateObj._outBytesUsed = stateObj._outputHeaderLen;
3041             byte len;
3042             if (!stateObj.TryReadByte(out len)) {
3043                 return false;
3044             }
3045
3046             if (!stateObj.TryReadString(len, out a.programName)) {
3047                 return false;
3048             }
3049             if (!stateObj.TryReadByte(out a.majorVersion)) {
3050                 return false;
3051             }
3052             if (!stateObj.TryReadByte(out a.minorVersion)) {
3053                 return false;
3054             }
3055             byte buildNumHi, buildNumLo;
3056             if (!stateObj.TryReadByte(out buildNumHi)) {
3057                 return false;
3058             }
3059             if (!stateObj.TryReadByte(out buildNumLo)) {
3060                 return false;
3061             }
3062
3063             a.buildNum = (short)((buildNumHi << 8) + buildNumLo);
3064
3065             Debug.Assert(_state == TdsParserState.OpenNotLoggedIn, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn");
3066             _state = TdsParserState.OpenLoggedIn;
3067
3068             if (_isYukon) {
3069                 if (_fMARS) {
3070                     _resetConnectionEvent = new AutoResetEvent(true);
3071                 }
3072             }
3073
3074             // Fail if SSE UserInstance and we have not received this info.
3075             if ( _connHandler.ConnectionOptions.UserInstance && 
3076                 ADP.IsEmpty(_connHandler.InstanceName)) {
3077                 stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, Server, SQLMessage.UserInstanceFailure(), "", 0));
3078                 ThrowExceptionAndWarning(stateObj);
3079             }
3080
3081             sqlLoginAck = a;
3082             return true;
3083         }
3084
3085         private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, out SqlFedAuthInfo sqlFedAuthInfo) {
3086             sqlFedAuthInfo = null;
3087             SqlFedAuthInfo tempFedAuthInfo = new SqlFedAuthInfo();
3088
3089             // Skip reading token length, since it has already been read in caller
3090
3091             if (Bid.AdvancedOn) {
3092                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FEDAUTHINFO token stream length = {0}\n", tokenLen);
3093             }
3094
3095             if (tokenLen < sizeof(uint)) {
3096                 // the token must at least contain a DWORD indicating the number of info IDs
3097                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream length too short for CountOfInfoIDs.\n");
3098                 throw SQL.ParsingErrorLength(ParsingErrorState.FedAuthInfoLengthTooShortForCountOfInfoIds, tokenLen);
3099             }
3100
3101             // read how many FedAuthInfo options there are
3102             uint optionsCount;
3103             if (!stateObj.TryReadUInt32(out optionsCount)) {
3104                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read CountOfInfoIDs in FEDAUTHINFO token stream.\n");
3105                 throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadCountOfInfoIds);
3106             }
3107             tokenLen -= sizeof(uint); // remaining length is shortened since we read optCount
3108
3109             if (Bid.AdvancedOn) {
3110                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> CountOfInfoIDs = {0}\n", optionsCount.ToString(CultureInfo.InvariantCulture));
3111             }
3112
3113             if (tokenLen > 0) {
3114                 // read the rest of the token
3115                 byte[] tokenData = new byte[tokenLen];
3116                 int totalRead = 0;
3117                 bool successfulRead = stateObj.TryReadByteArray(tokenData, 0, tokenLen, out totalRead);
3118
3119                 if (Bid.AdvancedOn) {
3120                     Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Read rest of FEDAUTHINFO token stream: {0}\n", BitConverter.ToString(tokenData, 0, totalRead));
3121                 }
3122
3123                 if (!successfulRead || totalRead != tokenLen) {
3124                     Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read FEDAUTHINFO token stream. Attempted to read {0} bytes, actually read {1}\n", tokenLen, totalRead);
3125                     throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadTokenStream);
3126                 }
3127
3128                 // each FedAuthInfoOpt is 9 bytes:
3129                 //    1 byte for FedAuthInfoID
3130                 //    4 bytes for FedAuthInfoDataLen
3131                 //    4 bytes for FedAuthInfoDataOffset
3132                 // So this is the index in tokenData for the i-th option
3133                 const uint optionSize = 9;
3134
3135                 // the total number of bytes for all FedAuthInfoOpts together
3136                 uint totalOptionsSize = checked(optionsCount * optionSize);
3137
3138                 for (uint i = 0; i < optionsCount; i++) {
3139                     uint currentOptionOffset = checked(i * optionSize);
3140
3141                     byte id = tokenData[currentOptionOffset];
3142                     uint dataLen = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 1)));
3143                     uint dataOffset = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 5)));
3144
3145                     if (Bid.AdvancedOn) {
3146                         Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}\n", id, dataLen.ToString(CultureInfo.InvariantCulture), dataOffset.ToString(CultureInfo.InvariantCulture));
3147                     }
3148
3149                     // offset is measured from optCount, so subtract to make offset measured
3150                     // from the beginning of tokenData
3151                     checked {
3152                         dataOffset -= sizeof(uint);
3153                     }
3154
3155                     // if dataOffset points to a region within FedAuthInfoOpt or after the end of the token, throw
3156                     if (dataOffset < totalOptionsSize || dataOffset >= tokenLen) {
3157                         Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FedAuthInfoDataOffset points to an invalid location.\n");
3158                         throw SQL.ParsingErrorOffset(ParsingErrorState.FedAuthInfoInvalidOffset, unchecked((int)dataOffset));
3159                     }
3160
3161                     // try to read data and throw if the arguments are bad, meaning the server sent us a bad token
3162                     string data;
3163                     try {
3164                         data = System.Text.Encoding.Unicode.GetString(tokenData, checked((int)dataOffset), checked((int)dataLen));
3165                     }
3166                     catch (ArgumentOutOfRangeException e) {
3167                         Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read FedAuthInfoData.\n");
3168                         throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadData, e);
3169                     }
3170                     catch (ArgumentException e) {
3171                         Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FedAuthInfoData is not in unicode format.\n");
3172                         throw SQL.ParsingError(ParsingErrorState.FedAuthInfoDataNotUnicode, e);
3173                     }
3174
3175                     if (Bid.AdvancedOn) {
3176                         Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoData: {0}\n", data);
3177                     }
3178
3179                     // store data in tempFedAuthInfo
3180                     switch ((TdsEnums.FedAuthInfoId)id) {
3181                         case TdsEnums.FedAuthInfoId.Spn:
3182                             tempFedAuthInfo.spn = data;
3183                             break;
3184                         case TdsEnums.FedAuthInfoId.Stsurl:
3185                             tempFedAuthInfo.stsurl = data;
3186                             break;
3187                         default:
3188                             if (Bid.AdvancedOn) {
3189                                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Ignoring unknown federated authentication info option: {0}\n", id);
3190                             }
3191                             break;
3192                     }
3193                 }
3194             }
3195             else {
3196                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream is not long enough to contain the data it claims to.\n");
3197                 throw SQL.ParsingErrorLength(ParsingErrorState.FedAuthInfoLengthTooShortForData, tokenLen);
3198             }
3199
3200             Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Processed FEDAUTHINFO token stream: {0}\n", tempFedAuthInfo.ToString());
3201
3202             if (String.IsNullOrWhiteSpace(tempFedAuthInfo.stsurl) || String.IsNullOrWhiteSpace(tempFedAuthInfo.spn)) {
3203                 // We should be receiving both stsurl and spn
3204                 Bid.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream does not contain both STSURL and SPN.\n");
3205                 throw SQL.ParsingError(ParsingErrorState.FedAuthInfoDoesNotContainStsurlAndSpn);
3206             }
3207
3208             sqlFedAuthInfo = tempFedAuthInfo;
3209             return true;
3210         }
3211
3212         internal bool TryProcessError(byte token, TdsParserStateObject stateObj, out SqlError error) {
3213             ushort shortLen;
3214             byte byteLen;
3215             int number;
3216             byte state;
3217             byte errorClass;
3218
3219             error = null;
3220
3221             if (!stateObj.TryReadInt32(out number)) {
3222                 return false;
3223             }
3224             if (!stateObj.TryReadByte(out state)) {
3225                 return false;
3226             }
3227             if (!stateObj.TryReadByte(out errorClass)) {
3228                 return false;
3229             }
3230
3231             Debug.Assert(((errorClass >= TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLERROR) ||
3232                           ((errorClass < TdsEnums.MIN_ERROR_CLASS) && token == TdsEnums.SQLINFO), "class and token don't match!");
3233
3234             if (!stateObj.TryReadUInt16(out shortLen)) {
3235                 return false;
3236             }
3237             string message;
3238             if (!stateObj.TryReadString(shortLen, out message)) {
3239                 return false;
3240             }
3241
3242             if (!stateObj.TryReadByte(out byteLen)) {
3243                 return false;
3244             }
3245
3246             string server;
3247
3248             // MDAC bug #49307 - server sometimes does not send over server field! In those cases
3249             // we will use our locally cached value.
3250             if (byteLen == 0) {
3251                 server = _server;
3252             }
3253             else {
3254                 if (!stateObj.TryReadString(byteLen, out server)) {
3255                     return false;
3256                 }
3257             }
3258
3259             if (!stateObj.TryReadByte(out byteLen)) {
3260                 return false;
3261             }
3262             string procedure;
3263             if (!stateObj.TryReadString(byteLen, out procedure)) {
3264                 return false;
3265             }
3266
3267             int line;
3268             if (_isYukon) {
3269                 if (!stateObj.TryReadInt32(out line)) {
3270                     return false;
3271                 }
3272             }
3273             else {
3274                 ushort shortLine;
3275                 if (!stateObj.TryReadUInt16(out shortLine)) {
3276                     return false;
3277                 }
3278                 line = shortLine;
3279                 // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
3280                 // In that case we still have to read another 2 bytes
3281                 if ( _state == TdsParserState.OpenNotLoggedIn) {
3282                     // Login incomplete
3283                     byte b;
3284                     if (!stateObj.TryPeekByte(out b)) {
3285                         return false;
3286                     }
3287                     if (b == 0) {
3288                         // This is an invalid token value
3289                         ushort value;
3290                         if (!stateObj.TryReadUInt16(out value)) {
3291                             return false;
3292                         }
3293                         line = (line << 16) + value;
3294                     }
3295                 }
3296             }
3297
3298             error = new SqlError(number, state, errorClass, _server, message, procedure, line);
3299             return true;
3300         }
3301
3302
3303         internal bool TryProcessReturnValue(int length,
3304                                             TdsParserStateObject stateObj,
3305                                             out SqlReturnValue returnValue,
3306                                             SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
3307             returnValue = null;
3308             SqlReturnValue rec = new SqlReturnValue();
3309             rec.length = length;        // In Yukon this length is -1
3310             if (_isYukon) {
3311                 if (!stateObj.TryReadUInt16(out rec.parmIndex)) {
3312                     return false;
3313                 }
3314             }
3315             byte len;
3316             if (!stateObj.TryReadByte(out len)) { // Length of parameter name
3317                 return false;
3318             }
3319
3320             rec.parameter = null;
3321             if (len > 0) {
3322                 if (!stateObj.TryReadString(len, out rec.parameter)) {
3323                     return false;
3324                 }
3325             }
3326
3327             // read status and ignore
3328             byte ignored;
3329             if (!stateObj.TryReadByte(out ignored)) {
3330                 return false;
3331             }
3332
3333             UInt32 userType;
3334
3335             // read user type - 4 bytes Yukon, 2 backwards
3336             if (IsYukonOrNewer) {
3337                 if (!stateObj.TryReadUInt32(out userType)) {
3338                     return false;
3339                 }
3340             }
3341             else {
3342                 ushort userTypeShort;
3343                 if (!stateObj.TryReadUInt16(out userTypeShort)) {
3344                     return false;
3345                 }
3346                 userType = userTypeShort;
3347             }
3348
3349             // Read off the flags.
3350             // The first byte is ignored since it doesn't contain any interesting information.
3351             byte flags;
3352             if (!stateObj.TryReadByte(out flags)) {
3353                 return false;
3354             }
3355
3356             if (!stateObj.TryReadByte(out flags)) {
3357                 return false;
3358             }
3359
3360             // Check if the column is encrypted.
3361             if (_serverSupportsColumnEncryption) {
3362                 rec.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted));
3363             }
3364
3365             // read the type
3366             byte tdsType;
3367             if (!stateObj.TryReadByte(out tdsType)) {
3368                 return false;
3369             }
3370
3371             // read the MaxLen
3372             // For xml datatpyes, there is no tokenLength
3373             int tdsLen;
3374
3375             if (tdsType == TdsEnums.SQLXMLTYPE) {
3376                 tdsLen = TdsEnums.SQL_USHORTVARMAXLEN;
3377             }
3378             else if (IsVarTimeTds(tdsType))
3379                 tdsLen = 0;  // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
3380             else if (tdsType == TdsEnums.SQLDATE) {
3381                 tdsLen = 3;
3382             }
3383             else {
3384                 if (!TryGetTokenLength(tdsType, stateObj, out tdsLen)) {
3385                     return false;
3386                 }
3387             }
3388
3389             rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen);
3390             rec.type = rec.metaType.SqlDbType;
3391
3392             // always use the nullable type for parameters if Shiloh or later
3393             // Sphinx sometimes sends fixed length return values
3394             if (_isShiloh) {
3395                 rec.tdsType = rec.metaType.NullableType;
3396                 rec.isNullable = true;
3397                 if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) {
3398                     Debug.Assert(_isYukon, "plp data from pre-Yukon server");
3399                     rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType);
3400                 }
3401             }
3402             else {      // For sphinx, keep the fixed type if that is what is returned
3403                 if (rec.metaType.NullableType == tdsType)
3404                     rec.isNullable = true;
3405
3406                 rec.tdsType = (byte)tdsType;
3407             }
3408
3409             if (rec.type == SqlDbType.Decimal) {
3410                 if (!stateObj.TryReadByte(out rec.precision)) {
3411                     return false;
3412                 }
3413                 if (!stateObj.TryReadByte(out rec.scale)) {
3414                     return false;
3415                 }
3416             }
3417
3418             if (rec.metaType.IsVarTime) {
3419                 if (!stateObj.TryReadByte(out rec.scale)) {
3420                     return false;
3421                 }
3422             }
3423
3424             if (tdsType == TdsEnums.SQLUDT) {
3425                 if (!TryProcessUDTMetaData((SqlMetaDataPriv) rec, stateObj)) {
3426                     return false;
3427                 }
3428             }
3429
3430             if (rec.type == SqlDbType.Xml) {
3431                 // Read schema info
3432                 byte schemapresent;
3433                 if (!stateObj.TryReadByte(out schemapresent)) {
3434                     return false;
3435                 }
3436
3437                 if ((schemapresent & 1) != 0) {
3438                     if (!stateObj.TryReadByte(out len)) {
3439                         return false;
3440                     }
3441                     if (len != 0) {
3442                         if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionDatabase)) {
3443                             return false;
3444                         }
3445                     }
3446
3447                     if (!stateObj.TryReadByte(out len)) {
3448                         return false;
3449                     }
3450                     if (len != 0) {
3451                         if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionOwningSchema)) {
3452                             return false;
3453                         }
3454                     }
3455
3456                     short slen;
3457                     if (!stateObj.TryReadInt16(out slen)) {
3458                         return false;
3459                     }
3460
3461                     if (slen != 0) {
3462                         if (!stateObj.TryReadString(slen, out rec.xmlSchemaCollectionName)) {
3463                             return false;
3464                         }
3465                     }
3466
3467                 }
3468             }
3469             else if (_isShiloh && rec.metaType.IsCharType) {
3470                 // read the collation for 8.x servers
3471                 if (!TryProcessCollation(stateObj, out rec.collation)) {
3472                     return false;
3473                 }
3474
3475                 int codePage = GetCodePage(rec.collation, stateObj);
3476
3477                 // if the column lcid is the same as the default, use the default encoder
3478                 if (codePage == _defaultCodePage) {
3479                     rec.codePage = _defaultCodePage;
3480                     rec.encoding = _defaultEncoding;
3481                 }
3482                 else {
3483                     rec.codePage = codePage;
3484                     rec.encoding = System.Text.Encoding.GetEncoding(rec.codePage);
3485                 }
3486             }
3487
3488             // For encrypted parameters, read the unencrypted type and encryption information.
3489             if (_serverSupportsColumnEncryption && rec.isEncrypted) {
3490                 if (!TryProcessTceCryptoMetadata(stateObj, rec, cipherTable: null, columnEncryptionSetting: columnEncryptionSetting, isReturnValue: true)) {
3491                     return false;
3492                 }
3493             }
3494
3495             // for now we coerce return values into a SQLVariant, not good...
3496             bool isNull = false;
3497             ulong valLen;
3498             if (!TryProcessColumnHeaderNoNBC(rec, stateObj, out isNull, out valLen)) {
3499                 return false;
3500             }
3501
3502             // always read as sql types
3503             Debug.Assert(valLen < (ulong)(Int32.MaxValue), "ProcessReturnValue received data size > 2Gb");
3504
3505             int intlen = valLen > (ulong)(Int32.MaxValue) ? Int32.MaxValue : (int)valLen;
3506
3507             if (rec.metaType.IsPlp) {
3508                 intlen = Int32.MaxValue;    // If plp data, read it all
3509             }
3510
3511             if (isNull) {
3512                 GetNullSqlValue(rec.value, rec, SqlCommandColumnEncryptionSetting.Disabled, _connHandler);
3513             }
3514             else {
3515                 // We should never do any decryption here, so pass disabled as the command encryption override.
3516                 // We only read the binary value and decryption will be performed by OnReturnValue().
3517                 if (!TryReadSqlValue(rec.value, rec, intlen, stateObj, SqlCommandColumnEncryptionSetting.Disabled, columnName:null /*Not used*/)) {
3518                     return false;
3519                 }
3520             }
3521
3522             returnValue = rec;
3523             return true;
3524         }
3525
3526         internal bool TryProcessTceCryptoMetadata (TdsParserStateObject stateObj, 
3527             SqlMetaDataPriv col, 
3528             SqlTceCipherInfoTable? cipherTable, 
3529             SqlCommandColumnEncryptionSetting columnEncryptionSetting,
3530             bool isReturnValue) {
3531             Debug.Assert(isReturnValue == (cipherTable == null), "Ciphertable is not set iff this is a return value");
3532
3533             // Read the ordinal into cipher table
3534             ushort index = 0;
3535             UInt32 userType;
3536
3537             // For return values there is not cipher table and no ordinal.
3538             if (cipherTable.HasValue) {
3539                 if (!stateObj.TryReadUInt16(out index)) {
3540                     return false;
3541                 }
3542
3543                 // validate the index (ordinal passed)
3544                 if (index >= cipherTable.Value.Size) {
3545                     Bid.Trace("<sc.TdsParser.TryProcessTceCryptoMetadata|TCE> Incorrect ordinal received %d, max tab size: %d\n", index, cipherTable.Value.Size);
3546                     throw SQL.ParsingErrorValue(ParsingErrorState.TceInvalidOrdinalIntoCipherInfoTable, index);
3547                 }
3548             }
3549
3550             // Read the user type
3551             if (!stateObj.TryReadUInt32(out userType)) {
3552                 return false;
3553             }
3554
3555             // Read the base TypeInfo
3556             col.baseTI = new SqlMetaDataPriv();
3557             if (!TryProcessTypeInfo(stateObj, col.baseTI, userType)) {
3558                 return false;
3559             }
3560
3561             // Read the cipher algorithm Id
3562             byte cipherAlgorithmId;
3563             if (!stateObj.TryReadByte(out cipherAlgorithmId)) {
3564                 return false;
3565             }
3566
3567             string cipherAlgorithmName = null;
3568             if (TdsEnums.CustomCipherAlgorithmId == cipherAlgorithmId) {
3569                 // Custom encryption algorithm, read the name
3570                 byte nameSize;
3571                 if (!stateObj.TryReadByte(out nameSize)) {
3572                     return false;
3573                 }
3574
3575                 if (!stateObj.TryReadString(nameSize, out cipherAlgorithmName)) {
3576                     return false;
3577                 }
3578             }
3579
3580             // Read Encryption Type. 
3581             byte encryptionType;
3582             if (!stateObj.TryReadByte(out encryptionType)) {
3583                 return false;
3584             }
3585
3586             // Read Normalization Rule Version.
3587             byte normalizationRuleVersion;
3588             if (!stateObj.TryReadByte(out normalizationRuleVersion)) {
3589                 return false;
3590             }
3591
3592             Debug.Assert(col.cipherMD == null, "col.cipherMD should be null in TryProcessTceCryptoMetadata.");
3593
3594             // Check if TCE is enable and if it is set the crypto MD for the column.
3595             // TCE is enabled if the command is set to enabled or to resultset only and this is not a return value
3596             // or if it is set to use connection setting and the connection has TCE enabled.
3597             if ((columnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled ||
3598                 (columnEncryptionSetting == SqlCommandColumnEncryptionSetting.ResultSetOnly && !isReturnValue)) ||
3599                 (columnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting &&
3600                 _connHandler != null && _connHandler.ConnectionOptions != null &&
3601                 _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled)) {
3602                 col.cipherMD = new SqlCipherMetadata(cipherTable.HasValue ? (SqlTceCipherInfoEntry?)cipherTable.Value[index] : null,
3603                                                         index,
3604                                                         cipherAlgorithmId: cipherAlgorithmId,
3605                                                         cipherAlgorithmName: cipherAlgorithmName,
3606                                                         encryptionType: encryptionType,
3607                                                         normalizationRuleVersion: normalizationRuleVersion);
3608             }
3609             else {
3610                 // If TCE is disabled mark the MD as not encrypted.
3611                 col.isEncrypted = false;
3612             }
3613
3614             return true;
3615         }
3616
3617         internal bool TryProcessCollation(TdsParserStateObject stateObj, out SqlCollation collation) {
3618             SqlCollation newCollation = new SqlCollation();
3619
3620             if (!stateObj.TryReadUInt32(out newCollation.info)) {
3621                 collation = null;
3622                 return false;
3623             }
3624             if (!stateObj.TryReadByte(out newCollation.sortId)) {
3625                 collation = null;
3626                 return false;
3627             }
3628
3629             collation = newCollation;
3630             return true;
3631         }
3632
3633         private void WriteCollation(SqlCollation collation, TdsParserStateObject stateObj) {
3634             if (collation == null) {
3635                 _physicalStateObj.WriteByte(0);
3636             }
3637             else {
3638                 _physicalStateObj.WriteByte(sizeof(UInt32)+sizeof(byte));
3639                 WriteUnsignedInt(collation.info, _physicalStateObj);
3640                 _physicalStateObj.WriteByte(collation.sortId);
3641             }
3642
3643         }
3644
3645         internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) {
3646             int codePage = 0;
3647
3648             if (0 != collation.sortId) {
3649                 codePage = TdsEnums.CODE_PAGE_FROM_SORT_ID[collation.sortId];
3650                 Debug.Assert(0 != codePage, "GetCodePage accessed codepage array and produced 0!, sortID =" + ((Byte)(collation.sortId)).ToString((IFormatProvider)null));
3651             }
3652             else {
3653                 int cultureId = collation.LCID;
3654                 bool success = false;
3655
3656                 try {
3657                     codePage = CultureInfo.GetCultureInfo(cultureId).TextInfo.ANSICodePage;
3658
3659                     // SqlHot 50001398: CodePage can be zero, but we should defer such errors until
3660                     //  we actually MUST use the code page (i.e. don't error if no ANSI data is sent).
3661                     success = true;
3662                 }
3663                 catch (ArgumentException e) {
3664                     ADP.TraceExceptionWithoutRethrow(e);
3665                 }
3666
3667                 // If we failed, it is quite possible this is because certain culture id's
3668                 // were removed in Win2k and beyond, however Sql Server still supports them.
3669                 // There is a workaround for the culture id's listed below, which is to mask
3670                 // off the sort id (the leading 1). If that fails, or we have a culture id
3671                 // other than the special cases below, we throw an error and throw away the
3672                 // rest of the results. For additional info, see MDAC 65963.
3673
3674                 // SqlHot 50001398: Sometimes GetCultureInfo will return CodePage 0 instead of throwing.
3675                 //  treat this as an error also, and switch into the special-case logic.
3676                 if (!success || codePage == 0) {
3677                     CultureInfo ci = null;
3678                     switch (cultureId) {
3679                         case 0x10404: // zh-TW
3680                         case 0x10804: // zh-CN
3681                         case 0x10c04: // zh-HK
3682                         case 0x11004: // zh-SG
3683                         case 0x11404: // zh-MO
3684                         case 0x10411: // ja-JP
3685                         case 0x10412: // ko-KR
3686                             // If one of the following special cases, mask out sortId and
3687                             // retry.
3688                             cultureId = cultureId & 0x03fff;
3689
3690                             try {
3691                                 ci = new CultureInfo(cultureId);
3692                                 success = true;
3693                             }
3694                             catch (ArgumentException e) {
3695                                 ADP.TraceExceptionWithoutRethrow(e);
3696                             }
3697                             break;
3698                         case 0x827:     // Non-supported Lithuanian code page, map it to supported Lithuanian.
3699                             try {
3700                                 ci = new CultureInfo(0x427);
3701                                 success = true;
3702                             }
3703                             catch (ArgumentException e) {
3704                                 ADP.TraceExceptionWithoutRethrow(e);
3705                             }
3706                             break;
3707                         default:
3708                             break;
3709                     }
3710
3711                     // I don't believe we should still be in failure case, but just in case.
3712                     if (!success) {
3713                         ThrowUnsupportedCollationEncountered(stateObj);
3714                     }
3715
3716                     if (null != ci) {
3717                         codePage = ci.TextInfo.ANSICodePage;
3718                     }
3719                 }
3720             }
3721
3722             return codePage;
3723         }
3724
3725
3726         internal void DrainData(TdsParserStateObject stateObj) {
3727             RuntimeHelpers.PrepareConstrainedRegions();
3728             try {
3729 #if DEBUG
3730                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3731
3732                 RuntimeHelpers.PrepareConstrainedRegions();
3733                 try {
3734                     tdsReliabilitySection.Start();
3735 #else
3736                   {
3737 #endif //DEBUG
3738                     try {
3739                         SqlDataReader.SharedState sharedState = stateObj._readerState;
3740                         if (sharedState != null && sharedState._dataReady) {
3741                             var metadata = stateObj._cleanupMetaData;
3742                             if (stateObj._partialHeaderBytesRead > 0) {
3743                                 if (!stateObj.TryProcessHeader()) {
3744                                     throw SQL.SynchronousCallMayNotPend();
3745                                 }
3746                             }
3747                             if (0 == sharedState._nextColumnHeaderToRead) {
3748                                 // i. user called read but didn't fetch anything
3749                                 if (!stateObj.Parser.TrySkipRow(stateObj._cleanupMetaData, stateObj)) {
3750                                     throw SQL.SynchronousCallMayNotPend();
3751                                 }
3752                             }
3753                             else {
3754                                 // iia.  if we still have bytes left from a partially read column, skip
3755                                 if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) {
3756                                     if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) {
3757                                         if (stateObj._longlen != 0) {
3758                                             ulong ignored;
3759                                             if (!TrySkipPlpValue(UInt64.MaxValue, stateObj, out ignored)) {
3760                                                 throw SQL.SynchronousCallMayNotPend();
3761                                             }
3762                                         }
3763                                     }
3764
3765                                     else if (0 < sharedState._columnDataBytesRemaining) {
3766                                         if (!stateObj.TrySkipLongBytes(sharedState._columnDataBytesRemaining)) {
3767                                             throw SQL.SynchronousCallMayNotPend();
3768                                         }
3769                                     }
3770
3771                                 }
3772
3773
3774                                 // iib.
3775                                 // now read the remaining values off the wire for this row
3776                                 if (!stateObj.Parser.TrySkipRow(metadata, sharedState._nextColumnHeaderToRead, stateObj)) {
3777                                     throw SQL.SynchronousCallMayNotPend();
3778                                 }
3779
3780                             }
3781                         }
3782                         Run(RunBehavior.Clean, null, null, null, stateObj);
3783                     }
3784                     catch {
3785                         _connHandler.DoomThisConnection();
3786                         throw;
3787                     }
3788                 }
3789 #if DEBUG
3790                 finally {
3791                     tdsReliabilitySection.Stop();
3792                 }
3793 #endif //DEBUG
3794             }
3795             catch (System.OutOfMemoryException) {
3796                 _connHandler.DoomThisConnection();
3797                 throw;
3798             }
3799             catch (System.StackOverflowException) {
3800                 _connHandler.DoomThisConnection();
3801                 throw;
3802             }
3803             catch (System.Threading.ThreadAbortException) {
3804                 _connHandler.DoomThisConnection();
3805                 throw;
3806             }
3807         }
3808         
3809
3810         internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj) {
3811             stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.CultureIdError(), "", 0));
3812
3813             if (null != stateObj) {
3814                 DrainData(stateObj);
3815
3816                 stateObj._pendingData = false;
3817             }
3818
3819             ThrowExceptionAndWarning(stateObj);
3820         }
3821
3822
3823
3824         internal bool TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) {
3825             Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!");
3826
3827             metaData = null;
3828             _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns, null);
3829             int[] indexMap = new int[cColumns];
3830
3831             if (!stateObj.TryReadUInt16(out altMetaDataSet.id)) {
3832                 return false;
3833             }
3834
3835             byte byCols;
3836             if (!stateObj.TryReadByte(out byCols)) {
3837                 return false;
3838             }
3839
3840             while (byCols > 0) {
3841                 if (!stateObj.TrySkipBytes(2)) { // ignore ColNum ...
3842                     return false;
3843                 }
3844                 byCols--;
3845             }
3846
3847             // pass 1, read the meta data off the wire
3848             for (int i = 0; i < cColumns; i++) {
3849                 // internal meta data class
3850                 _SqlMetaData col = altMetaDataSet[i];
3851
3852                 if (!stateObj.TryReadByte(out col.op)) {
3853                     return false;
3854                 }
3855                 if (!stateObj.TryReadUInt16(out col.operand)) {
3856                     return false;
3857                 }
3858
3859                 // TCE is not applicable to AltMetadata.
3860                 if (!TryCommonProcessMetaData(stateObj, col, null, fColMD: false, columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Disabled)) {
3861                     return false;
3862                 }
3863
3864                 if (ADP.IsEmpty(col.column)) {
3865                     // create column name from op
3866                     switch (col.op) {
3867                         case TdsEnums.AOPAVG:
3868                             col.column = "avg";
3869                             break;
3870
3871                         case TdsEnums.AOPCNT:
3872                             col.column = "cnt";
3873                             break;
3874
3875                         case TdsEnums.AOPCNTB:
3876                             col.column = "cntb";
3877                             break;
3878
3879                         case TdsEnums.AOPMAX:
3880                             col.column = "max";
3881                             break;
3882
3883                         case TdsEnums.AOPMIN:
3884                             col.column = "min";
3885                             break;
3886
3887                         case TdsEnums.AOPSUM:
3888                             col.column = "sum";
3889                             break;
3890
3891                         case TdsEnums.AOPANY:
3892                             col.column = "any";
3893                             break;
3894
3895                         case TdsEnums.AOPNOOP:
3896                             col.column = "noop";
3897                             break;
3898
3899                         case TdsEnums.AOPSTDEV:
3900                             col.column = "stdev";
3901                             break;
3902
3903                         case TdsEnums.AOPSTDEVP:
3904                             col.column = "stdevp";
3905                             break;
3906
3907                         case TdsEnums.AOPVAR:
3908                             col.column = "var";
3909                             break;
3910
3911                         case TdsEnums.AOPVARP:
3912                             col.column = "varp";
3913                             break;
3914                     }
3915                 }
3916                 indexMap[i] = i;
3917             }
3918
3919             altMetaDataSet.indexMap = indexMap;
3920             altMetaDataSet.visibleColumns = cColumns;
3921
3922             metaData = altMetaDataSet;
3923             return true;
3924         }
3925
3926         /// <summary>
3927         /// <para> Parses the TDS message to read single CIPHER_INFO entry.</para>
3928         /// </summary>
3929         internal bool TryReadCipherInfoEntry (TdsParserStateObject stateObj, out SqlTceCipherInfoEntry entry) {
3930             byte cekValueCount = 0;
3931             entry = new SqlTceCipherInfoEntry(ordinal: 0);
3932
3933             // Read the DB ID
3934             int dbId;
3935             if (!stateObj.TryReadInt32(out dbId)) {
3936                 return false;
3937             }
3938
3939             // Read the keyID
3940             int keyId;
3941             if (!stateObj.TryReadInt32(out keyId)) {
3942                 return false;
3943             }
3944
3945             // Read the key version
3946             int keyVersion;
3947             if (!stateObj.TryReadInt32(out keyVersion)) {
3948                 return false;
3949             }
3950
3951             // Read the key MD Version
3952             byte[] keyMDVersion = new byte[8];
3953             if (!stateObj.TryReadByteArray(keyMDVersion, 0, 8)) {
3954                 return false;
3955             }
3956
3957             // Read the value count
3958             if (!stateObj.TryReadByte (out cekValueCount)) {
3959                 return false;
3960             }
3961
3962             for (int i = 0; i < cekValueCount; i++) {
3963                 // Read individual CEK values
3964                 byte[] encryptedCek;
3965                 string keyPath;
3966                 string keyStoreName;
3967                 byte algorithmLength;
3968                 string algorithmName;
3969                 ushort shortValue;
3970                 byte byteValue;
3971                 int length;
3972
3973                 // Read the length of encrypted CEK 
3974                 if (!stateObj.TryReadUInt16 (out shortValue)) {
3975                     return false; 
3976                 }
3977
3978                 length = shortValue;
3979                 encryptedCek = new byte[length];
3980
3981                 // Read the actual encrypted CEK
3982                 if (!stateObj.TryReadByteArray (encryptedCek, 0, length)) {
3983                     return false;
3984                 }
3985
3986                 // Read the length of key store name
3987                 if (!stateObj.TryReadByte (out byteValue)) {
3988                     return false;
3989                 }
3990
3991                 length = byteValue;
3992
3993                 // And read the key store name now
3994                 if (!stateObj.TryReadString(length, out keyStoreName)) {
3995                     return false;
3996                 }
3997
3998                 // Read the length of key Path
3999                 if (!stateObj.TryReadUInt16 (out shortValue)) {
4000                     return false;
4001                 }
4002
4003                 length = shortValue;
4004
4005                 // Read the key path string
4006                 if (!stateObj.TryReadString(length, out keyPath)) {
4007                     return false;
4008                 }
4009
4010                 // Read the length of the string carrying the encryption algo
4011                 if (!stateObj.TryReadByte(out algorithmLength)) {
4012                     return false;
4013                 }
4014
4015                 length = (int)algorithmLength;
4016
4017                 // Read the string carrying the encryption algo  (eg. RSA_PKCS_OAEP)
4018                 if (!stateObj.TryReadString(length, out algorithmName)) {
4019                     return false;
4020                 }
4021
4022                 // Add this encrypted CEK blob to our list of encrypted values for the CEK
4023                 entry.Add(encryptedCek, 
4024                     databaseId: dbId, 
4025                     cekId: keyId, 
4026                     cekVersion: keyVersion, 
4027                     cekMdVersion: keyMDVersion, 
4028                     keyPath: keyPath, 
4029                     keyStoreName: keyStoreName, 
4030                     algorithmName: algorithmName);
4031             }
4032
4033             return true;
4034         }
4035
4036         /// <summary>
4037         /// <para> Parses the TDS message to read a single CIPHER_INFO table.</para>
4038         /// </summary>
4039         internal bool TryProcessCipherInfoTable (TdsParserStateObject stateObj, out SqlTceCipherInfoTable? cipherTable) {
4040             // Read count
4041             short tableSize = 0;
4042             cipherTable = null;
4043             if (!stateObj.TryReadInt16(out tableSize)) {
4044                 return false;
4045             }
4046
4047             if (0 != tableSize) {
4048                 SqlTceCipherInfoTable tempTable = new SqlTceCipherInfoTable(tableSize);
4049
4050                 // Read individual entries
4051                 for (int i = 0; i < tableSize; i++) {
4052                     SqlTceCipherInfoEntry entry;
4053                     if (!TryReadCipherInfoEntry (stateObj, out entry)) {
4054                         return false;
4055                     }
4056
4057                     tempTable[i] = entry;
4058                 }
4059
4060                 cipherTable = tempTable;
4061             }
4062
4063             return true;
4064         }
4065
4066         internal bool TryProcessMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData, SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
4067             Debug.Assert(cColumns > 0, "should have at least 1 column in metadata!");
4068
4069             // Read the cipher info table first 
4070             SqlTceCipherInfoTable? cipherTable = null;
4071             if (_serverSupportsColumnEncryption) {
4072                 if (!TryProcessCipherInfoTable (stateObj, out cipherTable)) {
4073                     metaData = null;
4074                     return false;
4075                 }
4076             }
4077
4078             // Read the ColumnData fields
4079             _SqlMetaDataSet newMetaData = new _SqlMetaDataSet(cColumns, cipherTable);
4080             for (int i = 0; i < cColumns; i++) {
4081                 if (!TryCommonProcessMetaData(stateObj, newMetaData[i], cipherTable, fColMD: true, columnEncryptionSetting: columnEncryptionSetting)) {
4082                     metaData = null;
4083                     return false;
4084                 }
4085             }
4086
4087             // DEVNOTE: cipherTable is discarded at this point since its no longer needed.
4088             metaData = newMetaData;
4089             return true;
4090         }
4091
4092         private bool IsVarTimeTds(byte tdsType) {
4093             return tdsType == TdsEnums.SQLTIME || tdsType == TdsEnums.SQLDATETIME2 || tdsType == TdsEnums.SQLDATETIMEOFFSET;
4094         }
4095
4096         private bool TryProcessTypeInfo (TdsParserStateObject stateObj, SqlMetaDataPriv col, UInt32 userType) {
4097             byte byteLen;
4098             byte tdsType;
4099             if (!stateObj.TryReadByte(out tdsType)) {
4100                 return false;
4101             }
4102
4103             if (tdsType == TdsEnums.SQLXMLTYPE)
4104                 col.length = TdsEnums.SQL_USHORTVARMAXLEN;  //Use the same length as other plp datatypes
4105             else if (IsVarTimeTds(tdsType))
4106                 col.length = 0;  // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
4107             else if (tdsType == TdsEnums.SQLDATE) {
4108                 col.length = 3;
4109             }
4110             else {
4111                 if (!TryGetTokenLength(tdsType, stateObj, out col.length)) {
4112                     return false;
4113                 }
4114             }
4115
4116             col.metaType = MetaType.GetSqlDataType(tdsType, userType, col.length);
4117             col.type = col.metaType.SqlDbType;
4118
4119             // If sphinx, do not change to nullable type
4120             if (_isShiloh)
4121                 col.tdsType = (col.isNullable ? col.metaType.NullableType : col.metaType.TDSType);
4122             else
4123                 col.tdsType = tdsType;
4124
4125             if (_isYukon) {
4126                 if (TdsEnums.SQLUDT == tdsType) {
4127                     if (!TryProcessUDTMetaData((SqlMetaDataPriv) col, stateObj)) {
4128                         return false;
4129                     }
4130                 }
4131
4132                 if (col.length == TdsEnums.SQL_USHORTVARMAXLEN) {
4133                     Debug.Assert(tdsType == TdsEnums.SQLXMLTYPE ||
4134                                  tdsType == TdsEnums.SQLBIGVARCHAR ||
4135                                  tdsType == TdsEnums.SQLBIGVARBINARY ||
4136                                  tdsType == TdsEnums.SQLNVARCHAR ||
4137                                  tdsType == TdsEnums.SQLUDT,
4138                                  "Invalid streaming datatype");
4139                     col.metaType = MetaType.GetMaxMetaTypeFromMetaType(col.metaType);
4140                     Debug.Assert(col.metaType.IsLong, "Max datatype not IsLong");
4141                     col.length = Int32.MaxValue;
4142                     if (tdsType == TdsEnums.SQLXMLTYPE) {
4143                         byte schemapresent;
4144                         if (!stateObj.TryReadByte(out schemapresent)) {
4145                             return false;
4146                         }
4147
4148                         if ((schemapresent & 1) != 0) {
4149                             if (!stateObj.TryReadByte(out byteLen)) {
4150                                 return false;
4151                             }
4152                             if (byteLen != 0) {
4153                                 if (!stateObj.TryReadString(byteLen, out col.xmlSchemaCollectionDatabase)) {
4154                                     return false;
4155                                 }
4156                             }
4157
4158                             if (!stateObj.TryReadByte(out byteLen)) {
4159                                 return false;
4160                             }
4161                             if (byteLen != 0) {
4162                                 if (!stateObj.TryReadString(byteLen, out col.xmlSchemaCollectionOwningSchema)) {
4163                                     return false;
4164                                 }
4165                             }
4166
4167                             short shortLen;
4168                             if (!stateObj.TryReadInt16(out shortLen)) {
4169                                 return false;
4170                             }
4171                             if (byteLen != 0) {
4172                                 if (!stateObj.TryReadString(shortLen, out col.xmlSchemaCollectionName)) {
4173                                     return false;
4174                                 }
4175                             }
4176                         }
4177                     }
4178                 }
4179             }
4180
4181             if (col.type == SqlDbType.Decimal) {
4182                 if (!stateObj.TryReadByte(out col.precision)) {
4183                     return false;
4184                 }
4185                 if (!stateObj.TryReadByte(out col.scale)) {
4186                     return false;
4187                 }
4188             }
4189
4190             if (col.metaType.IsVarTime) {
4191                 if (!stateObj.TryReadByte(out col.scale)) {
4192                     return false;
4193                 }
4194
4195                 Debug.Assert(0 <= col.scale && col.scale <= 7);
4196
4197                 // calculate actual column length here
4198                 // 
4199                 switch (col.metaType.SqlDbType)
4200                 {
4201                     case SqlDbType.Time:
4202                         col.length = MetaType.GetTimeSizeFromScale(col.scale);
4203                         break;
4204                     case SqlDbType.DateTime2:
4205                         // Date in number of days (3 bytes) + time
4206                         col.length = 3 + MetaType.GetTimeSizeFromScale(col.scale);
4207                         break;
4208                     case SqlDbType.DateTimeOffset:
4209                         // Date in days (3 bytes) + offset in minutes (2 bytes) + time
4210                         col.length = 5 + MetaType.GetTimeSizeFromScale(col.scale);
4211                         break;
4212
4213                     default:
4214                         Debug.Assert(false, "Unknown VariableTime type!");
4215                         break;
4216                 }
4217             }
4218
4219             // read the collation for 7.x servers
4220             if (_isShiloh && col.metaType.IsCharType && (tdsType != TdsEnums.SQLXMLTYPE)) {
4221                 if (!TryProcessCollation(stateObj, out col.collation)) {
4222                     return false;
4223                 }
4224
4225                 int codePage = GetCodePage(col.collation, stateObj);
4226
4227                 if (codePage == _defaultCodePage) {
4228                     col.codePage = _defaultCodePage;
4229                     col.encoding = _defaultEncoding;
4230                 }
4231                 else {
4232                     col.codePage = codePage;
4233                     col.encoding = System.Text.Encoding.GetEncoding(col.codePage);
4234                 }
4235             }
4236
4237             return true;
4238         }
4239
4240         private bool TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col, SqlTceCipherInfoTable? cipherTable, bool fColMD, SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
4241             byte byteLen;
4242             UInt32 userType;
4243
4244             // read user type - 4 bytes Yukon, 2 backwards
4245             if (IsYukonOrNewer) {
4246                 if (!stateObj.TryReadUInt32(out userType)) {
4247                     return false;
4248                 }
4249             }
4250             else {
4251                 ushort userTypeShort;
4252                 if (!stateObj.TryReadUInt16(out userTypeShort)) {
4253                     return false;
4254                 }
4255                 userType = userTypeShort;
4256             }
4257
4258             // read flags and set appropriate flags in structure
4259             byte flags;
4260             if (!stateObj.TryReadByte(out flags)) {
4261                 return false;
4262             }
4263
4264             col.updatability = (byte)((flags & TdsEnums.Updatability) >> 2);
4265             col.isNullable = (TdsEnums.Nullable == (flags & TdsEnums.Nullable));
4266             col.isIdentity = (TdsEnums.Identity == (flags & TdsEnums.Identity));
4267
4268             // read second byte of column metadata flags
4269             if (!stateObj.TryReadByte(out flags)) {
4270                 return false;
4271             }
4272             
4273             col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet));
4274             if (fColMD && _serverSupportsColumnEncryption) {
4275                 col.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted));
4276             }
4277
4278             // Read TypeInfo
4279             if (!TryProcessTypeInfo (stateObj, col, userType)) {
4280                 return false;
4281             }
4282
4283             // Read tablename if present
4284             if (col.metaType.IsLong && !col.metaType.IsPlp) {
4285                 if (_isYukon) {
4286                     int  unusedLen = 0xFFFF;      //We ignore this value
4287                     if (!TryProcessOneTable(stateObj, ref unusedLen, out col.multiPartTableName)) {
4288                         return false;
4289                     }
4290                 } else {
4291                     ushort shortLen;
4292                     if (!stateObj.TryReadUInt16(out shortLen)) {
4293                         return false;
4294                     }
4295                     string tableName;
4296                     if (!stateObj.TryReadString(shortLen, out tableName)) {
4297                         return false;
4298                     }
4299                     // with Sql2000 this is returned as an unquoted mix of catalog.owner.table
4300                     // all of which may contain "." and unable to parse correctly from the string alone
4301                     // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column
4302                     // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios
4303                     col.multiPartTableName = new MultiPartTableName(tableName);
4304                 }
4305             }
4306
4307             // Read the TCE column cryptoinfo
4308             if (fColMD && _serverSupportsColumnEncryption && col.isEncrypted) {
4309                 // If the column is encrypted, we should have a valid cipherTable
4310                 if (cipherTable.HasValue && !TryProcessTceCryptoMetadata (stateObj, col, cipherTable.Value, columnEncryptionSetting, isReturnValue: false)) {
4311                     return false;
4312                 }
4313             }
4314
4315             // Read the column name 
4316             if (!stateObj.TryReadByte(out byteLen)) {
4317                 return false;
4318             }
4319             if (!stateObj.TryReadString(byteLen, out col.column)) {
4320                 return false;
4321             }
4322
4323             // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings.
4324             // We only need to fire this event when we actually have a meta data stream with 0 or more rows.
4325             stateObj._receivedColMetaData = true;
4326             return true;
4327         }
4328
4329         private bool TryProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObject stateObj) {
4330             ushort shortLength;
4331             byte byteLength;
4332
4333             if (!stateObj.TryReadUInt16(out shortLength)) { // max byte size
4334                 return false;
4335             }
4336             metaData.length = shortLength;
4337
4338             // database name
4339             if (!stateObj.TryReadByte(out byteLength)) {
4340                 return false;
4341             }
4342             if (byteLength != 0) {
4343                 if (!stateObj.TryReadString(byteLength, out metaData.udtDatabaseName)) {
4344                     return false;
4345                 }
4346             }
4347
4348             // schema name
4349             if (!stateObj.TryReadByte(out byteLength)) {
4350                 return false;
4351             }
4352             if (byteLength != 0) {
4353                 if (!stateObj.TryReadString(byteLength, out metaData.udtSchemaName)) {
4354                     return false;
4355                 }
4356             }
4357
4358             // type name
4359             if (!stateObj.TryReadByte(out byteLength)) {
4360                 return false;
4361             }
4362             if (byteLength != 0) {
4363                 if (!stateObj.TryReadString(byteLength, out metaData.udtTypeName)) {
4364                     return false;
4365                 }
4366             }
4367
4368             if (!stateObj.TryReadUInt16(out shortLength)) {
4369                 return false;
4370             }
4371             if (shortLength != 0) {
4372                 if (!stateObj.TryReadString(shortLength, out metaData.udtAssemblyQualifiedName)) {
4373                     return false;
4374                 }
4375             }
4376
4377             return true;
4378         }
4379
4380         private void WriteUDTMetaData(object value, string database, string schema, string type,
4381                                         TdsParserStateObject stateObj) {
4382             // database
4383             if (ADP.IsEmpty(database)) {
4384                 stateObj.WriteByte(0);
4385             }
4386             else {
4387                 stateObj.WriteByte((byte)database.Length);
4388                 WriteString(database, stateObj);
4389             }
4390
4391             // schema
4392             if (ADP.IsEmpty(schema)) {
4393                 stateObj.WriteByte(0);
4394             }
4395             else {
4396                 stateObj.WriteByte((byte)schema.Length);
4397                 WriteString(schema, stateObj);
4398             }
4399
4400             // type
4401             if (ADP.IsEmpty(type)) {
4402                 stateObj.WriteByte(0);
4403             }
4404             else {
4405                 stateObj.WriteByte((byte)type.Length);
4406                 WriteString(type, stateObj);
4407             }
4408         }
4409
4410         internal bool TryProcessTableName(int length, TdsParserStateObject stateObj, out MultiPartTableName[] multiPartTableNames) {
4411             int tablesAdded = 0;
4412
4413             MultiPartTableName[] tables = new MultiPartTableName[1];
4414             MultiPartTableName mpt;
4415             while (length > 0) {
4416                 // 
4417
4418
4419                 if (!TryProcessOneTable(stateObj, ref length, out mpt)) {
4420                     multiPartTableNames = null;
4421                     return false;
4422                 }
4423                 if (tablesAdded == 0) {
4424                     tables[tablesAdded] = mpt;
4425                 }
4426                 else {
4427                     MultiPartTableName[] newTables = new MultiPartTableName[tables.Length + 1];
4428                     Array.Copy(tables, 0, newTables, 0, tables.Length);
4429                     newTables[tables.Length] = mpt;
4430                     tables = newTables;
4431                 }
4432
4433                 tablesAdded++;
4434             }
4435
4436             multiPartTableNames = tables;
4437             return true;
4438         }
4439
4440         private bool TryProcessOneTable(TdsParserStateObject stateObj, ref int length, out MultiPartTableName multiPartTableName) {
4441             ushort tableLen;
4442             MultiPartTableName mpt;
4443             string value;
4444
4445             multiPartTableName = default(MultiPartTableName);
4446
4447             if (_isShilohSP1) {
4448
4449                 mpt = new MultiPartTableName();
4450                 byte nParts;
4451
4452                 // Find out how many parts in the TDS stream
4453                 if (!stateObj.TryReadByte(out nParts)) {
4454                     return false;
4455                 }
4456                 length--;
4457                 if (nParts == 4) {
4458                     if (!stateObj.TryReadUInt16(out tableLen)) {
4459                         return false;
4460                     }
4461                     length -= 2;
4462                     if (!stateObj.TryReadString(tableLen, out value)) {
4463                         return false;
4464                     }
4465                     mpt.ServerName = value;
4466                     nParts--;
4467                     length -= (tableLen * 2); // wide bytes
4468                 }
4469                 if (nParts == 3) {
4470                     if (!stateObj.TryReadUInt16(out tableLen)) {
4471                         return false;
4472                     }
4473                     length -= 2;
4474                     if (!stateObj.TryReadString(tableLen, out value)) {
4475                         return false;
4476                     }
4477                     mpt.CatalogName = value;
4478                     length -= (tableLen * 2); // wide bytes
4479                     nParts--;
4480                 }
4481                 if (nParts == 2) {
4482                     if (!stateObj.TryReadUInt16(out tableLen)) {
4483                         return false;
4484                     }
4485                     length -= 2;
4486                     if (!stateObj.TryReadString(tableLen, out value)) {
4487                         return false;
4488                     }
4489                     mpt.SchemaName = value;
4490                     length -= (tableLen * 2); // wide bytes
4491                     nParts--;
4492                 }
4493                 if (nParts == 1) {
4494                     if (!stateObj.TryReadUInt16(out tableLen)) {
4495                         return false;
4496                     }
4497                     length -= 2;
4498                     if (!stateObj.TryReadString(tableLen, out value)) {
4499                         return false;
4500                     }
4501                     mpt.TableName = value;
4502                     length -= (tableLen * 2); // wide bytes
4503                     nParts--;
4504                 }
4505                 Debug.Assert(nParts == 0 , "ProcessTableName:Unidentified parts in the table name token stream!");
4506
4507             }
4508             else {
4509                 if (!stateObj.TryReadUInt16(out tableLen)) {
4510                     return false;
4511                 }
4512                 length -= 2;
4513                 if (!stateObj.TryReadString(tableLen, out value)) {
4514                     return false;
4515                 }
4516                 string tableName = value;
4517                 length -= (tableLen * 2); // wide bytes
4518                 mpt = new MultiPartTableName(MultipartIdentifier.ParseMultipartIdentifier(tableName, "[\"", "]\"", Res.SQL_TDSParserTableName, false));
4519             }
4520
4521             multiPartTableName = mpt;
4522             return true;
4523         }
4524
4525         // augments current metadata with table and key information
4526         private bool TryProcessColInfo(_SqlMetaDataSet columns, SqlDataReader reader, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) {
4527             Debug.Assert(columns != null && columns.Length > 0, "no metadata available!");
4528
4529             metaData = null;
4530
4531             for (int i = 0; i < columns.Length; i++) {
4532                 _SqlMetaData col = columns[i];
4533
4534                 byte ignored;
4535                 if (!stateObj.TryReadByte(out ignored)) { // colnum, ignore
4536                     return false;
4537                 }
4538                 if (!stateObj.TryReadByte(out col.tableNum)) {
4539                     return false;
4540                 }
4541
4542                 // interpret status
4543                 byte status;
4544                 if (!stateObj.TryReadByte(out status)) {
4545                     return false;
4546                 }
4547
4548                 col.isDifferentName = (TdsEnums.SQLDifferentName == (status & TdsEnums.SQLDifferentName));
4549                 col.isExpression = (TdsEnums.SQLExpression == (status & TdsEnums.SQLExpression));
4550                 col.isKey = (TdsEnums.SQLKey == (status & TdsEnums.SQLKey));
4551                 col.isHidden = (TdsEnums.SQLHidden == (status & TdsEnums.SQLHidden));
4552
4553                 // read off the base table name if it is different than the select list column name
4554                 if (col.isDifferentName) {
4555                     byte len;
4556                     if (!stateObj.TryReadByte(out len)) {
4557                         return false;
4558                     }
4559                     if (!stateObj.TryReadString(len, out col.baseColumn)) {
4560                         return false;
4561                     }
4562                 }
4563
4564                 // Fixup column name - only if result of a table - that is if it was not the result of
4565                 // an expression.
4566                 if ((reader.TableNames != null) && (col.tableNum > 0)) {
4567                     Debug.Assert(reader.TableNames.Length >= col.tableNum, "invalid tableNames array!");
4568                     col.multiPartTableName = reader.TableNames[col.tableNum - 1];
4569                 }
4570
4571                 // MDAC 60109: expressions are readonly
4572                 if (col.isExpression) {
4573                     col.updatability = 0;
4574                 }
4575             }
4576
4577             // set the metadata so that the stream knows some metadata info has changed
4578             metaData = columns;
4579             return true;
4580         }
4581
4582         // takes care of any per data header information:
4583         // for long columns, reads off textptrs, reads length, check nullability
4584         // for other columns, reads length, checks nullability
4585         // returns length and nullability
4586         internal bool TryProcessColumnHeader(SqlMetaDataPriv col, TdsParserStateObject stateObj, int columnOrdinal, out bool isNull, out ulong length) {
4587             // query NBC row information first
4588             if (stateObj.IsNullCompressionBitSet(columnOrdinal)) {
4589                 isNull = true;
4590                 // column information is not present in TDS if null compression bit is set, return now
4591                 length = 0;
4592                 return true;
4593             }
4594
4595             return TryProcessColumnHeaderNoNBC(col, stateObj, out isNull, out length);
4596         }
4597
4598         private bool TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col, TdsParserStateObject stateObj, out bool isNull, out ulong length) {
4599             if (col.metaType.IsLong && !col.metaType.IsPlp) {
4600                 //
4601                 // we don't care about TextPtrs, simply go after the data after it
4602                 //
4603                 byte textPtrLen;
4604                 if (!stateObj.TryReadByte(out textPtrLen)) {
4605                     isNull = false;
4606                     length = 0;
4607                     return false;
4608                 }
4609
4610                 if (0 != textPtrLen) {
4611                     // read past text pointer
4612                     if (!stateObj.TrySkipBytes(textPtrLen)) {
4613                         isNull = false;
4614                         length = 0;
4615                         return false;
4616                     }
4617
4618                     // read past timestamp
4619                     if (!stateObj.TrySkipBytes(TdsEnums.TEXT_TIME_STAMP_LEN)) {
4620                         isNull = false;
4621                         length = 0;
4622                         return false;
4623                     }
4624
4625                     isNull = false;
4626                     return TryGetDataLength(col, stateObj, out length);
4627                 }
4628                 else {
4629                     isNull = true;
4630                     length = 0;
4631                     return true;
4632
4633                 }
4634             }
4635             else {
4636                 // non-blob columns
4637                 ulong longlen;
4638                 if (!TryGetDataLength(col, stateObj, out longlen)) {
4639                     isNull = false;
4640                     length = 0;
4641                     return false;
4642                 }
4643                 isNull = IsNull(col.metaType, longlen);
4644                 length = (isNull ? 0 : longlen);
4645                 return true;
4646             }
4647         }
4648
4649         // assumes that the current position is at the start of an altrow!
4650         internal bool TryGetAltRowId(TdsParserStateObject stateObj, out int id) {
4651             byte token;
4652             if (!stateObj.TryReadByte(out token)) { // skip over ALTROW token
4653                 id = 0;
4654                 return false;
4655             }
4656             Debug.Assert((token == TdsEnums.SQLALTROW), "");
4657
4658             // Start a fresh row - disable NBC since Alt Rows are never compressed
4659             if (!stateObj.TryStartNewRow(isNullCompressed: false)) {
4660                 id = 0;
4661                 return false;
4662             }
4663
4664             ushort shortId;
4665             if (!stateObj.TryReadUInt16(out shortId)) {
4666                 id = 0;
4667                 return false;
4668             }
4669
4670             id = shortId;
4671             return true;
4672         }
4673
4674         // Used internally by BulkCopy only
4675         private bool TryProcessRow(_SqlMetaDataSet columns, object[] buffer, int[] map, TdsParserStateObject stateObj) {
4676             SqlBuffer data = new SqlBuffer();
4677
4678             for (int i = 0; i < columns.Length; i++) {
4679                 _SqlMetaData md = columns[i];
4680                 Debug.Assert(md != null, "_SqlMetaData should not be null for column " + i.ToString(CultureInfo.InvariantCulture));
4681
4682                 bool isNull;
4683                 ulong len;
4684                 if (!TryProcessColumnHeader(md, stateObj, i, out isNull, out len)) {
4685                     return false;
4686                 }
4687
4688                 if (isNull) {
4689                     GetNullSqlValue(data, md, SqlCommandColumnEncryptionSetting.Disabled /*Column Encryption Disabled for Bulk Copy*/, _connHandler);
4690                     buffer[map[i]] = data.SqlValue;
4691                 }
4692                 else {
4693                     // We only read up to 2Gb. Throw if data is larger. Very large data
4694                     // should be read in chunks in sequential read mode
4695                     // For Plp columns, we may have gotten only the length of the first chunk
4696                     if (!TryReadSqlValue(data, md, md.metaType.IsPlp ? (Int32.MaxValue) : (int)len, stateObj,
4697                                          SqlCommandColumnEncryptionSetting.Disabled /*Column Encryption Disabled for Bulk Copy*/,
4698                                          md.column)) {
4699                         return false;
4700                     }
4701                     buffer[map[i]] = data.SqlValue;
4702                     if (stateObj._longlen != 0) {
4703                         throw new SqlTruncateException(Res.GetString(Res.SqlMisc_TruncationMaxDataMessage));
4704                     }
4705                 }
4706                 data.Clear();
4707             }
4708
4709             return true;
4710         }
4711
4712         /// <summary>
4713         /// Determines if a column value should be transparently decrypted (based on SqlCommand and Connection String settings).
4714         /// </summary>
4715         /// <returns>true if the value should be transparently decrypted, false otherwise</returns>
4716         internal static bool ShouldHonorTceForRead (SqlCommandColumnEncryptionSetting columnEncryptionSetting, 
4717             SqlInternalConnectionTds connection) {
4718
4719             // Command leve setting trumps all
4720             switch (columnEncryptionSetting) {
4721                 case SqlCommandColumnEncryptionSetting.Disabled:
4722                     return false;
4723                 case SqlCommandColumnEncryptionSetting.Enabled:
4724                     return true;
4725                 case SqlCommandColumnEncryptionSetting.ResultSetOnly:
4726                     return true;
4727                 default:
4728                     // Check connection level setting!
4729                     Debug.Assert(SqlCommandColumnEncryptionSetting.UseConnectionSetting == columnEncryptionSetting,
4730                         "Unexpected value for command level override");
4731                     return (connection != null && connection.ConnectionOptions != null &&
4732                         connection.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled);
4733             }
4734         }
4735
4736         internal static object GetNullSqlValue(
4737                 SqlBuffer nullVal, 
4738                 SqlMetaDataPriv md, 
4739                 SqlCommandColumnEncryptionSetting columnEncryptionSetting,
4740                 SqlInternalConnectionTds connection) {
4741             SqlDbType type = md.type;
4742
4743             if (type == SqlDbType.VarBinary && // if its a varbinary
4744                 md.isEncrypted &&// and encrypted
4745                 ShouldHonorTceForRead(columnEncryptionSetting, connection)){
4746                 type = md.baseTI.type; // the use the actual (plaintext) type
4747             }
4748
4749             switch (type) {
4750                 case SqlDbType.Real:
4751                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Single);
4752                     break;
4753
4754                 case SqlDbType.Float:
4755                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Double);
4756                     break;
4757
4758                 case SqlDbType.Udt:
4759                 case SqlDbType.Binary:
4760                 case SqlDbType.VarBinary:
4761                 case SqlDbType.Image:
4762                     nullVal.SqlBinary = SqlBinary.Null;
4763                     break;
4764
4765                 case SqlDbType.UniqueIdentifier:
4766                     nullVal.SqlGuid = SqlGuid.Null;
4767                     break;
4768
4769                 case SqlDbType.Bit:
4770                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Boolean);
4771                     break;
4772
4773                 case SqlDbType.TinyInt:
4774                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Byte);
4775                     break;
4776
4777                 case SqlDbType.SmallInt:
4778                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Int16);
4779                     break;
4780
4781                 case SqlDbType.Int:
4782                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Int32);
4783                     break;
4784
4785                 case SqlDbType.BigInt:
4786                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Int64);
4787                     break;
4788
4789                 case SqlDbType.Char:
4790                 case SqlDbType.VarChar:
4791                 case SqlDbType.NChar:
4792                 case SqlDbType.NVarChar:
4793                 case SqlDbType.Text:
4794                 case SqlDbType.NText:
4795                     nullVal.SetToNullOfType(SqlBuffer.StorageType.String);
4796                     break;
4797
4798                 case SqlDbType.Decimal:
4799                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Decimal);
4800                     break;
4801
4802                 case SqlDbType.DateTime:
4803                 case SqlDbType.SmallDateTime:
4804                     nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime);
4805                     break;
4806
4807                 case SqlDbType.Money:
4808                 case SqlDbType.SmallMoney:
4809                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Money);
4810                     break;
4811
4812                 case SqlDbType.Variant:
4813                     // DBNull.Value will have to work here
4814                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Empty);
4815                     break;
4816
4817                 case SqlDbType.Xml:
4818                     nullVal.SqlCachedBuffer = SqlCachedBuffer.Null;
4819                     break;
4820
4821                 case SqlDbType.Date:
4822                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Date);
4823                     break;
4824
4825                 case SqlDbType.Time:
4826                     nullVal.SetToNullOfType(SqlBuffer.StorageType.Time);
4827                     break;
4828                     
4829                 case SqlDbType.DateTime2:
4830                     nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime2);
4831                     break;
4832                     
4833                 case SqlDbType.DateTimeOffset:
4834                     nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTimeOffset);
4835                     break;
4836
4837                 case SqlDbType.Timestamp:
4838                     // Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change
4839                     // Dev10 Bug #752790 - don't assert when it does happen
4840                     break;
4841
4842                 default:
4843                     Debug.Assert(false, "unknown null sqlType!" + md.type.ToString());
4844                     break;
4845             }
4846
4847             return nullVal;
4848         }
4849
4850         internal bool TrySkipRow(_SqlMetaDataSet columns, TdsParserStateObject stateObj) {
4851             return TrySkipRow(columns, 0, stateObj);
4852         }
4853
4854         internal bool TrySkipRow(_SqlMetaDataSet columns, int startCol, TdsParserStateObject stateObj) {
4855             for (int i = startCol; i < columns.Length; i++) {
4856                 _SqlMetaData md = columns[i];
4857
4858                 if (!TrySkipValue(md, i, stateObj)) {
4859                     return false;
4860                 }
4861             }
4862             return true;
4863         }
4864
4865         /// <summary>
4866         /// This method skips bytes of a single column value from the media. It supports NBCROW and handles all types of values, including PLP and long
4867         /// </summary>
4868         internal bool TrySkipValue(SqlMetaDataPriv md, int columnOrdinal, TdsParserStateObject stateObj) {
4869             if (stateObj.IsNullCompressionBitSet(columnOrdinal)) {
4870                 return true;
4871             }
4872
4873             if (md.metaType.IsPlp) {
4874                 ulong ignored;
4875                 if (!TrySkipPlpValue(UInt64.MaxValue, stateObj, out ignored)) {
4876                     return false;
4877                 }
4878             }
4879             else if (md.metaType.IsLong) {
4880                 
4881                 Debug.Assert(!md.metaType.IsPlp, "Plp types must be handled using SkipPlpValue");
4882
4883                 byte textPtrLen;
4884                 if (!stateObj.TryReadByte(out textPtrLen)) {
4885                     return false;
4886                 }
4887
4888                 if (0 != textPtrLen) {
4889                     if (!stateObj.TrySkipBytes(textPtrLen + TdsEnums.TEXT_TIME_STAMP_LEN)) {
4890                         return false;
4891                     }
4892
4893                     int length;
4894                     if (!TryGetTokenLength(md.tdsType, stateObj, out length)) {
4895                         return false;
4896                     }
4897                     if (!stateObj.TrySkipBytes(length)) {
4898                         return false;
4899                     }
4900                 }
4901             }
4902             else {
4903                 int length;
4904                 if (!TryGetTokenLength(md.tdsType, stateObj, out length)) {
4905                     return false;
4906                 }
4907
4908                 // if false, no value to skip - it's null
4909                 if (!IsNull(md.metaType, (ulong)length)) {
4910                     if (!stateObj.TrySkipBytes(length)) {
4911                         return false;
4912                     }
4913                 }
4914             }
4915
4916             return true;
4917         }
4918
4919         private bool IsNull(MetaType mt, ulong length) {
4920             // null bin and char types have a length of -1 to represent null
4921             if (mt.IsPlp) {
4922                 return (TdsEnums.SQL_PLP_NULL == length);
4923             }
4924
4925             // HOTFIX #50000415: for image/text, 0xFFFF is the length, not representing null
4926             if ((TdsEnums.VARNULL == length) && !mt.IsLong) {
4927                 return true;
4928             }
4929
4930             // other types have a length of 0 to represent null
4931             // long and non-PLP types will always return false because these types are either char or binary
4932             // this is expected since for long and non-plp types isnull is checked based on textptr field and not the length
4933             return ((TdsEnums.FIXEDNULL == length) && !mt.IsCharType && !mt.IsBinType);
4934         }
4935
4936         private bool TryReadSqlStringValue(SqlBuffer value, byte type, int length, Encoding encoding, bool isPlp, TdsParserStateObject stateObj) {
4937             switch (type) {
4938                 case TdsEnums.SQLCHAR:
4939                 case TdsEnums.SQLBIGCHAR:
4940                 case TdsEnums.SQLVARCHAR:
4941                 case TdsEnums.SQLBIGVARCHAR:
4942                 case TdsEnums.SQLTEXT:
4943                     // If bigvarchar(max), we only read the first chunk here,
4944                     // expecting the caller to read the rest
4945                     if (encoding == null) {
4946                         // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
4947                         // 7.0 has no support for multiple code pages in data - single code page support only
4948                         encoding = _defaultEncoding;
4949                     }
4950                     string stringValue;
4951                     if (!stateObj.TryReadStringWithEncoding(length, encoding, isPlp, out stringValue)) {
4952                         return false;
4953                     }
4954                     value.SetToString(stringValue);
4955                     break;
4956
4957                 case TdsEnums.SQLNCHAR:
4958                 case TdsEnums.SQLNVARCHAR:
4959                 case TdsEnums.SQLNTEXT:
4960                 {
4961                         String s = null;
4962
4963                         if (isPlp) {
4964                             char[] cc = null;
4965
4966                             if (!TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length)) {
4967                                 return false;
4968                             }
4969                             if (length > 0) {
4970                                 s = new String(cc, 0, length);
4971                             }
4972                             else {
4973                                 s = ADP.StrEmpty;
4974                             }
4975                         }
4976                         else {
4977                             if (!stateObj.TryReadString(length >> 1, out s)) {
4978                                 return false;
4979                             }
4980                         }
4981
4982                         value.SetToString(s);
4983                         break;
4984                     }
4985
4986                 default:
4987                     Debug.Assert(false, "Unknown tds type for SqlString!" + type.ToString(CultureInfo.InvariantCulture));
4988                     break;
4989             }
4990
4991             return true;
4992         }
4993
4994         /// <summary>
4995         /// Deserializes the unencrypted bytes into a value based on the target type info.
4996         /// </summary>
4997         internal bool DeserializeUnencryptedValue (SqlBuffer value, byte[] unencryptedBytes, SqlMetaDataPriv md, TdsParserStateObject stateObj, byte normalizationVersion) {
4998             if (normalizationVersion != 0x01) {
4999                 throw SQL.UnsupportedNormalizationVersion(normalizationVersion);
5000             }
5001
5002             byte tdsType = md.baseTI.tdsType;
5003             int length = unencryptedBytes.Length;
5004
5005             // For normalized types, the length and scale of the actual type might be different than the value's.
5006             int denormalizedLength = md.baseTI.length;
5007             byte denormalizedScale = md.baseTI.scale;
5008
5009             Debug.Assert (false == md.baseTI.isEncrypted, "Double encryption detected");
5010             switch (tdsType) {
5011                 // We normalize to allow conversion across data types. All data types below are serialized into a BIGINT.
5012                 case TdsEnums.SQLBIT:
5013                 case TdsEnums.SQLBITN:
5014                 case TdsEnums.SQLINTN:
5015                 case TdsEnums.SQLINT1:
5016                 case TdsEnums.SQLINT2:
5017                 case TdsEnums.SQLINT4:
5018                 case TdsEnums.SQLINT8:
5019                     Debug.Assert(length == 8, "invalid length for SqlInt64 type!");
5020                     byte byteValue;
5021                     long longValue;
5022
5023                     if (unencryptedBytes.Length != 8) {
5024                         return false;
5025                     }
5026
5027                     longValue = BitConverter.ToInt64(unencryptedBytes, 0);
5028
5029                     if (tdsType == TdsEnums.SQLBIT ||
5030                         tdsType == TdsEnums.SQLBITN) {
5031                         value.Boolean = (longValue != 0);
5032                         break;
5033                     }
5034
5035                     if (tdsType == TdsEnums.SQLINT1 || denormalizedLength == 1)
5036                         value.Byte = (byte)longValue;
5037                     else if (tdsType == TdsEnums.SQLINT2 || denormalizedLength == 2)
5038                         value.Int16 = (Int16)longValue;
5039                     else if (tdsType == TdsEnums.SQLINT4 || denormalizedLength == 4)
5040                         value.Int32 = (Int32)longValue;
5041                     else
5042                         value.Int64 = longValue;
5043
5044                     break;
5045
5046                 case TdsEnums.SQLFLTN:
5047                     if (length == 4) {
5048                         goto case TdsEnums.SQLFLT4;
5049                     }
5050                     else {
5051                         goto case TdsEnums.SQLFLT8;
5052                     }
5053
5054                 case TdsEnums.SQLFLT4:
5055                     Debug.Assert(length == 4, "invalid length for SqlSingle type!");
5056                     float singleValue;
5057                     if (unencryptedBytes.Length != 4) {
5058                         return false;
5059                     }
5060
5061                     singleValue = BitConverter.ToSingle(unencryptedBytes, 0);
5062                     value.Single = singleValue;
5063                     break;
5064
5065                 case TdsEnums.SQLFLT8:
5066                     double doubleValue;
5067                     if (unencryptedBytes.Length != 8) {
5068                         return false;
5069                     }
5070
5071                     doubleValue = BitConverter.ToDouble(unencryptedBytes, 0);
5072                     value.Double = doubleValue;
5073                     break;
5074
5075                 // We normalize to allow conversion across data types. SMALLMONEY is serialized into a MONEY.
5076                 case TdsEnums.SQLMONEYN:
5077                 case TdsEnums.SQLMONEY4:
5078                 case TdsEnums.SQLMONEY:
5079                 {
5080                     int mid;
5081                     uint lo;
5082
5083                     if (unencryptedBytes.Length != 8) {
5084                         return false;
5085                     }
5086
5087                     mid = BitConverter.ToInt32(unencryptedBytes, 0);
5088                     lo = BitConverter.ToUInt32(unencryptedBytes, 4);
5089
5090                     long l = (((long)mid) << 0x20) + ((long)lo);
5091                     value.SetToMoney(l);
5092                     break;
5093                 }
5094
5095                 case TdsEnums.SQLDATETIMN:
5096                     if (length == 4) {
5097                         goto case TdsEnums.SQLDATETIM4;
5098                     }
5099                     else {
5100                         goto case TdsEnums.SQLDATETIME;
5101                     }
5102
5103                 case TdsEnums.SQLDATETIM4:
5104                     ushort daypartShort, timepartShort;
5105                     if (unencryptedBytes.Length != 4) {
5106                         return false;
5107                     }
5108
5109                     daypartShort = (UInt16)((unencryptedBytes[1] << 8) + unencryptedBytes[0]);
5110                     timepartShort = (UInt16)((unencryptedBytes[3] << 8) + unencryptedBytes[2]);
5111                     value.SetToDateTime(daypartShort, timepartShort * SqlDateTime.SQLTicksPerMinute);
5112                     break;
5113
5114                 case TdsEnums.SQLDATETIME:
5115                     int daypart;
5116                     uint timepart;
5117                     if (unencryptedBytes.Length != 8) {
5118                         return false;
5119                     }
5120
5121                     daypart = BitConverter.ToInt32(unencryptedBytes, 0);
5122                     timepart = BitConverter.ToUInt32(unencryptedBytes, 4);
5123                     value.SetToDateTime(daypart, (int)timepart);
5124                     break;
5125
5126                 case TdsEnums.SQLUNIQUEID:
5127                 {
5128                     Debug.Assert(length == 16, "invalid length for SqlGuid type!");
5129                     value.SqlGuid = new SqlGuid(unencryptedBytes, true);   // doesn't copy the byte array
5130                     break;
5131                 }
5132
5133                 case TdsEnums.SQLBINARY:
5134                 case TdsEnums.SQLBIGBINARY:
5135                 case TdsEnums.SQLBIGVARBINARY:
5136                 case TdsEnums.SQLVARBINARY:
5137                 case TdsEnums.SQLIMAGE:
5138                 {
5139                     // Note: Better not come here with plp data!!
5140                     Debug.Assert(length <= TdsEnums.MAXSIZE, "Plp data decryption attempted");
5141
5142                     // If this is a fixed length type, pad with zeros to get to the fixed length size.
5143                     if (tdsType == TdsEnums.SQLBINARY || tdsType == TdsEnums.SQLBIGBINARY) {
5144                         byte[] bytes = new byte[md.baseTI.length];
5145                         Buffer.BlockCopy(unencryptedBytes, 0, bytes, 0, unencryptedBytes.Length);
5146                         unencryptedBytes = bytes;
5147                     }
5148
5149                     value.SqlBinary = new SqlBinary(unencryptedBytes, true);   // doesn't copy the byte array
5150                     break;
5151                 }
5152
5153                 case TdsEnums.SQLDECIMALN:
5154                 case TdsEnums.SQLNUMERICN:
5155                     // Check the sign from the first byte.
5156                     int index = 0;
5157                     byteValue = unencryptedBytes[index++];
5158                     bool fPositive = (1 == byteValue);
5159
5160                     // Now read the 4 next integers which contain the actual value.
5161                     length = checked((int)length-1);
5162                     int[] bits = new int[4];
5163                     int decLength = length>>2;
5164                     for (int i = 0; i < decLength; i++) {
5165                         // up to 16 bytes of data following the sign byte
5166                         bits[i] = BitConverter.ToInt32(unencryptedBytes, index);
5167                         index += 4;
5168                     }
5169                     value.SetToDecimal (md.baseTI.precision, md.baseTI.scale, fPositive, bits);
5170                     break;
5171
5172                 case TdsEnums.SQLCHAR:
5173                 case TdsEnums.SQLBIGCHAR:
5174                 case TdsEnums.SQLVARCHAR:
5175                 case TdsEnums.SQLBIGVARCHAR:
5176                 case TdsEnums.SQLTEXT:
5177                     {
5178                         System.Text.Encoding encoding = md.baseTI.encoding;
5179
5180                         if (null == encoding) {
5181                             encoding = _defaultEncoding;
5182                         }
5183
5184                         if (null == encoding) {
5185                             ThrowUnsupportedCollationEncountered(stateObj);
5186                         }
5187
5188                         string strValue = encoding.GetString(unencryptedBytes, 0, length);
5189
5190                         // If this is a fixed length type, pad with spaces to get to the fixed length size.
5191                         if (tdsType == TdsEnums.SQLCHAR || tdsType == TdsEnums.SQLBIGCHAR) {
5192                             strValue = strValue.PadRight(md.baseTI.length);
5193                         }
5194
5195                         value.SetToString(strValue);
5196                         break;
5197                     }
5198
5199                 case TdsEnums.SQLNCHAR:
5200                 case TdsEnums.SQLNVARCHAR:
5201                 case TdsEnums.SQLNTEXT:
5202                     {
5203                         string strValue = System.Text.Encoding.Unicode.GetString(unencryptedBytes, 0, length);
5204
5205                         // If this is a fixed length type, pad with spaces to get to the fixed length size.
5206                         if (tdsType == TdsEnums.SQLNCHAR) {
5207                             strValue = strValue.PadRight(md.baseTI.length / ADP.CharSize);
5208                         }
5209
5210                         value.SetToString(strValue);
5211                         break;
5212                     }
5213
5214                 case TdsEnums.SQLDATE:
5215                     Debug.Assert(length == 3, "invalid length for date type!");
5216                     value.SetToDate(unencryptedBytes);
5217                     break;
5218
5219                 case TdsEnums.SQLTIME:
5220                     // We normalize to maximum precision to allow conversion across different precisions.
5221                     Debug.Assert(length == 5, "invalid length for time type!");
5222                     value.SetToTime(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale);
5223                     break;
5224
5225                 case TdsEnums.SQLDATETIME2:
5226                     // We normalize to maximum precision to allow conversion across different precisions.
5227                     Debug.Assert(length == 8, "invalid length for datetime2 type!");
5228                     value.SetToDateTime2(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale);
5229                     break;
5230
5231                 case TdsEnums.SQLDATETIMEOFFSET:
5232                     // We normalize to maximum precision to allow conversion across different precisions.
5233                     Debug.Assert(length == 10, "invalid length for datetimeoffset type!");
5234                     value.SetToDateTimeOffset(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale);
5235                     break;
5236
5237                 default:
5238                     MetaType metaType = md.baseTI.metaType;
5239
5240                     // If we don't have a metatype already, construct one to get the proper type name.
5241                     if (metaType == null) {
5242                         metaType = MetaType.GetSqlDataType(tdsType, userType:0, length:length);
5243                     }
5244
5245                     throw SQL.UnsupportedDatatypeEncryption(metaType.TypeName);
5246             }
5247
5248             return true;
5249         }
5250
5251         internal bool TryReadSqlValue(SqlBuffer value,
5252                                       SqlMetaDataPriv md,
5253                                       int length,
5254                                       TdsParserStateObject stateObj,
5255                                       SqlCommandColumnEncryptionSetting columnEncryptionOverride, 
5256                                       string columnName) {
5257             bool isPlp = md.metaType.IsPlp;
5258             byte tdsType = md.tdsType;
5259
5260             Debug.Assert(isPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!");
5261             if (isPlp) {
5262                 // We must read the column value completely, no matter what length is passed in
5263                 length = Int32.MaxValue;
5264             }
5265
5266             //DEVNOTE: When modifying the following routines (for deserialization) please pay attention to 
5267             // deserialization code in DecryptWithKey () method and modify it accordingly.
5268             switch (tdsType) {
5269                 case TdsEnums.SQLDECIMALN:
5270                 case TdsEnums.SQLNUMERICN:
5271                     if (!TryReadSqlDecimal(value, length, md.precision, md.scale, stateObj)) {
5272                         return false;
5273                     }
5274                     break;
5275
5276                 case TdsEnums.SQLUDT:
5277                 case TdsEnums.SQLBINARY:
5278                 case TdsEnums.SQLBIGBINARY:
5279                 case TdsEnums.SQLBIGVARBINARY:
5280                 case TdsEnums.SQLVARBINARY:
5281                 case TdsEnums.SQLIMAGE:
5282                     byte[] b = null;
5283
5284                     // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
5285                     if (isPlp) {
5286                         // If we are given -1 for length, then we read the entire value,
5287                         // otherwise only the requested amount, usually first chunk.
5288                         int ignored;
5289                         if (!stateObj.TryReadPlpBytes(ref b, 0, length, out ignored)) {
5290                             return false;
5291                         }
5292                     }
5293                     else {
5294                         //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
5295                         b = new byte[length];
5296                         if (!stateObj.TryReadByteArray(b, 0, length)) {
5297                             return false;
5298                         }
5299                     }
5300
5301                     if (md.isEncrypted 
5302                         && ((columnEncryptionOverride == SqlCommandColumnEncryptionSetting.Enabled
5303                             || columnEncryptionOverride == SqlCommandColumnEncryptionSetting.ResultSetOnly)
5304                             || (columnEncryptionOverride == SqlCommandColumnEncryptionSetting.UseConnectionSetting
5305                                 && _connHandler != null && _connHandler.ConnectionOptions != null
5306                                 && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled))) {
5307                         try {
5308                             // CipherInfo is present, decrypt and read
5309                             byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.ConnectionOptions.DataSource);
5310
5311                             if (unencryptedBytes != null) {
5312                                 DeserializeUnencryptedValue(value, unencryptedBytes, md, stateObj, md.NormalizationRuleVersion);
5313                             }
5314                         }
5315                         catch (Exception e) {
5316                             throw SQL.ColumnDecryptionFailed(columnName, null, e);
5317                         }
5318                     }
5319                     else {
5320                         value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array
5321                     }
5322                     break;
5323
5324                 case TdsEnums.SQLCHAR:
5325                 case TdsEnums.SQLBIGCHAR:
5326                 case TdsEnums.SQLVARCHAR:
5327                 case TdsEnums.SQLBIGVARCHAR:
5328                 case TdsEnums.SQLTEXT:
5329                 case TdsEnums.SQLNCHAR:
5330                 case TdsEnums.SQLNVARCHAR:
5331                 case TdsEnums.SQLNTEXT:
5332                     if (!TryReadSqlStringValue(value, tdsType, length, md.encoding, isPlp, stateObj)) {
5333                         return false;
5334                     }
5335                     break;
5336
5337                 case TdsEnums.SQLXMLTYPE:
5338                     // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader.
5339                     SqlCachedBuffer sqlBuf;
5340                     if (!SqlCachedBuffer.TryCreate(md, this, stateObj, out sqlBuf)) {
5341                         return false;
5342                     }
5343
5344                     value.SqlCachedBuffer = sqlBuf;
5345                     break;
5346
5347                 case TdsEnums.SQLDATE:
5348                 case TdsEnums.SQLTIME: 
5349                 case TdsEnums.SQLDATETIME2:
5350                 case TdsEnums.SQLDATETIMEOFFSET:
5351                     if (!TryReadSqlDateTime(value, tdsType, length, md.scale, stateObj)) {
5352                         return false;
5353                     }
5354                     break;
5355                     
5356                 default:
5357                     Debug.Assert(!isPlp, "ReadSqlValue calling ReadSqlValueInternal with plp data");
5358                     if (!TryReadSqlValueInternal(value, tdsType, length, stateObj)) {
5359                         return false;
5360                     }
5361                     break;
5362             }
5363
5364             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));
5365             return true;
5366         }
5367
5368         private bool TryReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj) {
5369             byte[] datetimeBuffer = new byte[length];
5370
5371             if (!stateObj.TryReadByteArray(datetimeBuffer, 0, length)) {
5372                 return false;
5373             }
5374
5375             switch (tdsType) {
5376                 case TdsEnums.SQLDATE:
5377                     Debug.Assert(length == 3, "invalid length for date type!");
5378                     value.SetToDate(datetimeBuffer);
5379                     break;
5380
5381                 case TdsEnums.SQLTIME:
5382                     Debug.Assert(3 <= length && length <= 5, "invalid length for time type!");
5383                     value.SetToTime(datetimeBuffer, length, scale, scale);
5384                     break;
5385
5386                 case TdsEnums.SQLDATETIME2:
5387                     Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!");
5388                     value.SetToDateTime2(datetimeBuffer, length, scale, scale);
5389                     break;
5390
5391                 case TdsEnums.SQLDATETIMEOFFSET:
5392                     Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!");
5393                     value.SetToDateTimeOffset(datetimeBuffer, length, scale, scale);
5394                     break;
5395
5396                 default:
5397                     Debug.Assert(false, "ReadSqlDateTime is called with the wrong tdsType");
5398                     break;
5399             }
5400
5401             return true;
5402         }
5403
5404         internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, TdsParserStateObject stateObj) {
5405             switch (tdsType) {
5406                 case TdsEnums.SQLBIT:
5407                 case TdsEnums.SQLBITN:
5408                     Debug.Assert(length == 1, "invalid length for SqlBoolean type!");
5409                     byte byteValue;
5410                     if (!stateObj.TryReadByte(out byteValue)) {
5411                         return false;
5412                     }
5413                     value.Boolean = (byteValue != 0);
5414                     break;
5415
5416                 case TdsEnums.SQLINTN:
5417                     if (length == 1) {
5418                         goto case TdsEnums.SQLINT1;
5419                     }
5420                     else if (length == 2) {
5421                         goto case TdsEnums.SQLINT2;
5422                     }
5423                     else if (length == 4) {
5424                         goto case TdsEnums.SQLINT4;
5425                     }
5426                     else {
5427                         goto case TdsEnums.SQLINT8;
5428                     }
5429
5430                 case TdsEnums.SQLINT1:
5431                     Debug.Assert(length == 1, "invalid length for SqlByte type!");
5432                     if (!stateObj.TryReadByte(out byteValue)) {
5433                         return false;
5434                     }
5435                     value.Byte = byteValue;
5436                     break;
5437
5438                 case TdsEnums.SQLINT2:
5439                     Debug.Assert(length == 2, "invalid length for SqlInt16 type!");
5440                     short shortValue;
5441                     if (!stateObj.TryReadInt16(out shortValue)) {
5442                         return false;
5443                     }
5444                     value.Int16 = shortValue;
5445                     break;
5446
5447                 case TdsEnums.SQLINT4:
5448                     Debug.Assert(length == 4, "invalid length for SqlInt32 type!");
5449                     int intValue;
5450                     if (!stateObj.TryReadInt32(out intValue)) {
5451                         return false;
5452                     }
5453                     value.Int32 = intValue;
5454                     break;
5455
5456                 case TdsEnums.SQLINT8:
5457                     Debug.Assert(length == 8, "invalid length for SqlInt64 type!");
5458                     long longValue;
5459                     if (!stateObj.TryReadInt64(out longValue)) {
5460                         return false;
5461                     }
5462                     value.Int64 = longValue;
5463                     break;
5464
5465                 case TdsEnums.SQLFLTN:
5466                     if (length == 4) {
5467                         goto case TdsEnums.SQLFLT4;
5468                     }
5469                     else {
5470                         goto case TdsEnums.SQLFLT8;
5471                     }
5472
5473                 case TdsEnums.SQLFLT4:
5474                     Debug.Assert(length == 4, "invalid length for SqlSingle type!");
5475                     float singleValue;
5476                     if (!stateObj.TryReadSingle(out singleValue)) {
5477                         return false;
5478                     }
5479                     value.Single = singleValue;
5480                     break;
5481
5482                 case TdsEnums.SQLFLT8:
5483                     Debug.Assert(length == 8, "invalid length for SqlDouble type!");
5484                     double doubleValue;
5485                     if (!stateObj.TryReadDouble(out doubleValue)) {
5486                         return false;
5487                     }
5488                     value.Double = doubleValue;
5489                     break;
5490
5491                 case TdsEnums.SQLMONEYN:
5492                     if (length == 4) {
5493                         goto case TdsEnums.SQLMONEY4;
5494                     }
5495                     else {
5496                         goto case TdsEnums.SQLMONEY;
5497                     }
5498
5499                 case TdsEnums.SQLMONEY:
5500                 {
5501                     int mid;
5502                     uint lo;
5503
5504                     if (!stateObj.TryReadInt32(out mid)) {
5505                         return false;
5506                     }
5507                     if (!stateObj.TryReadUInt32(out lo)) {
5508                         return false;
5509                     }
5510
5511                     long l = (((long)mid) << 0x20) + ((long)lo);
5512
5513                     value.SetToMoney(l);
5514                     break;
5515                 }
5516
5517                 case TdsEnums.SQLMONEY4:
5518                     if (!stateObj.TryReadInt32(out intValue)) {
5519                         return false;
5520                     }
5521                     value.SetToMoney(intValue);
5522                     break;
5523
5524                 case TdsEnums.SQLDATETIMN:
5525                     if (length == 4) {
5526                         goto case TdsEnums.SQLDATETIM4;
5527                     }
5528                     else {
5529                         goto case TdsEnums.SQLDATETIME;
5530                     }
5531
5532                 case TdsEnums.SQLDATETIM4:
5533                     ushort daypartShort, timepartShort;
5534                     if (!stateObj.TryReadUInt16(out daypartShort)) {
5535                         return false;
5536                     }
5537                     if (!stateObj.TryReadUInt16(out timepartShort)) {
5538                         return false;
5539                     }
5540                     value.SetToDateTime(daypartShort, timepartShort * SqlDateTime.SQLTicksPerMinute);
5541                     break;
5542
5543                 case TdsEnums.SQLDATETIME:
5544                     int daypart;
5545                     uint timepart;
5546                     if (!stateObj.TryReadInt32(out daypart)) {
5547                         return false;
5548                     }
5549                     if (!stateObj.TryReadUInt32(out timepart)) {
5550                         return false;
5551                     }
5552                     value.SetToDateTime(daypart, (int)timepart);
5553                     break;
5554
5555                 case TdsEnums.SQLUNIQUEID:
5556                 {
5557                     Debug.Assert(length == 16, "invalid length for SqlGuid type!");
5558
5559                     byte[] b = new byte[length];
5560
5561                     if (!stateObj.TryReadByteArray(b, 0, length)) {
5562                         return false;
5563                     }
5564                     value.SqlGuid = new SqlGuid(b, true);   // doesn't copy the byte array
5565                     break;
5566                 }
5567
5568                 case TdsEnums.SQLBINARY:
5569                 case TdsEnums.SQLBIGBINARY:
5570                 case TdsEnums.SQLBIGVARBINARY:
5571                 case TdsEnums.SQLVARBINARY:
5572                 case TdsEnums.SQLIMAGE:
5573                 {
5574                     // Note: Better not come here with plp data!!
5575                     Debug.Assert(length <= TdsEnums.MAXSIZE);
5576                     byte[] b = new byte[length];
5577                     if (!stateObj.TryReadByteArray(b, 0, length)) {
5578                         return false;
5579                     }
5580                     value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array
5581
5582                     break;
5583                 }
5584
5585                 case TdsEnums.SQLVARIANT:
5586                     if (!TryReadSqlVariant(value, length, stateObj)) {
5587                         return false;
5588                     }
5589                     break;
5590
5591                 default:
5592                     Debug.Assert(false, "Unknown SqlType!" + tdsType.ToString(CultureInfo.InvariantCulture));
5593                     break;
5594             } // switch
5595
5596             return true;
5597         }
5598
5599         //
5600         // Read in a SQLVariant
5601         //
5602         // SQLVariant looks like:
5603         // struct
5604         // {
5605         //      BYTE TypeTag
5606         //      BYTE cbPropBytes
5607         //      BYTE[] Properties
5608         //      BYTE[] DataVal
5609         // }
5610         internal bool TryReadSqlVariant(SqlBuffer value, int lenTotal, TdsParserStateObject stateObj) {
5611             Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variaint in pre-SQL2000 server!");
5612             // get the SQLVariant type
5613             byte type;
5614             if (!stateObj.TryReadByte(out type)) {
5615                 return false;
5616             }
5617             ushort lenMax = 0; // maximum lenData of value inside variant
5618
5619             // read cbPropBytes
5620             byte cbPropsActual;
5621             if (!stateObj.TryReadByte(out cbPropsActual)) {
5622                 return false;
5623             }
5624             MetaType mt = MetaType.GetSqlDataType(type, 0 /*no user datatype*/, 0 /* no lenData, non-nullable type */);
5625             byte cbPropsExpected = mt.PropBytes;
5626
5627             int lenConsumed = TdsEnums.SQLVARIANT_SIZE + cbPropsActual; // type, count of propBytes, and actual propBytes
5628             int lenData = lenTotal - lenConsumed; // length of actual data
5629
5630             // read known properties and skip unknown properties
5631             Debug.Assert(cbPropsActual >= cbPropsExpected, "cbPropsActual is less that cbPropsExpected!");
5632
5633             //
5634             // now read the value
5635             //
5636             switch (type) {
5637                 case TdsEnums.SQLBIT:
5638                 case TdsEnums.SQLINT1:
5639                 case TdsEnums.SQLINT2:
5640                 case TdsEnums.SQLINT4:
5641                 case TdsEnums.SQLINT8:
5642                 case TdsEnums.SQLFLT4:
5643                 case TdsEnums.SQLFLT8:
5644                 case TdsEnums.SQLMONEY:
5645                 case TdsEnums.SQLMONEY4:
5646                 case TdsEnums.SQLDATETIME:
5647                 case TdsEnums.SQLDATETIM4:
5648                 case TdsEnums.SQLUNIQUEID:
5649                     if (!TryReadSqlValueInternal(value, type, lenData, stateObj)) {
5650                         return false;
5651                     }
5652                     break;
5653
5654                 case TdsEnums.SQLDECIMALN:
5655                 case TdsEnums.SQLNUMERICN:
5656                 {
5657                     Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for decimal/numeric type!");
5658
5659                     byte precision;
5660                     if (!stateObj.TryReadByte(out precision)) {
5661                         return false;
5662                     }
5663                     byte scale;
5664                     if (!stateObj.TryReadByte(out scale)) {
5665                         return false;
5666                     }
5667
5668                     // skip over unknown properties
5669                     if (cbPropsActual > cbPropsExpected) {
5670                         if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
5671                             return false;
5672                         }
5673                     }
5674
5675                     if (!TryReadSqlDecimal(value, TdsEnums.MAX_NUMERIC_LEN, precision, scale, stateObj)) {
5676                         return false;
5677                     }
5678                     break;
5679                 }
5680
5681                 case TdsEnums.SQLBIGBINARY:
5682                 case TdsEnums.SQLBIGVARBINARY:
5683                     //Debug.Assert(TdsEnums.VARNULL == lenData, "SqlVariant: data length for Binary indicates null?");
5684                     Debug.Assert(cbPropsExpected == 2, "SqlVariant: invalid PropBytes for binary type!");
5685
5686                     if (!stateObj.TryReadUInt16(out lenMax)) {
5687                         return false;
5688                     }
5689                     Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarbinary(max) in a sqlvariant");
5690
5691                     // skip over unknown properties
5692                     if (cbPropsActual > cbPropsExpected) {
5693                         if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
5694                             return false;
5695                         }
5696                     }
5697
5698                     goto case TdsEnums.SQLBIT;
5699
5700                 case TdsEnums.SQLBIGCHAR:
5701                 case TdsEnums.SQLBIGVARCHAR:
5702                 case TdsEnums.SQLNCHAR:
5703                 case TdsEnums.SQLNVARCHAR:
5704                 {
5705                     Debug.Assert(cbPropsExpected == 7, "SqlVariant: invalid PropBytes for character type!");
5706
5707                     SqlCollation collation;
5708                     if (!TryProcessCollation(stateObj, out collation)) {
5709                         return false;
5710                     }
5711
5712                     if (!stateObj.TryReadUInt16(out lenMax)) {
5713                         return false;
5714                     }
5715                     Debug.Assert(lenMax != TdsEnums.SQL_USHORTVARMAXLEN, "bigvarchar(max) or nvarchar(max) in a sqlvariant");
5716
5717                     // skip over unknown properties
5718                     if (cbPropsActual > cbPropsExpected) {
5719                         if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
5720                             return false;
5721                         }
5722                     }
5723
5724                     Encoding encoding = Encoding.GetEncoding(GetCodePage(collation, stateObj));
5725                     if (!TryReadSqlStringValue(value, type, lenData, encoding, false, stateObj)) {
5726                         return false;
5727                     }
5728                     break;
5729                 }
5730                 case TdsEnums.SQLDATE:
5731                     if (!TryReadSqlDateTime(value, type, lenData, 0, stateObj)) {
5732                         return false;
5733                     }
5734                     break;
5735
5736                 case TdsEnums.SQLTIME:
5737                 case TdsEnums.SQLDATETIME2:
5738                 case TdsEnums.SQLDATETIMEOFFSET:
5739                 {
5740                     Debug.Assert(cbPropsExpected == 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!");
5741
5742                     byte scale;
5743                     if (!stateObj.TryReadByte(out scale)) {
5744                         return false;
5745                     }
5746
5747                     // skip over unknown properties
5748                     if (cbPropsActual > cbPropsExpected) {
5749                         if (!stateObj.TrySkipBytes(cbPropsActual - cbPropsExpected)) {
5750                             return false;
5751                         }
5752                     }
5753
5754                     if (!TryReadSqlDateTime(value, type, lenData, scale, stateObj)) {
5755                         return false;
5756                     }
5757                     break;
5758                 }
5759
5760                 default:
5761                     Debug.Assert(false, "Unknown tds type in SqlVariant!" + type.ToString(CultureInfo.InvariantCulture));
5762                     break;
5763             } // switch
5764
5765             return true;
5766         }
5767
5768         //
5769         // Translates a com+ object -> SqlVariant
5770         // when the type is ambiguous, we always convert to the bigger type
5771         // note that we also write out the maxlen and actuallen members (4 bytes each)
5772         // in addition to the SQLVariant structure
5773         //
5774         internal Task WriteSqlVariantValue(object value, int length, int offset, TdsParserStateObject stateObj,  bool canAccumulate = true) {
5775             Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
5776
5777             // handle null values
5778             if (ADP.IsNull(value)) {
5779                 WriteInt(TdsEnums.FIXEDNULL, stateObj); //maxlen
5780                 WriteInt(TdsEnums.FIXEDNULL, stateObj); //actuallen
5781                 return null;
5782             }
5783
5784             MetaType mt = MetaType.GetMetaTypeFromValue(value);
5785
5786             // Special case data type correction for SqlMoney inside a SqlVariant.
5787             if ((TdsEnums.SQLNUMERICN == mt.TDSType) && (8 == length)) {
5788                 // The caller will coerce all SqlTypes to native CLR types, which means SqlMoney will 
5789                 // coerce to decimal/SQLNUMERICN (via SqlMoney.Value call).  In the case where the original 
5790                 // value was SqlMoney the caller will also pass in the metadata length for the SqlMoney type 
5791                 // which is 8 bytes.  To honor the intent of the caller here we coerce this special case 
5792                 // input back to SqlMoney from decimal/SQLNUMERICN.
5793                 mt = MetaType.GetMetaTypeFromValue(new SqlMoney((decimal)value));
5794             }
5795
5796             if (mt.IsAnsiType) {
5797                 length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding);
5798             }
5799
5800             // max and actual len are equal to
5801             // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
5802             WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // maxLen
5803             WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // actualLen
5804
5805             // write the SQLVariant header (type and cbPropBytes)
5806             stateObj.WriteByte(mt.TDSType);
5807             stateObj.WriteByte(mt.PropBytes);
5808
5809             // now write the actual PropBytes and data
5810             switch (mt.TDSType) {
5811                 case TdsEnums.SQLFLT4:
5812                     WriteFloat((Single)value, stateObj);
5813                     break;
5814
5815                 case TdsEnums.SQLFLT8:
5816                     WriteDouble((Double)value, stateObj);
5817                     break;
5818
5819                 case TdsEnums.SQLINT8:
5820                     WriteLong((Int64)value, stateObj);
5821                     break;
5822
5823                 case TdsEnums.SQLINT4:
5824                     WriteInt((Int32)value, stateObj);
5825                     break;
5826
5827                 case TdsEnums.SQLINT2:
5828                     WriteShort((Int16)value, stateObj);
5829                     break;
5830
5831                 case TdsEnums.SQLINT1:
5832                     stateObj.WriteByte((byte)value);
5833                     break;
5834
5835                 case TdsEnums.SQLBIT:
5836                     if ((bool)value == true)
5837                         stateObj.WriteByte(1);
5838                     else
5839                         stateObj.WriteByte(0);
5840
5841                     break;
5842
5843                 case TdsEnums.SQLBIGVARBINARY:
5844                     {
5845                         byte[] b = (byte[])value;
5846
5847                         WriteShort(length, stateObj); // propbytes: varlen
5848                         return stateObj.WriteByteArray(b, length, offset, canAccumulate);
5849                     }
5850
5851                 case TdsEnums.SQLBIGVARCHAR:
5852                     {
5853                         string s = (string)value;
5854
5855                         WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
5856                         stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
5857                         WriteShort(length, stateObj); // propbyte: varlen
5858                         return WriteEncodingChar(s, _defaultEncoding, stateObj,  canAccumulate);
5859                     }
5860
5861                 case TdsEnums.SQLUNIQUEID: 
5862                     {
5863                         System.Guid guid = (System.Guid)value;
5864                         byte[] b = guid.ToByteArray();
5865
5866                         Debug.Assert((length == b.Length) && (length == 16), "Invalid length for guid type in com+ object");
5867                         stateObj.WriteByteArray(b, length, 0);
5868                         break;
5869                     }
5870
5871                 case TdsEnums.SQLNVARCHAR:
5872                     {
5873                         string s = (string)value;
5874
5875                         WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
5876                         stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
5877                         WriteShort(length, stateObj); // propbyte: varlen
5878
5879                         // string takes cchar, not cbyte so convert
5880                         length >>= 1;
5881                         return WriteString(s, length, offset, stateObj,  canAccumulate);
5882                     }
5883
5884                 case TdsEnums.SQLDATETIME: 
5885                     {
5886                         TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8);
5887
5888                         WriteInt(dt.days, stateObj);
5889                         WriteInt(dt.time, stateObj);
5890                         break;
5891                     }
5892
5893                 case TdsEnums.SQLMONEY: 
5894                     {
5895                         WriteCurrency((Decimal)value, 8, stateObj);
5896                         break;
5897                     }
5898
5899                 case TdsEnums.SQLNUMERICN: 
5900                     {
5901                         stateObj.WriteByte(mt.Precision); //propbytes: precision
5902                         stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
5903                         WriteDecimal((Decimal)value, stateObj);
5904                         break;
5905                     }
5906
5907                 case TdsEnums.SQLTIME:
5908                     stateObj.WriteByte(mt.Scale); //propbytes: scale
5909                     WriteTime((TimeSpan)value, mt.Scale, length, stateObj);
5910                     break;
5911
5912                 case TdsEnums.SQLDATETIMEOFFSET:
5913                     stateObj.WriteByte(mt.Scale); //propbytes: scale
5914                     WriteDateTimeOffset((DateTimeOffset)value, mt.Scale, length, stateObj);
5915                     break;
5916
5917                 default:
5918                     Debug.Assert(false, "unknown tds type for sqlvariant!");
5919                     break;
5920             } // switch
5921             // return point for accumulated writes, note: non-accumulated writes returned from their case statements
5922             return null;
5923         }
5924
5925 // todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider
5926 // combining these tow methods.
5927
5928         //
5929         // Translates a com+ object -> SqlVariant
5930         // when the type is ambiguous, we always convert to the bigger type
5931         // note that we also write out the maxlen and actuallen members (4 bytes each)
5932         // in addition to the SQLVariant structure
5933         //
5934         // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value.
5935         // Therefore the sql_variant value must not include the MaxLength. This is the major difference
5936         // between this method and WriteSqlVariantValue above.
5937         //
5938         internal Task WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj, bool canAccumulate = true) {
5939             Debug.Assert(_isShiloh == true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
5940
5941             // handle null values
5942             if ((null == value) || (DBNull.Value == value)) {
5943                 WriteInt(TdsEnums.FIXEDNULL, stateObj);
5944                 return null;
5945             }
5946
5947             MetaType metatype = MetaType.GetMetaTypeFromValue(value);
5948             int length = 0;
5949
5950             if (metatype.IsAnsiType) {
5951                 length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding);
5952             }
5953
5954             switch (metatype.TDSType) {
5955                 case TdsEnums.SQLFLT4:
5956                     WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
5957                     WriteFloat((Single)value, stateObj);
5958                     break;
5959
5960                 case TdsEnums.SQLFLT8:
5961                     WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
5962                     WriteDouble((Double)value, stateObj);
5963                     break;
5964
5965                 case TdsEnums.SQLINT8:
5966                     WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
5967                     WriteLong((Int64)value, stateObj);
5968                     break;
5969
5970                 case TdsEnums.SQLINT4:
5971                     WriteSqlVariantHeader(6, metatype.TDSType, metatype.PropBytes, stateObj);
5972                     WriteInt((Int32)value, stateObj);
5973                     break;
5974
5975                 case TdsEnums.SQLINT2:
5976                     WriteSqlVariantHeader(4, metatype.TDSType, metatype.PropBytes, stateObj);
5977                     WriteShort((Int16)value, stateObj);
5978                     break;
5979
5980                 case TdsEnums.SQLINT1:
5981                     WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj);
5982                     stateObj.WriteByte((byte)value);
5983                     break;
5984
5985                 case TdsEnums.SQLBIT:
5986                     WriteSqlVariantHeader(3, metatype.TDSType, metatype.PropBytes, stateObj);
5987                     if ((bool)value == true)
5988                         stateObj.WriteByte(1);
5989                     else
5990                         stateObj.WriteByte(0);
5991
5992                     break;
5993
5994                 case TdsEnums.SQLBIGVARBINARY:
5995                     {
5996                         byte[] b = (byte[])value;
5997
5998                         length = b.Length;
5999                         WriteSqlVariantHeader(4 + length, metatype.TDSType, metatype.PropBytes, stateObj);
6000                         WriteShort(length, stateObj); // propbytes: varlen
6001                         return stateObj.WriteByteArray(b, length, 0,  canAccumulate);
6002                     }
6003
6004                 case TdsEnums.SQLBIGVARCHAR:
6005                     {
6006                         string s = (string)value;
6007
6008                         length = s.Length;
6009                         WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj);
6010                         WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
6011                         stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
6012                         WriteShort(length, stateObj);
6013                         return WriteEncodingChar(s, _defaultEncoding, stateObj, canAccumulate);
6014                     }
6015
6016                 case TdsEnums.SQLUNIQUEID: 
6017                     {
6018                         System.Guid guid = (System.Guid)value;
6019                         byte[] b = guid.ToByteArray();
6020
6021                         length = b.Length;
6022                         Debug.Assert(length == 16, "Invalid length for guid type in com+ object");
6023                         WriteSqlVariantHeader(18, metatype.TDSType, metatype.PropBytes, stateObj);
6024                         stateObj.WriteByteArray(b, length, 0);
6025                         break;
6026                     }
6027
6028                 case TdsEnums.SQLNVARCHAR:
6029                     {
6030                         string s = (string)value;
6031
6032                         length = s.Length * 2;
6033                         WriteSqlVariantHeader(9 + length, metatype.TDSType, metatype.PropBytes, stateObj);
6034                         WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
6035                         stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
6036                         WriteShort(length, stateObj); // propbyte: varlen
6037
6038                         // string takes cchar, not cbyte so convert
6039                         length >>= 1;
6040                         return WriteString(s, length, 0, stateObj, canAccumulate);
6041                     }
6042
6043                 case TdsEnums.SQLDATETIME: 
6044                     {
6045                         TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8);
6046
6047                         WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
6048                         WriteInt(dt.days, stateObj);
6049                         WriteInt(dt.time, stateObj);
6050                         break;
6051                     }
6052
6053                 case TdsEnums.SQLMONEY: 
6054                     {
6055                         WriteSqlVariantHeader(10, metatype.TDSType, metatype.PropBytes, stateObj);
6056                         WriteCurrency((Decimal)value, 8, stateObj);
6057                         break;
6058                     }
6059
6060                 case TdsEnums.SQLNUMERICN: 
6061                     {
6062                         WriteSqlVariantHeader(21, metatype.TDSType, metatype.PropBytes, stateObj);
6063                         stateObj.WriteByte(metatype.Precision); //propbytes: precision
6064                         stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
6065                         WriteDecimal((Decimal)value, stateObj);
6066                         break;
6067                     }
6068
6069                 case TdsEnums.SQLTIME:
6070                     WriteSqlVariantHeader(8, metatype.TDSType, metatype.PropBytes, stateObj);
6071                     stateObj.WriteByte(metatype.Scale); //propbytes: scale
6072                     WriteTime((TimeSpan)value, metatype.Scale, 5, stateObj);
6073                     break;
6074
6075                 case TdsEnums.SQLDATETIMEOFFSET:
6076                     WriteSqlVariantHeader(13, metatype.TDSType, metatype.PropBytes, stateObj);
6077                     stateObj.WriteByte(metatype.Scale); //propbytes: scale
6078                     WriteDateTimeOffset((DateTimeOffset)value, metatype.Scale, 10, stateObj);
6079                     break;
6080
6081                 default:
6082                     Debug.Assert(false, "unknown tds type for sqlvariant!");
6083                     break;
6084             } // switch
6085             // return point for accumualated writes, note: non-accumulated writes returned from their case statements
6086             return null;
6087         }
6088
6089         internal void WriteSqlVariantHeader(int length, byte tdstype, byte propbytes, TdsParserStateObject stateObj) {
6090             WriteInt(length, stateObj);
6091             stateObj.WriteByte(tdstype);
6092             stateObj.WriteByte(propbytes);
6093         }
6094
6095         internal void WriteSqlVariantDateTime2(DateTime value, TdsParserStateObject stateObj) 
6096         {
6097             MSS.SmiMetaData dateTime2MetaData = MSS.SmiMetaData.DefaultDateTime2;
6098             // NOTE: 3 bytes added here to support additional header information for variant - internal type, scale prop, scale
6099             WriteSqlVariantHeader((int)(dateTime2MetaData.MaxLength + 3), TdsEnums.SQLDATETIME2, 1 /* one scale prop */, stateObj);
6100             stateObj.WriteByte(dateTime2MetaData.Scale); //scale property
6101             WriteDateTime2(value, dateTime2MetaData.Scale, (int)(dateTime2MetaData.MaxLength), stateObj);
6102         }
6103
6104         internal void WriteSqlVariantDate(DateTime value, TdsParserStateObject stateObj) 
6105         {
6106             MSS.SmiMetaData dateMetaData = MSS.SmiMetaData.DefaultDate;
6107             // NOTE: 2 bytes added here to support additional header information for variant - internal type, scale prop (ignoring scale here)
6108             WriteSqlVariantHeader((int)(dateMetaData.MaxLength + 2), TdsEnums.SQLDATE, 0 /* one scale prop */, stateObj);
6109             WriteDate(value, stateObj);
6110         }
6111
6112         private byte[] SerializeSqlMoney(SqlMoney value, int length, TdsParserStateObject stateObj) {
6113             return SerializeCurrency(value.Value, length, stateObj);
6114         }
6115
6116         private void WriteSqlMoney(SqlMoney value, int length, TdsParserStateObject stateObj) {
6117             // 
6118             int[] bits = Decimal.GetBits(value.Value);
6119
6120             // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6121             bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
6122             long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0];
6123
6124             if (isNeg)
6125                 l = -l;
6126
6127             if (length == 4) {
6128                 Decimal decimalValue = value.Value;
6129
6130                 // validate the value can be represented as a small money
6131                 if (decimalValue < TdsEnums.SQL_SMALL_MONEY_MIN || decimalValue > TdsEnums.SQL_SMALL_MONEY_MAX) {
6132                     throw SQL.MoneyOverflow(decimalValue.ToString(CultureInfo.InvariantCulture));
6133                 }
6134
6135                 WriteInt((int)l, stateObj);
6136             }
6137             else {
6138                 WriteInt((int)(l >> 0x20), stateObj);
6139                 WriteInt((int)l, stateObj);
6140             }
6141         }
6142
6143         private byte[] SerializeCurrency(Decimal value, int length, TdsParserStateObject stateObj) {
6144             SqlMoney m = new SqlMoney(value);
6145             int[] bits = Decimal.GetBits(m.Value);
6146
6147             // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6148             bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
6149             long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0];
6150
6151             if (isNeg)
6152                 l = -l;
6153
6154             if (length == 4) {
6155                 // validate the value can be represented as a small money
6156                 if (value < TdsEnums.SQL_SMALL_MONEY_MIN || value > TdsEnums.SQL_SMALL_MONEY_MAX) {
6157                     throw SQL.MoneyOverflow(value.ToString(CultureInfo.InvariantCulture));
6158                 }
6159
6160                 // We normalize to allow conversion across data types. SMALLMONEY is serialized into a MONEY.
6161                 length = 8;
6162             }
6163
6164             Debug.Assert (8 == length, "invalid length in SerializeCurrency");
6165             if (null == stateObj._bLongBytes) {
6166                 stateObj._bLongBytes = new byte[8];
6167             }
6168
6169             byte[] bytes = stateObj._bLongBytes;
6170             int current = 0;
6171
6172             byte[] bytesPart = SerializeInt((int)(l >> 0x20), stateObj);
6173             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6174             current += 4;
6175
6176             bytesPart = SerializeInt((int)l, stateObj);
6177             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6178
6179             return bytes;
6180         }
6181
6182         private void WriteCurrency(Decimal value, int length, TdsParserStateObject stateObj) {
6183             SqlMoney m = new SqlMoney(value);
6184             int[] bits = Decimal.GetBits(m.Value);
6185
6186             // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6187             bool isNeg = (0 != (bits[3] & unchecked((int)0x80000000)));
6188             long l = ((long)(uint)bits[1]) << 0x20 | (uint)bits[0];
6189
6190             if (isNeg)
6191                 l = -l;
6192
6193             if (length == 4) {
6194                 // validate the value can be represented as a small money
6195                 if (value < TdsEnums.SQL_SMALL_MONEY_MIN || value > TdsEnums.SQL_SMALL_MONEY_MAX) {
6196                     throw SQL.MoneyOverflow(value.ToString(CultureInfo.InvariantCulture));
6197                 }
6198
6199                 WriteInt((int)l, stateObj);
6200             }
6201             else {
6202                 WriteInt((int)(l >> 0x20), stateObj);
6203                 WriteInt((int)l, stateObj);
6204             }
6205         }
6206
6207         private byte[] SerializeDate(DateTime value) {
6208             long days = value.Subtract(DateTime.MinValue).Days;
6209             return SerializePartialLong(days, 3);
6210         }
6211
6212         private void WriteDate(DateTime value, TdsParserStateObject stateObj) {
6213             long days = value.Subtract(DateTime.MinValue).Days;
6214             WritePartialLong(days, 3, stateObj);
6215         }
6216
6217         private byte[] SerializeTime(TimeSpan value, byte scale, int length) {
6218             if (0 > value.Ticks || value.Ticks >= TimeSpan.TicksPerDay) {
6219                 throw SQL.TimeOverflow(value.ToString());
6220             }
6221
6222             long time = value.Ticks / TdsEnums.TICKS_FROM_SCALE[scale];
6223
6224             // We normalize to maximum precision to allow conversion across different precisions.
6225             time = time * TdsEnums.TICKS_FROM_SCALE[scale];
6226             length = TdsEnums.MAX_TIME_LENGTH;
6227
6228             return SerializePartialLong(time, length);
6229         }
6230
6231         private void WriteTime(TimeSpan value, byte scale, int length, TdsParserStateObject stateObj) {
6232             if (0 > value.Ticks || value.Ticks >= TimeSpan.TicksPerDay) {
6233                 throw SQL.TimeOverflow(value.ToString());
6234             }
6235             long time = value.Ticks / TdsEnums.TICKS_FROM_SCALE[scale];
6236             WritePartialLong(time, length, stateObj);
6237         }
6238
6239         private byte[] SerializeDateTime2(DateTime value, byte scale, int length) {
6240             long time = value.TimeOfDay.Ticks / TdsEnums.TICKS_FROM_SCALE[scale]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
6241
6242             // We normalize to maximum precision to allow conversion across different precisions.
6243             time = time * TdsEnums.TICKS_FROM_SCALE[scale];
6244             length = TdsEnums.MAX_DATETIME2_LENGTH;
6245
6246             byte[] bytes = new byte[length];
6247             byte[] bytesPart;
6248             int current = 0;
6249
6250             bytesPart = SerializePartialLong(time, length - 3);
6251             Buffer.BlockCopy(bytesPart, 0, bytes, current, length - 3);
6252             current += length - 3;
6253
6254             bytesPart = SerializeDate(value);
6255             Buffer.BlockCopy(bytesPart, 0, bytes, current, 3);
6256
6257             return bytes;
6258         }
6259
6260         private void WriteDateTime2(DateTime value, byte scale, int length, TdsParserStateObject stateObj) {
6261             long time = value.TimeOfDay.Ticks / TdsEnums.TICKS_FROM_SCALE[scale]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
6262             WritePartialLong(time, length - 3, stateObj);
6263             WriteDate(value, stateObj);
6264         }
6265
6266         private byte[] SerializeDateTimeOffset(DateTimeOffset value, byte scale, int length) {
6267             byte[] bytesPart;
6268             int current = 0;
6269
6270             bytesPart = SerializeDateTime2(value.UtcDateTime, scale, length - 2);
6271             
6272             // We need to allocate the array after we have received the length of the serialized value
6273             // since it might be higher due to normalization.
6274             length = bytesPart.Length + 2;
6275             byte[] bytes = new byte[length];
6276
6277             Buffer.BlockCopy(bytesPart, 0, bytes, current, length - 2);
6278             current += length - 2;
6279
6280             Int16 offset = (Int16)value.Offset.TotalMinutes;
6281             bytes[current++] = (byte)(offset & 0xff);
6282             bytes[current++] = (byte)((offset >> 8) & 0xff);
6283
6284             return bytes;
6285         }
6286
6287         private void WriteDateTimeOffset(DateTimeOffset value, byte scale, int length, TdsParserStateObject stateObj) {
6288             WriteDateTime2(value.UtcDateTime, scale, length - 2, stateObj);
6289             Int16 offset = (Int16)value.Offset.TotalMinutes;
6290             stateObj.WriteByte((byte)(offset & 0xff));
6291             stateObj.WriteByte((byte)((offset >> 8) & 0xff));
6292         }
6293
6294         private bool TryReadSqlDecimal(SqlBuffer value, int length, byte precision, byte scale, TdsParserStateObject stateObj) {
6295             byte byteValue;
6296             if (!stateObj.TryReadByte(out byteValue)) {
6297                 return false;
6298             }
6299             bool fPositive = (1 == byteValue);
6300
6301             length = checked((int)length-1);
6302
6303             int[] bits;
6304             if (!TryReadDecimalBits(length, stateObj, out bits)) {
6305                 return false;
6306             }
6307
6308             value.SetToDecimal(precision, scale, fPositive, bits);
6309             return true;
6310         }
6311
6312         // @devnote: length should be size of decimal without the sign
6313         // @devnote: sign should have already been read off the wire
6314         private bool TryReadDecimalBits(int length, TdsParserStateObject stateObj, out int[] bits) {
6315             bits = stateObj._decimalBits; // used alloc'd array if we have one already
6316             int i;
6317
6318             if (null == bits)
6319                 bits = new int[4];
6320             else {
6321                 for (i = 0; i < bits.Length; i++)
6322                     bits[i] = 0;
6323             }
6324
6325             Debug.Assert((length > 0) &&
6326                          (length <= TdsEnums.MAX_NUMERIC_LEN - 1) &&
6327                          (length % 4 == 0), "decimal should have 4, 8, 12, or 16 bytes of data");
6328
6329             int decLength = length >> 2;
6330
6331             for (i = 0; i < decLength; i++) {
6332                 // up to 16 bytes of data following the sign byte
6333                 if (!stateObj.TryReadInt32(out bits[i])) {
6334                     return false;
6335                 }
6336             }
6337
6338             return true;
6339         }
6340
6341         static internal SqlDecimal AdjustSqlDecimalScale(SqlDecimal d, int newScale) {
6342             if (d.Scale != newScale) {
6343                 return SqlDecimal.AdjustScale(d, newScale - d.Scale, false /* Don't round, truncate.  MDAC 69229 */);
6344             }
6345
6346             return d;
6347         }
6348
6349         static internal decimal AdjustDecimalScale(decimal value, int newScale) {
6350             int oldScale = (Decimal.GetBits(value)[3] & 0x00ff0000) >> 0x10;
6351
6352             if (newScale != oldScale) {
6353                 SqlDecimal num = new SqlDecimal(value);
6354
6355                 num = SqlDecimal.AdjustScale(num, newScale - oldScale, false /* Don't round, truncate.  MDAC 69229 */);
6356                 return num.Value;
6357             }
6358
6359             return value;
6360         }
6361
6362         internal byte[] SerializeSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) {
6363             if (null == stateObj._bDecimalBytes) {
6364                 stateObj._bDecimalBytes = new byte[17];
6365             }
6366
6367             byte[] bytes = stateObj._bDecimalBytes;
6368             int current = 0;
6369
6370             // sign
6371             if (d.IsPositive)
6372                 bytes[current++] = 1;
6373             else
6374                 bytes[current++] = 0;
6375
6376             byte[] bytesPart = SerializeUnsignedInt(d.m_data1, stateObj);
6377             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6378             current += 4;
6379             bytesPart = SerializeUnsignedInt(d.m_data2, stateObj);
6380             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6381             current += 4;
6382             bytesPart = SerializeUnsignedInt(d.m_data3, stateObj);
6383             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6384             current += 4;
6385             bytesPart = SerializeUnsignedInt(d.m_data4, stateObj);
6386             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6387             
6388             return bytes;
6389         }
6390
6391         internal void WriteSqlDecimal(SqlDecimal d, TdsParserStateObject stateObj) {
6392             // sign
6393             if (d.IsPositive)
6394                 stateObj.WriteByte(1);
6395             else
6396                 stateObj.WriteByte(0);
6397
6398             WriteUnsignedInt(d.m_data1, stateObj);
6399             WriteUnsignedInt(d.m_data2, stateObj);
6400             WriteUnsignedInt(d.m_data3, stateObj);
6401             WriteUnsignedInt(d.m_data4, stateObj);
6402         }
6403
6404         private byte[] SerializeDecimal(decimal value, TdsParserStateObject stateObj) {
6405             int[] decimalBits = Decimal.GetBits(value);
6406             if (null == stateObj._bDecimalBytes) {
6407                 stateObj._bDecimalBytes = new byte[17];
6408             }
6409
6410             byte[] bytes = stateObj._bDecimalBytes;
6411             int current = 0;
6412
6413             /*
6414              Returns a binary representation of a Decimal. The return value is an integer
6415              array with four elements. Elements 0, 1, and 2 contain the low, middle, and
6416              high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
6417              the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
6418              unused; bits 16-23 contain a value between 0 and 28, indicating the power of
6419              10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
6420              30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
6421              meaning positive and 1 meaning negative.
6422
6423              SQLDECIMAL/SQLNUMERIC has a byte stream of:
6424              struct {
6425                  BYTE sign; // 1 if positive, 0 if negative
6426                  BYTE data[];
6427              }
6428
6429              For TDS 7.0 and above, there are always 17 bytes of data
6430             */
6431
6432             // write the sign (note that COM and SQL are opposite)
6433             if (0x80000000 == (decimalBits[3] & 0x80000000))
6434                 bytes[current++] = 0;
6435             else
6436                 bytes[current++] = 1;
6437
6438             byte[] bytesPart = SerializeInt(decimalBits[0], stateObj);
6439             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6440             current += 4;
6441             bytesPart = SerializeInt(decimalBits[1], stateObj);
6442             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6443             current += 4;
6444             bytesPart = SerializeInt(decimalBits[2], stateObj);
6445             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6446             current += 4;
6447             bytesPart = SerializeInt(0, stateObj);
6448             Buffer.BlockCopy(bytesPart, 0, bytes, current, 4);
6449
6450             return bytes;
6451         }
6452
6453         private void WriteDecimal(decimal value, TdsParserStateObject stateObj) {
6454             stateObj._decimalBits = Decimal.GetBits(value);
6455             Debug.Assert(null != stateObj._decimalBits, "decimalBits should be filled in at TdsExecuteRPC time");
6456
6457             /*
6458              Returns a binary representation of a Decimal. The return value is an integer
6459              array with four elements. Elements 0, 1, and 2 contain the low, middle, and
6460              high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
6461              the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
6462              unused; bits 16-23 contain a value between 0 and 28, indicating the power of
6463              10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
6464              30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
6465              meaning positive and 1 meaning negative.
6466
6467              SQLDECIMAL/SQLNUMERIC has a byte stream of:
6468              struct {
6469                  BYTE sign; // 1 if positive, 0 if negative
6470                  BYTE data[];
6471              }
6472
6473              For TDS 7.0 and above, there are always 17 bytes of data
6474             */
6475
6476             // write the sign (note that COM and SQL are opposite)
6477             if (0x80000000 == (stateObj._decimalBits[3] & 0x80000000))
6478                 stateObj.WriteByte(0);
6479             else
6480                 stateObj.WriteByte(1);
6481
6482             WriteInt(stateObj._decimalBits[0], stateObj);
6483             WriteInt(stateObj._decimalBits[1], stateObj);
6484             WriteInt(stateObj._decimalBits[2], stateObj);
6485             WriteInt(0, stateObj);
6486         }
6487
6488         private void WriteIdentifier(string s, TdsParserStateObject stateObj) {
6489             if (null != s) {
6490                 stateObj.WriteByte(checked((byte)s.Length));
6491                 WriteString(s, stateObj);
6492             }
6493             else {
6494                 stateObj.WriteByte((byte)0);
6495             }
6496         }
6497
6498         private void WriteIdentifierWithShortLength(string s, TdsParserStateObject stateObj) {
6499             if (null != s) {
6500                 WriteShort(checked((short)s.Length), stateObj);
6501                 WriteString(s, stateObj);
6502             }
6503             else {
6504                 WriteShort(0, stateObj);
6505             }
6506         }
6507
6508         private Task WriteString(string s, TdsParserStateObject stateObj,  bool canAccumulate = true) {
6509             return WriteString(s, s.Length, 0, stateObj, canAccumulate);
6510         }
6511
6512         internal byte[] SerializeCharArray(char[] carr, int length, int offset) {
6513             int cBytes = ADP.CharSize * length;
6514             byte[] bytes = new byte[cBytes];
6515
6516             CopyCharsToBytes(carr, offset, bytes, 0, length);
6517             return bytes;
6518         }
6519
6520         internal Task WriteCharArray(char[] carr, int length, int offset, TdsParserStateObject stateObj,  bool canAccumulate = true) {
6521             int cBytes = ADP.CharSize * length;
6522
6523             // Perf shortcut: If it fits, write directly to the outBuff
6524             if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) {
6525                 CopyCharsToBytes(carr, offset, stateObj._outBuff, stateObj._outBytesUsed, length);
6526                 stateObj._outBytesUsed += cBytes;
6527                 return null;
6528             }
6529             else {
6530                 if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
6531                     stateObj._bTmp = new byte[cBytes];
6532                 }
6533
6534                 CopyCharsToBytes(carr, offset, stateObj._bTmp, 0, length);
6535                 return stateObj.WriteByteArray(stateObj._bTmp, cBytes, 0,  canAccumulate);
6536             }
6537         }
6538
6539         internal byte[] SerializeString(string s, int length, int offset) {
6540             int cBytes = ADP.CharSize * length;
6541             byte[] bytes = new byte[cBytes];
6542
6543             CopyStringToBytes(s, offset, bytes, 0, length);
6544             return bytes;
6545         }
6546
6547         internal Task WriteString(string s, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true) {
6548             int cBytes = ADP.CharSize * length;
6549
6550             // Perf shortcut: If it fits, write directly to the outBuff
6551             if(cBytes < (stateObj._outBuff.Length - stateObj._outBytesUsed)) {
6552                 CopyStringToBytes(s, offset, stateObj._outBuff, stateObj._outBytesUsed, length);
6553                 stateObj._outBytesUsed += cBytes;
6554                 return null;
6555             }
6556             else {
6557                 if (stateObj._bTmp == null || stateObj._bTmp.Length < cBytes) {
6558                     stateObj._bTmp = new byte[cBytes];
6559                 }
6560
6561                 CopyStringToBytes(s, offset, stateObj._bTmp, 0, length);
6562                 return stateObj.WriteByteArray(stateObj._bTmp, cBytes, 0,  canAccumulate);
6563             }
6564         }
6565
6566
6567         private unsafe static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength) {
6568             // DEVNOTE: BE EXTREMELY CAREFULL in this method.  Since it pins the buffers and copies the memory
6569             //  directly, it bypasses all of the CLR's usual array-bounds checking.  For maintainability, the checks
6570             //  here should NOT be removed just because some other code will check them ahead of time.
6571             if (charLength < 0) {
6572                 throw ADP.InvalidDataLength(charLength);
6573             }
6574
6575             if (checked(sourceOffset + charLength) > source.Length || sourceOffset < 0) {
6576                 throw ADP.IndexOutOfRange(sourceOffset);
6577             }
6578
6579             // charLength >= 0 & checked conversion implies byteLength >= 0
6580             int byteLength = checked(charLength * ADP.CharSize);
6581
6582             if (checked(destOffset + byteLength) > dest.Length || destOffset < 0) {
6583                 throw ADP.IndexOutOfRange(destOffset);
6584             }
6585
6586             fixed (char* sourcePtr = source) {
6587                 char* srcPtr = sourcePtr;  // Can't increment the target of a Fixed statement
6588                 srcPtr += sourceOffset;  // char* increments by sizeof(char)
6589                 fixed (byte* destinationPtr = dest) {
6590                     byte* destPtr = destinationPtr;
6591                     destPtr += destOffset;
6592                     NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength);
6593                 }
6594             }
6595         }
6596
6597         private unsafe static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength) {
6598             // DEVNOTE: BE EXTREMELY CAREFULL in this method.  Since it pins the buffers and copies the memory
6599             //  directly, it bypasses all of the CLR's usual array-bounds checking.  For maintainability, the checks
6600             //  here should NOT be removed just because some other code will check them ahead of time.
6601             if (charLength < 0) {
6602                 throw ADP.InvalidDataLength(charLength);
6603             }
6604
6605             if (checked(sourceOffset + charLength) > source.Length || sourceOffset < 0) {
6606                 throw ADP.IndexOutOfRange(sourceOffset);
6607             }
6608
6609             // charLength >= 0 & checked conversion implies byteLength >= 0
6610             int byteLength = checked(charLength * ADP.CharSize);
6611
6612             if (checked(destOffset + byteLength) > dest.Length || destOffset < 0) { 
6613                 throw ADP.IndexOutOfRange(destOffset);
6614             }
6615
6616             fixed (char* sourcePtr = source) {
6617                 char* srcPtr = sourcePtr;  // Can't increment the target of a Fixed statement
6618                 srcPtr += sourceOffset;  // char* increments by sizeof(char)
6619                 fixed (byte* destinationPtr = dest) {
6620                     byte* destPtr = destinationPtr;
6621                     destPtr += destOffset;
6622                     NativeOledbWrapper.MemoryCopy((IntPtr)destPtr, (IntPtr)srcPtr, byteLength);
6623                 }
6624             }
6625         }
6626
6627         private Task WriteEncodingChar(string s, Encoding encoding, TdsParserStateObject stateObj,  bool canAccumulate = true) {
6628             return WriteEncodingChar(s, s.Length, 0, encoding, stateObj,  canAccumulate);
6629         }
6630
6631         private byte[] SerializeEncodingChar(string s, int numChars, int offset, Encoding encoding) {
6632             char[] charData;
6633             byte[] byteData = null;
6634
6635             // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6636             // 7.0 has no support for multiple code pages in data - single code page support only
6637             if (encoding == null)
6638                 encoding = _defaultEncoding;
6639             
6640             charData = s.ToCharArray(offset, numChars);
6641
6642             byteData = new byte[encoding.GetByteCount(charData, 0, charData.Length)];
6643             encoding.GetBytes(charData, 0, charData.Length, byteData, 0);
6644
6645             return byteData;
6646         }
6647
6648         private Task WriteEncodingChar(string s, int numChars, int offset, Encoding encoding, TdsParserStateObject stateObj,  bool canAccumulate = true) {
6649             char[] charData;
6650             byte[] byteData;
6651
6652             // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6653             // 7.0 has no support for multiple code pages in data - single code page support only
6654             if (encoding == null)
6655                 encoding = _defaultEncoding;
6656
6657             charData = s.ToCharArray(offset, numChars);
6658
6659             // Optimization: if the entire string fits in the current buffer, then copy it directly
6660             int bytesLeft = stateObj._outBuff.Length - stateObj._outBytesUsed;
6661             if ((numChars <= bytesLeft) && (encoding.GetMaxByteCount(charData.Length) <= bytesLeft)) {
6662                 int bytesWritten = encoding.GetBytes(charData, 0, charData.Length, stateObj._outBuff, stateObj._outBytesUsed);
6663                 stateObj._outBytesUsed += bytesWritten;
6664                 return null;
6665             }
6666             else {
6667                 byteData = encoding.GetBytes(charData, 0, numChars);
6668                 Debug.Assert(byteData != null, "no data from encoding");
6669                 return stateObj.WriteByteArray(byteData, byteData.Length, 0,  canAccumulate);
6670             }
6671         }
6672
6673         internal int GetEncodingCharLength(string value, int numChars, int charOffset, Encoding encoding) {
6674             // 
6675
6676             if (value == null || value == ADP.StrEmpty) {
6677                 return 0;
6678             }
6679
6680             // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6681             // 7.0 has no support for multiple code pages in data - single code page support only
6682             if (encoding == null) {
6683                 if (null == _defaultEncoding) {
6684                     ThrowUnsupportedCollationEncountered(null);
6685                 }
6686
6687                 encoding = _defaultEncoding;
6688             }
6689
6690             char[] charData = value.ToCharArray(charOffset, numChars);
6691
6692             return encoding.GetByteCount(charData, 0, numChars);
6693         }
6694
6695         //
6696         // Returns the data stream length of the data identified by tds type or SqlMetaData returns
6697         // Returns either the total size or the size of the first chunk for partially length prefixed types.
6698         //
6699         internal bool TryGetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject stateObj, out ulong length) {
6700             // Handle Yukon specific tokens
6701             if (_isYukon && colmeta.metaType.IsPlp) {
6702                 Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE ||
6703                              colmeta.tdsType == TdsEnums.SQLBIGVARCHAR ||
6704                              colmeta.tdsType == TdsEnums.SQLBIGVARBINARY ||
6705                              colmeta.tdsType == TdsEnums.SQLNVARCHAR ||
6706                              // Large UDTs is WinFS-only
6707                              colmeta.tdsType == TdsEnums.SQLUDT,
6708                              "GetDataLength:Invalid streaming datatype");
6709                 return stateObj.TryReadPlpLength(true, out length);
6710             }
6711             else {
6712                 int intLength;
6713                 if (!TryGetTokenLength(colmeta.tdsType, stateObj, out intLength)) {
6714                     length = 0;
6715                     return false;
6716                 }
6717                 length = (ulong)intLength;
6718                 return true;
6719             }
6720         }
6721
6722         //
6723         // returns the token length of the token or tds type
6724         // Returns -1 for partially length prefixed (plp) types for metadata info.
6725         // DOES NOT handle plp data streams correctly!!!
6726         // Plp data streams length information should be obtained from GetDataLength
6727         //
6728         internal bool TryGetTokenLength(byte token, TdsParserStateObject stateObj, out int tokenLength) {
6729             Debug.Assert(token != 0, "0 length token!");
6730
6731             switch (token) { // rules about SQLLenMask no longer apply to new tokens (as of 7.4)
6732                 case TdsEnums.SQLFEATUREEXTACK: 
6733                     tokenLength = -1;
6734                     return true;
6735                 case TdsEnums.SQLSESSIONSTATE:
6736                 case TdsEnums.SQLFEDAUTHINFO:
6737                     return stateObj.TryReadInt32(out tokenLength);
6738             }
6739
6740             if (_isYukon) {     // Handle Yukon specific exceptions
6741                 if (token == TdsEnums.SQLUDT) { // special case for UDTs
6742                     tokenLength = -1; // Should we return -1 or not call GetTokenLength for UDTs?
6743                     return true;
6744                 }
6745                 else if (token == TdsEnums.SQLRETURNVALUE) {
6746                     tokenLength = -1; // In Yukon, the RETURNVALUE token stream no longer has length
6747                     return true;
6748                 }
6749                 else if (token == TdsEnums.SQLXMLTYPE) {
6750                     ushort value;
6751                     if (!stateObj.TryReadUInt16(out value)) {
6752                         tokenLength = 0;
6753                         return false;
6754                     }
6755                     tokenLength = (int)value;
6756                     Debug.Assert(tokenLength == TdsEnums.SQL_USHORTVARMAXLEN, "Invalid token stream for xml datatype");
6757                     return true;
6758                 }
6759             }
6760
6761             switch (token & TdsEnums.SQLLenMask) {
6762                 case TdsEnums.SQLFixedLen:
6763                     tokenLength = ((0x01 << ((token & 0x0c) >> 2))) & 0xff;
6764                     return true;
6765                 case TdsEnums.SQLZeroLen:
6766                     tokenLength = 0;
6767                     return true;
6768                 case TdsEnums.SQLVarLen:
6769                 case TdsEnums.SQLVarCnt:
6770                     if (0 != (token & 0x80)) {
6771                         ushort value;
6772                         if (!stateObj.TryReadUInt16(out value)) {
6773                             tokenLength = 0;
6774                             return false;
6775                         }
6776                         tokenLength = value;
6777                         return true;
6778                     }
6779                     else if (0 == (token & 0x0c)) {
6780                         // 
6781                         if (!stateObj.TryReadInt32(out tokenLength)) {
6782                             return false;
6783                         }
6784                         return true;
6785                     }
6786                     else {
6787                         byte value;
6788                         if (!stateObj.TryReadByte(out value)) {
6789                             tokenLength = 0;
6790                             return false;
6791                         }
6792                         tokenLength = value;
6793                         return true;
6794                     }
6795                 default:
6796                     Debug.Assert(false, "Unknown token length!");
6797                     tokenLength = 0;
6798                     return true;
6799             }
6800         }
6801
6802         private void ProcessAttention(TdsParserStateObject stateObj) {
6803             if (_state == TdsParserState.Closed || _state == TdsParserState.Broken){
6804                 return;
6805             }
6806             Debug.Assert(stateObj._attentionSent, "invalid attempt to ProcessAttention, attentionSent == false!");
6807
6808             // Attention processing scenarios:
6809             // 1) EOM packet with header ST_AACK bit plus DONE with status DONE_ATTN
6810             // 2) Packet without ST_AACK header bit but has DONE with status DONE_ATTN
6811             // 3) Secondary timeout occurs while reading, break connection
6812
6813             // Since errors can occur and we need to cancel prior to throwing those errors, we
6814             // cache away error state and then process TDS for the attention.  We restore those
6815             // errors after processing.
6816             stateObj.StoreErrorAndWarningForAttention();
6817
6818             try {
6819                 // Call run loop to process looking for attention ack.
6820                 Run(RunBehavior.Attention, null, null, null, stateObj);
6821             }
6822             catch (Exception e) {
6823                 // 
6824                 if (!ADP.IsCatchableExceptionType(e)) {
6825                     throw;
6826                 }
6827
6828                 // If an exception occurs - break the connection.
6829                 // Attention error will not be thrown in this case by Run(), but other failures may.
6830                 ADP.TraceExceptionWithoutRethrow(e);
6831                 _state = TdsParserState.Broken;
6832                 _connHandler.BreakConnection();
6833
6834                 throw;
6835             }
6836
6837             stateObj.RestoreErrorAndWarningAfterAttention();                        
6838                 
6839             Debug.Assert(!stateObj._attentionSent, "Invalid attentionSent state at end of ProcessAttention");
6840         }
6841        
6842         static private int StateValueLength(int dataLen) {
6843             return dataLen < 0xFF ? (dataLen + 1) : (dataLen + 5);
6844         }
6845                 
6846         internal int WriteSessionRecoveryFeatureRequest(SessionData reconnectData, bool write /* if false just calculates the length */) {
6847             int len = 1;
6848             if (write) {
6849                 _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_SRECOVERY);
6850             }
6851             if (reconnectData == null) {
6852                 if (write) {
6853                     WriteInt(0, _physicalStateObj);
6854                 }
6855                 len += 4;
6856             }
6857             else {
6858                 Debug.Assert(reconnectData._unrecoverableStatesCount == 0, "Unrecoverable state count should be 0");
6859                 int initialLength = 0; // sizeof(DWORD) - length itself
6860                 initialLength += 1 + 2 * TdsParserStaticMethods.NullAwareStringLength(reconnectData._initialDatabase);
6861                 initialLength += 1 + 2 * TdsParserStaticMethods.NullAwareStringLength(reconnectData._initialLanguage);
6862                 initialLength += (reconnectData._initialCollation == null) ? 1 : 6;
6863                 for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
6864                     if (reconnectData._initialState[i] != null) {
6865                         initialLength += 1 /* StateId*/ + StateValueLength(reconnectData._initialState[i].Length);
6866                     }
6867                 }
6868                 int currentLength = 0; // sizeof(DWORD) - length itself                
6869                 currentLength += 1 + 2 * (reconnectData._initialDatabase == reconnectData._database ? 0 : TdsParserStaticMethods.NullAwareStringLength(reconnectData._database));
6870                 currentLength += 1 + 2 * (reconnectData._initialLanguage == reconnectData._language ? 0 : TdsParserStaticMethods.NullAwareStringLength(reconnectData._language));
6871                 currentLength += (reconnectData._collation != null && !SqlCollation.AreSame(reconnectData._collation, reconnectData._initialCollation)) ? 6 : 1;
6872                 bool[] writeState = new bool[SessionData._maxNumberOfSessionStates];
6873                 for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
6874                     if (reconnectData._delta[i] != null) {
6875                         Debug.Assert(reconnectData._delta[i]._recoverable, "State should be recoverable");
6876                         writeState[i] = true;
6877                         if (reconnectData._initialState[i] != null && reconnectData._initialState[i].Length == reconnectData._delta[i]._dataLength) {
6878                             writeState[i] = false;
6879                             for (int j = 0; j < reconnectData._delta[i]._dataLength; j++) {
6880                                 if (reconnectData._initialState[i][j] != reconnectData._delta[i]._data[j]) {
6881                                     writeState[i] = true;
6882                                     break;
6883                                 }
6884                             }
6885                         }
6886                         if (writeState[i]) {
6887                             currentLength += 1 /* StateId*/ + StateValueLength(reconnectData._delta[i]._dataLength);
6888                         }
6889                     }
6890                 }                
6891                 if (write) {
6892                     WriteInt(8 + initialLength + currentLength, _physicalStateObj); // length of data w/o total length (initil+current+2*sizeof(DWORD))
6893                     WriteInt(initialLength, _physicalStateObj);
6894                     WriteIdentifier(reconnectData._initialDatabase, _physicalStateObj);
6895                     WriteCollation(reconnectData._initialCollation, _physicalStateObj);
6896                     WriteIdentifier(reconnectData._initialLanguage, _physicalStateObj);
6897                     for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
6898                         if (reconnectData._initialState[i] != null) {
6899                             _physicalStateObj.WriteByte((byte)i);
6900                             if (reconnectData._initialState[i].Length < 0xFF) {
6901                                 _physicalStateObj.WriteByte((byte)reconnectData._initialState[i].Length);
6902                             }
6903                             else {
6904                                 _physicalStateObj.WriteByte(0xFF);
6905                                 WriteInt(reconnectData._initialState[i].Length, _physicalStateObj);
6906                             }
6907                             _physicalStateObj.WriteByteArray(reconnectData._initialState[i], reconnectData._initialState[i].Length, 0);
6908                         }
6909                     }
6910                     WriteInt(currentLength, _physicalStateObj);
6911                     WriteIdentifier(reconnectData._database != reconnectData._initialDatabase ? reconnectData._database : null, _physicalStateObj);
6912                     WriteCollation(SqlCollation.AreSame(reconnectData._initialCollation, reconnectData._collation) ? null : reconnectData._collation, _physicalStateObj);
6913                     WriteIdentifier(reconnectData._language != reconnectData._initialLanguage ? reconnectData._language : null, _physicalStateObj);
6914                     for (int i = 0; i < SessionData._maxNumberOfSessionStates; i++) {
6915                         if (writeState[i]) {
6916                             _physicalStateObj.WriteByte((byte)i);
6917                             if (reconnectData._delta[i]._dataLength < 0xFF) {
6918                                 _physicalStateObj.WriteByte((byte)reconnectData._delta[i]._dataLength);
6919                             }
6920                             else {
6921                                 _physicalStateObj.WriteByte(0xFF);
6922                                 WriteInt(reconnectData._delta[i]._dataLength, _physicalStateObj);
6923                             }
6924                             _physicalStateObj.WriteByteArray(reconnectData._delta[i]._data, reconnectData._delta[i]._dataLength, 0);
6925                         }
6926                     }
6927                 }
6928                 len += initialLength + currentLength + 12 /* length fields (initial, current, total) */;
6929             }
6930             return len;
6931         }
6932
6933         internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionData fedAuthFeatureData,
6934                                                 bool write /* if false just calculates the length */) {
6935             Debug.Assert(fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.ADAL || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityToken,
6936                 "only fed auth library type ADAL and Security Token are supported in writing feature request");
6937
6938             int dataLen = 0;
6939             int totalLen = 0;
6940
6941             // set dataLen and totalLen
6942             switch (fedAuthFeatureData.libraryType) {
6943                 case TdsEnums.FedAuthLibrary.ADAL:
6944                     dataLen = 2;  // length of feature data = 1 byte for library and echo + 1 byte for workflow
6945                     break;
6946                 case TdsEnums.FedAuthLibrary.SecurityToken:
6947                     Debug.Assert(fedAuthFeatureData.accessToken != null, "AccessToken should not be null.");
6948                     dataLen = 1 + sizeof(int) + fedAuthFeatureData.accessToken.Length; // length of feature data = 1 byte for library and echo, security token length and sizeof(int) for token lengh itself
6949                     break;
6950                default:
6951                     Debug.Assert(false, "Unrecognized library type for fedauth feature extension request");
6952                     break;
6953             }
6954
6955             totalLen = dataLen + 5; // length of feature id (1 byte), data length field (4 bytes), and feature data (dataLen)
6956
6957             // write feature id
6958             if (write) {
6959                 _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_FEDAUTH);
6960
6961                 // set options
6962                 byte options = 0x00;
6963
6964                 // set upper 7 bits of options to indicate fed auth library type
6965                 switch (fedAuthFeatureData.libraryType) {
6966                     case TdsEnums.FedAuthLibrary.ADAL:
6967                         Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true");
6968                         options |= TdsEnums.FEDAUTHLIB_ADAL << 1;
6969                         break;
6970                     case TdsEnums.FedAuthLibrary.SecurityToken:
6971                         Debug.Assert(_connHandler._federatedAuthenticationRequested == true, "_federatedAuthenticationRequested field should be true");
6972                         options |= TdsEnums.FEDAUTHLIB_SECURITYTOKEN << 1;
6973                         break;
6974                     default:
6975                         Debug.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request");
6976                         break;
6977                 }
6978
6979                 options |= (byte)(fedAuthFeatureData.fedAuthRequiredPreLoginResponse == true ? 0x01 : 0x00);
6980
6981                 // write dataLen and options
6982                 WriteInt(dataLen, _physicalStateObj);
6983                 _physicalStateObj.WriteByte(options);
6984
6985                 // write workflow for FedAuthLibrary.ADAL
6986                 // write accessToken for FedAuthLibrary.SecurityToken
6987                 switch (fedAuthFeatureData.libraryType) {
6988                     case TdsEnums.FedAuthLibrary.ADAL:
6989                         byte workflow = 0x00;
6990                         switch (fedAuthFeatureData.authentication) {
6991                             case SqlAuthenticationMethod.ActiveDirectoryPassword:
6992                                 workflow = TdsEnums.ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD;
6993                                 break;
6994                             case SqlAuthenticationMethod.ActiveDirectoryIntegrated:
6995                                 workflow = TdsEnums.ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED;
6996                                 break;
6997                             default:
6998                                 Debug.Assert(false, "Unrecognized Authentication type for fedauth ADAL request");
6999                                 break;
7000                         }
7001
7002                         _physicalStateObj.WriteByte(workflow);
7003                         break;
7004                     case TdsEnums.FedAuthLibrary.SecurityToken:
7005                         WriteInt(fedAuthFeatureData.accessToken.Length, _physicalStateObj);
7006                         _physicalStateObj.WriteByteArray(fedAuthFeatureData.accessToken, fedAuthFeatureData.accessToken.Length, 0);
7007                         break;
7008                     default:
7009                         Debug.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request");
7010                         break;
7011                  }
7012             }
7013             return totalLen;
7014         }
7015
7016         internal int WriteTceFeatureRequest (bool write /* if false just calculates the length */) {
7017             int len = 6; // (1byte = featureID, 4bytes = featureData length, 1 bytes = Version
7018
7019             if (write) {
7020                 // Write Feature ID, legth of the version# field and TCE Version#
7021                 _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_TCE);
7022                 WriteInt (1, _physicalStateObj);
7023                 _physicalStateObj.WriteByte(TdsEnums.MAX_SUPPORTED_TCE_VERSION);
7024             }
7025
7026             return len; // size of data written
7027         }
7028
7029         internal int WriteGlobalTransactionsFeatureRequest(bool write /* if false just calculates the length */) {
7030             int len = 5; // 1byte = featureID, 4bytes = featureData length
7031
7032             if (write) {
7033                 // Write Feature ID
7034                 _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS);
7035                 WriteInt(0, _physicalStateObj); // we don't send any data
7036             }
7037
7038             return len;
7039         }
7040
7041         internal void TdsLogin(SqlLogin rec,
7042                                TdsEnums.FeatureExtension requestedFeatures,
7043                                SessionData recoverySessionData,
7044                                FederatedAuthenticationFeatureExtensionData? fedAuthFeatureExtensionData) {
7045             _physicalStateObj.SetTimeoutSeconds(rec.timeout);
7046
7047             Debug.Assert(recoverySessionData == null || (requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0, "Recovery session data without session recovery feature request");
7048             Debug.Assert(TdsEnums.MAXLEN_HOSTNAME>=rec.hostName.Length, "_workstationId.Length exceeds the max length for this value");
7049
7050             Debug.Assert(!(rec.useSSPI && _connHandler._fedAuthRequired), "Cannot use SSPI when server has responded 0x01 for FedAuthRequired PreLogin Option.");
7051             Debug.Assert(!rec.useSSPI || (requestedFeatures & TdsEnums.FeatureExtension.FedAuth) == 0, "Cannot use both SSPI and FedAuth");
7052             Debug.Assert(fedAuthFeatureExtensionData == null || (requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0, "fedAuthFeatureExtensionData provided without fed auth feature request");
7053             Debug.Assert(fedAuthFeatureExtensionData != null || (requestedFeatures & TdsEnums.FeatureExtension.FedAuth) == 0, "Fed Auth feature requested without specifying fedAuthFeatureExtensionData.");
7054
7055             Debug.Assert(rec.userName == null || (rec.userName != null && TdsEnums.MAXLEN_USERNAME >= rec.userName.Length), "_userID.Length exceeds the max length for this value");
7056             Debug.Assert(rec.credential == null || (rec.credential != null && TdsEnums.MAXLEN_USERNAME >= rec.credential.UserId.Length), "_credential.UserId.Length exceeds the max length for this value");
7057
7058             Debug.Assert(rec.password == null || (rec.password != null && TdsEnums.MAXLEN_PASSWORD>=rec.password.Length), "_password.Length exceeds the max length for this value");
7059             Debug.Assert(rec.credential == null || (rec.credential != null && TdsEnums.MAXLEN_PASSWORD >= rec.credential.Password.Length), "_credential.Password.Length exceeds the max length for this value");
7060
7061             Debug.Assert(rec.credential != null || rec.userName != null || rec.password != null, "cannot mix the new secure password system and the connection string based password");
7062             Debug.Assert(rec.newSecurePassword != null || rec.newPassword != null, "cannot have both new secure change password and string based change password");
7063             Debug.Assert(TdsEnums.MAXLEN_APPNAME>=rec.applicationName.Length, "_applicationName.Length exceeds the max length for this value");
7064             Debug.Assert(TdsEnums.MAXLEN_SERVERNAME>=rec.serverName.Length, "_dataSource.Length exceeds the max length for this value");
7065             Debug.Assert(TdsEnums.MAXLEN_LANGUAGE>=rec.language.Length, "_currentLanguage .Length exceeds the max length for this value");
7066             Debug.Assert(TdsEnums.MAXLEN_DATABASE>=rec.database.Length, "_initialCatalog.Length exceeds the max length for this value");
7067             Debug.Assert(TdsEnums.MAXLEN_ATTACHDBFILE>=rec.attachDBFilename.Length, "_attachDBFileName.Length exceeds the max length for this value");
7068
7069             Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
7070             _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.LoginBegin);
7071             _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth);
7072
7073             // get the password up front to use in sspi logic below
7074             byte[] encryptedPassword = null;
7075             byte[] encryptedChangePassword = null;
7076             int encryptedPasswordLengthInBytes;
7077             int encryptedChangePasswordLengthInBytes;
7078             bool useFeatureExt = (requestedFeatures != TdsEnums.FeatureExtension.None);
7079
7080             string userName;
7081
7082             if (rec.credential != null) {
7083                 userName = rec.credential.UserId;
7084                 encryptedPasswordLengthInBytes = rec.credential.Password.Length * 2;
7085             }
7086             else {
7087                 userName = rec.userName;
7088                 encryptedPassword = TdsParserStaticMethods.EncryptPassword(rec.password);
7089                 encryptedPasswordLengthInBytes = encryptedPassword.Length;  // password in clear text is already encrypted and its length is in byte
7090             }
7091
7092             if (rec.newSecurePassword != null) {
7093                 encryptedChangePasswordLengthInBytes = rec.newSecurePassword.Length * 2;
7094             }
7095             else {
7096                 encryptedChangePassword = TdsParserStaticMethods.EncryptPassword(rec.newPassword);
7097                 encryptedChangePasswordLengthInBytes = encryptedChangePassword.Length;
7098             }
7099
7100
7101             // set the message type
7102             _physicalStateObj._outputMessageType = TdsEnums.MT_LOGIN7;
7103
7104             // length in bytes
7105             int length = TdsEnums.YUKON_LOG_REC_FIXED_LEN;
7106
7107             string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME;
7108             Debug.Assert(TdsEnums.MAXLEN_CLIENTINTERFACE >= clientInterfaceName.Length, "cchCltIntName can specify at most 128 unicode characters. See Tds spec");
7109
7110             // add up variable-len portions (multiply by 2 for byte len of char strings)
7111             //
7112             checked {
7113                 length += (rec.hostName.Length + rec.applicationName.Length +
7114                             rec.serverName.Length + clientInterfaceName.Length +
7115                             rec.language.Length + rec.database.Length +
7116                             rec.attachDBFilename.Length) * 2;
7117                 if (useFeatureExt) {
7118                     length += 4;
7119                 }
7120             }
7121
7122             // allocate memory for SSPI variables
7123             byte[] outSSPIBuff = null;
7124             UInt32 outSSPILength = 0;
7125
7126             // only add lengths of password and username if not using SSPI or requesting federated authentication info
7127             if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) {
7128                 checked {
7129                     length += (userName.Length * 2) + encryptedPasswordLengthInBytes
7130                     + encryptedChangePasswordLengthInBytes;
7131                 }
7132             }
7133             else {
7134                 if (rec.useSSPI) {
7135                     // now allocate proper length of buffer, and set length
7136                     outSSPIBuff = new byte[s_maxSSPILength];
7137                     outSSPILength = s_maxSSPILength;
7138
7139                     // Call helper function for SSPI data and actual length.
7140                     // Since we don't have SSPI data from the server, send null for the
7141                     // byte[] buffer and 0 for the int length.
7142                     Debug.Assert(SniContext.Snix_Login==_physicalStateObj.SniContext, String.Format((IFormatProvider)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj.SniContext));
7143                     _physicalStateObj.SniContext = SniContext.Snix_LoginSspi;
7144                     SSPIData(null, 0, outSSPIBuff, ref outSSPILength);
7145                     if (outSSPILength > Int32.MaxValue) {
7146                         throw SQL.InvalidSSPIPacketSize();  // SqlBu 332503
7147                     }
7148                     _physicalStateObj.SniContext=SniContext.Snix_Login;
7149
7150                     checked {
7151                         length += (Int32)outSSPILength;
7152                     }
7153                 }
7154             }
7155
7156             int feOffset = length;
7157
7158             if (useFeatureExt) {
7159                 checked {
7160                     if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) {
7161                         length += WriteSessionRecoveryFeatureRequest(recoverySessionData, false);
7162                     };
7163                     if ((requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0) {
7164                         Debug.Assert(fedAuthFeatureExtensionData != null, "fedAuthFeatureExtensionData should not null.");
7165                         length += WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData.Value, write:false);
7166                     }
7167                     if ((requestedFeatures & TdsEnums.FeatureExtension.Tce) != 0) {
7168                         length += WriteTceFeatureRequest (false);
7169                     }
7170                     if ((requestedFeatures & TdsEnums.FeatureExtension.GlobalTransactions) != 0) {
7171                         length += WriteGlobalTransactionsFeatureRequest(false);
7172                     }
7173                     length++; // for terminator
7174                 }
7175             }
7176
7177             try {
7178                 WriteInt(length, _physicalStateObj);
7179                 if (recoverySessionData == null) {
7180                     WriteInt((TdsEnums.DENALI_MAJOR << 24) | (TdsEnums.DENALI_INCREMENT << 16) | TdsEnums.DENALI_MINOR, _physicalStateObj);
7181                 }
7182                 else {
7183                     WriteUnsignedInt(recoverySessionData._tdsVersion, _physicalStateObj);
7184                 }
7185                 WriteInt(rec.packetSize, _physicalStateObj);
7186                 WriteInt(TdsEnums.CLIENT_PROG_VER, _physicalStateObj);
7187                 WriteInt(TdsParserStaticMethods.GetCurrentProcessIdForTdsLoginOnly(), _physicalStateObj); //MDAC 84718
7188                 WriteInt(0, _physicalStateObj); // connectionID is unused
7189
7190                 // Log7Flags (DWORD)
7191                 int log7Flags = 0;
7192
7193                 /*
7194                  Current snapshot from TDS spec with the offsets added:
7195                     0) fByteOrder:1,                // byte order of numeric data types on client
7196                     1) fCharSet:1,                  // character set on client
7197                     2) fFloat:2,                    // Type of floating point on client
7198                     4) fDumpLoad:1,                 // Dump/Load and BCP enable
7199                     5) fUseDb:1,                    // USE notification
7200                     6) fDatabase:1,                 // Initial database fatal flag
7201                     7) fSetLang:1,                  // SET LANGUAGE notification
7202                     8) fLanguage:1,                 // Initial language fatal flag
7203                     9) fODBC:1,                     // Set if client is ODBC driver
7204                    10) fTranBoundary:1,             // Transaction boundary notification
7205                    11) fDelegatedSec:1,             // Security with delegation is available
7206                    12) fUserType:3,                 // Type of user
7207                    15) fIntegratedSecurity:1,       // Set if client is using integrated security
7208                    16) fSQLType:4,                  // Type of SQL sent from client
7209                    20) fOLEDB:1,                    // Set if client is OLEDB driver
7210                    21) fSpare1:3,                   // first bit used for read-only intent, rest unused
7211                    24) fResetPassword:1,            // set if client wants to reset password
7212                    25) fNoNBCAndSparse:1,           // set if client does not support NBC and Sparse column
7213                    26) fUserInstance:1,             // This connection wants to connect to a SQL "user instance"
7214                    27) fUnknownCollationHandling:1, // This connection can handle unknown collation correctly.
7215                    28) fExtension:1                 // Extensions are used                 
7216                    32 - total
7217                 */
7218
7219                 // first byte
7220                 log7Flags |= TdsEnums.USE_DB_ON << 5;
7221                 log7Flags |= TdsEnums.INIT_DB_FATAL << 6;
7222                 log7Flags |= TdsEnums.SET_LANG_ON << 7;
7223
7224                 // second byte
7225                 log7Flags |= TdsEnums.INIT_LANG_FATAL << 8;
7226                 log7Flags |= TdsEnums.ODBC_ON << 9;
7227                 if (rec.useReplication) {
7228                     log7Flags |=  TdsEnums.REPL_ON << 12;
7229                 }
7230                 if (rec.useSSPI) {
7231                     log7Flags |=  TdsEnums.SSPI_ON << 15;
7232                 }
7233
7234                 // third byte
7235                 if (rec.readOnlyIntent) {
7236                     log7Flags |=  TdsEnums.READONLY_INTENT_ON << 21; // read-only intent flag is a first bit of fSpare1
7237                 }
7238
7239                 // 4th one
7240                 if (!ADP.IsEmpty(rec.newPassword) || (rec.newSecurePassword != null && rec.newSecurePassword.Length != 0)) {
7241                     log7Flags |=  1 << 24;
7242                 }
7243                 if (rec.userInstance) {
7244                     log7Flags |=  1 << 26;
7245                 }
7246
7247                 if (useFeatureExt) {
7248                     log7Flags |=  1 << 28;
7249                 }
7250
7251                 WriteInt(log7Flags, _physicalStateObj);
7252                 if (Bid.AdvancedOn) {
7253                     Bid.Trace("<sc.TdsParser.TdsLogin|ADV> %d#, TDS Login7 flags = %d:\n", ObjectID, log7Flags);
7254                 }
7255
7256                 WriteInt(0, _physicalStateObj);  // ClientTimeZone is not used
7257                 WriteInt(0, _physicalStateObj);  // LCID is unused by server
7258
7259                 // Start writing offset and length of variable length portions
7260                 int offset = TdsEnums.YUKON_LOG_REC_FIXED_LEN;
7261
7262                 // write offset/length pairs
7263
7264                 // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record
7265                 WriteShort(offset, _physicalStateObj); // host name offset
7266                 WriteShort(rec.hostName.Length, _physicalStateObj);
7267                 offset += rec.hostName.Length * 2;
7268
7269                 // Only send user/password over if not fSSPI or fed auth ADAL...  If both user/password and SSPI are in login
7270                 // rec, only SSPI is used.  Confirmed same bahavior as in luxor.
7271                 if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) {
7272                     WriteShort(offset, _physicalStateObj);  // userName offset
7273                     WriteShort(userName.Length, _physicalStateObj);
7274                     offset += userName.Length * 2;
7275
7276                     // the encrypted password is a byte array - so length computations different than strings
7277                     WriteShort(offset, _physicalStateObj); // password offset
7278                     WriteShort(encryptedPasswordLengthInBytes / 2, _physicalStateObj);
7279                     offset += encryptedPasswordLengthInBytes;
7280                 }
7281                 else {
7282                     // case where user/password data is not used, send over zeros
7283                     WriteShort(0, _physicalStateObj);  // userName offset
7284                     WriteShort(0, _physicalStateObj);
7285                     WriteShort(0, _physicalStateObj);  // password offset
7286                     WriteShort(0, _physicalStateObj);
7287                 }
7288
7289                 WriteShort(offset, _physicalStateObj); // app name offset
7290                 WriteShort(rec.applicationName.Length, _physicalStateObj);
7291                 offset += rec.applicationName.Length * 2;
7292
7293                 WriteShort(offset, _physicalStateObj); // server name offset
7294                 WriteShort(rec.serverName.Length, _physicalStateObj);
7295                 offset += rec.serverName.Length * 2;
7296
7297                 WriteShort(offset, _physicalStateObj);
7298                 if (useFeatureExt) {
7299                     WriteShort(4, _physicalStateObj); // length of ibFeatgureExtLong (which is a DWORD)
7300                     offset += 4;
7301                 }
7302                 else {
7303                     WriteShort(0, _physicalStateObj); // unused (was remote password ?)
7304                 }
7305
7306                 WriteShort(offset, _physicalStateObj); // client interface name offset
7307                 WriteShort(clientInterfaceName.Length, _physicalStateObj);
7308                 offset += clientInterfaceName.Length * 2;
7309
7310                 WriteShort(offset, _physicalStateObj); // language name offset
7311                 WriteShort(rec.language.Length, _physicalStateObj);
7312                 offset += rec.language.Length * 2;
7313
7314                 WriteShort(offset, _physicalStateObj); // database name offset
7315                 WriteShort(rec.database.Length, _physicalStateObj);
7316                 offset += rec.database.Length * 2;
7317
7318                 // 
7319
7320                 if (null == s_nicAddress)
7321                     s_nicAddress = TdsParserStaticMethods.GetNetworkPhysicalAddressForTdsLoginOnly();
7322
7323                 _physicalStateObj.WriteByteArray(s_nicAddress, s_nicAddress.Length, 0);
7324
7325                 WriteShort(offset, _physicalStateObj); // ibSSPI offset
7326                 if (rec.useSSPI) {
7327                     WriteShort((int)outSSPILength, _physicalStateObj);
7328                     offset += (int)outSSPILength;
7329                 }
7330                 else {
7331                     WriteShort(0, _physicalStateObj);
7332                 }
7333
7334                 WriteShort(offset, _physicalStateObj); // DB filename offset
7335                 WriteShort(rec.attachDBFilename.Length, _physicalStateObj);
7336                 offset += rec.attachDBFilename.Length * 2;
7337
7338                 WriteShort(offset, _physicalStateObj); // reset password offset
7339                 WriteShort(encryptedChangePasswordLengthInBytes / 2, _physicalStateObj);
7340
7341                 WriteInt(0, _physicalStateObj);        // reserved for chSSPI
7342
7343                 // write variable length portion
7344                 WriteString(rec.hostName, _physicalStateObj);
7345
7346                 // if we are using SSPI or fed auth ADAL, do not send over username/password, since we will use SSPI instead
7347                 // same behavior as Luxor
7348                 if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) {
7349                     WriteString(userName, _physicalStateObj);
7350
7351                     // Cache offset in packet for tracing.
7352                     _physicalStateObj._tracePasswordOffset = _physicalStateObj._outBytesUsed;
7353                     _physicalStateObj._tracePasswordLength = encryptedPasswordLengthInBytes;
7354
7355                     if (rec.credential != null) {
7356                         _physicalStateObj.WriteSecureString(rec.credential.Password);
7357                     }
7358                     else {
7359                         _physicalStateObj.WriteByteArray(encryptedPassword, encryptedPasswordLengthInBytes, 0);
7360                     }
7361                 }
7362
7363                 WriteString(rec.applicationName, _physicalStateObj);
7364                 WriteString(rec.serverName, _physicalStateObj);
7365
7366                 // write ibFeatureExtLong
7367                 if (useFeatureExt) {
7368                     WriteInt(feOffset, _physicalStateObj);
7369                 }
7370
7371                 WriteString(clientInterfaceName, _physicalStateObj);
7372                 WriteString(rec.language, _physicalStateObj);
7373                 WriteString(rec.database, _physicalStateObj);
7374
7375                 // send over SSPI data if we are using SSPI
7376                 if (rec.useSSPI)
7377                     _physicalStateObj.WriteByteArray(outSSPIBuff, (int)outSSPILength, 0);
7378
7379                 WriteString(rec.attachDBFilename, _physicalStateObj);
7380                 if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) {
7381                     // Cache offset in packet for tracing.
7382                     _physicalStateObj._traceChangePasswordOffset = _physicalStateObj._outBytesUsed;
7383                     _physicalStateObj._traceChangePasswordLength = encryptedChangePasswordLengthInBytes;
7384                     if (rec.newSecurePassword != null) {
7385                         _physicalStateObj.WriteSecureString(rec.newSecurePassword);
7386                     }
7387                     else {
7388                         _physicalStateObj.WriteByteArray(encryptedChangePassword, encryptedChangePasswordLengthInBytes, 0);
7389                     }
7390                 }
7391
7392                 if (useFeatureExt) {
7393                     if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) {
7394                         WriteSessionRecoveryFeatureRequest(recoverySessionData, true);
7395                     };
7396                     if ((requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0) {
7397                         Bid.Trace("<sc.TdsParser.TdsLogin|SEC> Sending federated authentication feature request\n");
7398                         Debug.Assert(fedAuthFeatureExtensionData != null, "fedAuthFeatureExtensionData should not null.");
7399                         WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData.Value, write: true);
7400                     };
7401                     if ((requestedFeatures & TdsEnums.FeatureExtension.Tce) != 0) {
7402                         WriteTceFeatureRequest (true);
7403                     };
7404                     if ((requestedFeatures & TdsEnums.FeatureExtension.GlobalTransactions) != 0) {
7405                         WriteGlobalTransactionsFeatureRequest(true);
7406                     };
7407                     _physicalStateObj.WriteByte(0xFF); // terminator
7408                 }
7409             } // try
7410             catch (Exception e) {
7411                 // 
7412                 if (ADP.IsCatchableExceptionType(e)) {
7413                     // be sure to wipe out our buffer if we started sending stuff
7414                     _physicalStateObj._outputPacketNumber = 1;  // end of message - reset to 1 - per ramas
7415                     _physicalStateObj.ResetBuffer();
7416                 }
7417
7418                 throw;
7419             }
7420
7421             _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
7422             _physicalStateObj.ResetSecurePasswordsInfomation();     // Password information is needed only from Login process; done with writing login packet and should clear information
7423             _physicalStateObj._pendingData = true;
7424             _physicalStateObj._messageStatus = 0;
7425         }// tdsLogin
7426
7427         /// <summary>
7428         /// Send the access token to the server.
7429         /// </summary>
7430         /// <param name="fedAuthToken">Type encapuslating a Federated Authentication access token.</param>
7431         internal void SendFedAuthToken(SqlFedAuthToken fedAuthToken) {
7432             Debug.Assert(fedAuthToken != null, "fedAuthToken cannot be null");
7433             Debug.Assert(fedAuthToken.accessToken != null, "fedAuthToken.accessToken cannot be null");
7434
7435
7436             Bid.Trace("<sc.TdsParser.SendFedAuthToken|SEC> Sending federated authentication token\n");
7437
7438             _physicalStateObj._outputMessageType = TdsEnums.MT_FEDAUTH;
7439
7440             byte[] accessToken = fedAuthToken.accessToken;
7441
7442             // Send total length (length of token plus 4 bytes for the token length field)
7443             // If we were sending a nonce, this would include that length as well
7444             WriteUnsignedInt((uint)accessToken.Length + sizeof(uint), _physicalStateObj);
7445
7446             // Send length of token
7447             WriteUnsignedInt((uint)accessToken.Length, _physicalStateObj);
7448
7449             // Send federated authentication access token
7450             _physicalStateObj.WriteByteArray(accessToken, accessToken.Length, 0);
7451
7452             _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
7453             _physicalStateObj._pendingData = true;
7454             _physicalStateObj._messageStatus = 0;
7455
7456             _connHandler._federatedAuthenticationRequested = true;
7457         }
7458
7459         private void SSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength) {
7460             SNISSPIData(receivedBuff, receivedLength, sendBuff, ref sendLength);
7461         }
7462
7463         private void SNISSPIData(byte[] receivedBuff, UInt32 receivedLength, byte[] sendBuff, ref UInt32 sendLength)
7464         {
7465             if (receivedBuff == null)
7466             {
7467                 // we do not have SSPI data coming from server, so send over 0's for pointer and length
7468                 receivedLength = 0;
7469             }
7470             // we need to respond to the server's message with SSPI data
7471             if(0 != SNINativeMethodWrapper.SNISecGenClientContext(_physicalStateObj.Handle, receivedBuff, receivedLength, sendBuff, ref sendLength, _sniSpnBuffer))
7472             {
7473                 SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT);
7474             }
7475         }
7476
7477
7478         private void ProcessSSPI(int receivedLength) {
7479             SniContext outerContext=_physicalStateObj.SniContext;
7480             _physicalStateObj.SniContext = SniContext.Snix_ProcessSspi;
7481             // allocate received buffer based on length from SSPI message
7482             byte[] receivedBuff = new byte[receivedLength];
7483
7484             // read SSPI data received from server
7485             Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
7486             bool result = _physicalStateObj.TryReadByteArray(receivedBuff, 0, receivedLength);
7487             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
7488
7489             // allocate send buffer and initialize length
7490             byte[] sendBuff = new byte[s_maxSSPILength];
7491             UInt32 sendLength = s_maxSSPILength;
7492
7493             // make call for SSPI data
7494             SSPIData(receivedBuff, (UInt32)receivedLength, sendBuff, ref sendLength);
7495
7496             // DO NOT SEND LENGTH - TDS DOC INCORRECT!  JUST SEND SSPI DATA!
7497             _physicalStateObj.WriteByteArray(sendBuff, (int)sendLength, 0);
7498
7499             // set message type so server knows its a SSPI response
7500             _physicalStateObj._outputMessageType = TdsEnums.MT_SSPI;
7501
7502             // send to server
7503             _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
7504             _physicalStateObj.SniContext=outerContext;
7505         }
7506
7507         private void SSPIError(string error, string procedure) {
7508             Debug.Assert(!ADP.IsEmpty(procedure), "TdsParser.SSPIError called with an empty or null procedure string");
7509             Debug.Assert(!ADP.IsEmpty(error), "TdsParser.SSPIError called with an empty or null error string");
7510
7511             _physicalStateObj.AddError(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, _server, error, procedure, 0));
7512             ThrowExceptionAndWarning(_physicalStateObj);
7513         }
7514
7515         private void LoadSSPILibrary() {
7516             // Outer check so we don't acquire lock once once it's loaded.
7517             if (!s_fSSPILoaded) {
7518                 lock (s_tdsParserLock) {
7519                     // re-check inside lock
7520                     if (!s_fSSPILoaded) {
7521                         // use local for ref param to defer setting s_maxSSPILength until we know the call succeeded.
7522                         UInt32 maxLength = 0;
7523                         if (0 != SNINativeMethodWrapper.SNISecInitPackage(ref maxLength))
7524                             SSPIError(SQLMessage.SSPIInitializeError(), TdsEnums.INIT_SSPI_PACKAGE);
7525
7526                         s_maxSSPILength = maxLength;
7527                         s_fSSPILoaded = true;
7528
7529                     }
7530                 }
7531             }
7532
7533             if (s_maxSSPILength > Int32.MaxValue) {
7534                 throw SQL.InvalidSSPIPacketSize();   // SqlBu 332503
7535             }
7536         }
7537
7538         private void LoadADALLibrary() {
7539             // Outer check so we don't acquire lock once once it's loaded.
7540             if (!s_fADALLoaded) {
7541                 lock (s_tdsParserLock) {
7542                     // re-check inside lock
7543                     if (!s_fADALLoaded) {
7544                         int result = ADALNativeWrapper.ADALInitialize();
7545
7546                         if (0 == result) {
7547                             s_fADALLoaded = true;
7548                         }
7549                         else {
7550                             s_fADALLoaded = false;
7551
7552                             SqlAuthenticationMethod authentication = SqlAuthenticationMethod.NotSpecified;
7553
7554                             if (_connHandler.ConnectionOptions != null)
7555                             {
7556                                 authentication = _connHandler.ConnectionOptions.Authentication;
7557
7558                                 // Only the below connection string options should have ended up calling this function.
7559                                 Debug.Assert(authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated || authentication == SqlAuthenticationMethod.ActiveDirectoryPassword);
7560                             }
7561
7562                             _physicalStateObj.AddError(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, _server, Res.GetString(Res.SQL_ADALInitializeError, authentication.ToString("G"), result.ToString("X")), TdsEnums.INIT_ADAL_PACKAGE, 0));
7563
7564                             ThrowExceptionAndWarning(_physicalStateObj);
7565                         }
7566                     }
7567                 }
7568             }
7569         }
7570
7571         internal byte[] GetDTCAddress(int timeout, TdsParserStateObject stateObj) {
7572             // If this fails, the server will return a server error - Sameet Agarwal confirmed.
7573             // Success: DTCAddress returned.  Failure: SqlError returned.
7574
7575             byte[] dtcAddr = null;
7576
7577             using (SqlDataReader dtcReader = TdsExecuteTransactionManagerRequest(
7578                                                         null,
7579                                                         TdsEnums.TransactionManagerRequestType.GetDTCAddress,
7580                                                         null,
7581                                                         TdsEnums.TransactionManagerIsolationLevel.Unspecified,
7582                                                         timeout, null, stateObj, true)) {
7583
7584                 Debug.Assert(SniContext.Snix_Read==stateObj.SniContext, String.Format((IFormatProvider)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj.SniContext));
7585                 if (null != dtcReader && dtcReader.Read()) {
7586                     Debug.Assert(dtcReader.GetName(0) == "TM Address", "TdsParser: GetDTCAddress did not return 'TM Address'");
7587
7588                     // DTCAddress is of variable size, and does not have a maximum.  So we call GetBytes
7589                     // to get the length of the dtcAddress, then allocate a byte array of that length,
7590                     // then call GetBytes again on that byte[] with the length
7591                     long dtcLength = dtcReader.GetBytes(0, 0, null, 0, 0);
7592
7593                     //
7594                     if (dtcLength <= Int32.MaxValue) {
7595                         int cb = (int)dtcLength;
7596
7597                         dtcAddr = new byte[cb];
7598                         dtcReader.GetBytes(0, 0, dtcAddr, 0, cb);
7599                     }
7600 #if DEBUG
7601                     else {
7602                         Debug.Assert(false, "unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes");
7603                         // if we hit this case we'll just return a null address so that the user
7604                         // will get a transcaction enlistment error in the upper layers
7605                     }
7606 #endif
7607                 }
7608             }
7609             return dtcAddr;
7610         }
7611
7612         // Propagate the dtc cookie to the server, enlisting the connection.
7613         internal void PropagateDistributedTransaction(byte[] buffer, int timeout, TdsParserStateObject stateObj) {
7614             // if this fails, the server will return a server error - Sameet Agarwal confirmed
7615             // Success: server will return done token.  Failure: SqlError returned.
7616             
7617             TdsExecuteTransactionManagerRequest(buffer,
7618                 TdsEnums.TransactionManagerRequestType.Propagate, null,
7619                 TdsEnums.TransactionManagerIsolationLevel.Unspecified, timeout, null, stateObj, true);
7620         }
7621
7622         internal SqlDataReader TdsExecuteTransactionManagerRequest(
7623                     byte[] buffer,
7624                     TdsEnums.TransactionManagerRequestType request,
7625                     string transactionName,
7626                     TdsEnums.TransactionManagerIsolationLevel isoLevel,
7627                     int timeout,
7628                     SqlInternalTransaction transaction,
7629                     TdsParserStateObject stateObj,
7630                     bool isDelegateControlRequest) {
7631
7632             Debug.Assert(this == stateObj.Parser, "different parsers");
7633
7634             if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
7635                 return null;
7636             }
7637
7638             // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7639             // delegated transactions often happen while there is an open result
7640             // set, so we need to handle them by using a different MARS session, 
7641             // otherwise we'll write on the physical state objects while someone
7642             // else is using it.  When we don't have MARS enabled, we need to 
7643             // lock the physical state object to syncronize it's use at least 
7644             // until we increment the open results count.  Once it's been 
7645             // incremented the delegated transaction requests will fail, so they
7646             // won't stomp on anything.
7647
7648
7649             Debug.Assert(!_connHandler.ThreadHasParserLockForClose || _connHandler._parserLock.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken");
7650             bool callerHasConnectionLock = _connHandler.ThreadHasParserLockForClose;   // If the thread already claims to have the parser lock, then we will let the caller handle releasing it
7651             if (!callerHasConnectionLock) {
7652                 _connHandler._parserLock.Wait(canReleaseFromAnyThread:false);
7653                 _connHandler.ThreadHasParserLockForClose = true;
7654             }
7655             // Capture _asyncWrite (after taking lock) to restore it afterwards
7656             bool hadAsyncWrites = _asyncWrite;
7657             try {
7658                 // Temprarily disable async writes
7659                 _asyncWrite = false;
7660
7661                 // This validation step MUST be done after locking the connection to guarantee we don't 
7662                 //  accidentally execute after the transaction has completed on a different thread.
7663                 if (!isDelegateControlRequest) {
7664                     _connHandler.CheckEnlistedTransactionBinding();
7665                 }
7666
7667                 stateObj._outputMessageType = TdsEnums.MT_TRANS;       // set message type
7668                 stateObj.SetTimeoutSeconds(timeout);
7669
7670                 stateObj.SniContext = SniContext.Snix_Execute;
7671
7672                 if (_isYukon) {
7673                     const int marsHeaderSize = 18; // 4 + 2 + 8 + 4
7674                     const int totalHeaderLength = 22; // 4 + 4 + 2 + 8 + 4
7675                     Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length");
7676                     // Write total header length
7677                     WriteInt(totalHeaderLength, stateObj);
7678                     // Write mars header length
7679                     WriteInt(marsHeaderSize, stateObj);
7680                     WriteMarsHeaderData(stateObj, _currentTransaction);
7681                 }
7682
7683                 WriteShort((short)request, stateObj); // write TransactionManager Request type
7684
7685                 bool returnReader = false;
7686
7687                 switch (request) {
7688                     case TdsEnums.TransactionManagerRequestType.GetDTCAddress:
7689                         WriteShort(0, stateObj);
7690
7691                         returnReader = true;
7692                         break;
7693                     case TdsEnums.TransactionManagerRequestType.Propagate:
7694                         if (null != buffer) {
7695                             WriteShort(buffer.Length, stateObj);
7696                             stateObj.WriteByteArray(buffer, buffer.Length, 0);
7697                         }
7698                         else {
7699                             WriteShort(0, stateObj);
7700                         }
7701                         break;
7702                     case TdsEnums.TransactionManagerRequestType.Begin:
7703                         Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
7704                         Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!");
7705
7706                         // Only assign the passed in transaction if it is not equal to the current transaction.
7707                         // And, if it is not equal, the current actually should be null.  Anything else
7708                         // is a unexpected state.  The concern here is mainly for the mixed use of 
7709                         // T-SQL and API transactions.  See SQL BU DT 345300 for full details and repro.
7710
7711                         // Expected states:
7712                         // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
7713                         // passed in on BeginTransaction API call.
7714                         // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
7715                         // passed in but equivalent to _currentTransaction.
7716
7717                         // #1 will occur on standard BeginTransactionAPI call.  #2 should only occur if
7718                         // t-sql transaction started followed by a call to SqlConnection.BeginTransaction.
7719                         // Any other state is unknown.
7720                         if (_currentTransaction != transaction) {
7721                             Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point");
7722                             PendingTransaction = transaction;
7723                         }
7724
7725                         stateObj.WriteByte((byte)isoLevel);
7726
7727                         stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
7728                         WriteString(transactionName, stateObj);
7729                         break;
7730                     case TdsEnums.TransactionManagerRequestType.Promote:
7731                         Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
7732                         // No payload - except current transaction in header
7733                         // Promote returns a DTC cookie.  However, the transaction cookie we use for the
7734                         // connection does not change after a promote.
7735                         break;
7736                     case TdsEnums.TransactionManagerRequestType.Commit:
7737                         Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");
7738
7739                         Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit");
7740                         stateObj.WriteByte((byte)0); // No xact name
7741
7742                         stateObj.WriteByte(0);  // No flags
7743
7744                         Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!");
7745                         // WriteByte((byte) 0, stateObj); // IsolationLevel
7746                         // WriteByte((byte) 0, stateObj); // No begin xact name
7747                         break;
7748                     case TdsEnums.TransactionManagerRequestType.Rollback:
7749                         Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");
7750
7751                         stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
7752                         WriteString(transactionName, stateObj);
7753
7754                         stateObj.WriteByte(0);  // No flags
7755
7756                         Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!");
7757                         // WriteByte((byte) 0, stateObj); // IsolationLevel
7758                         // WriteByte((byte) 0, stateObj); // No begin xact name
7759                         break;
7760                     case TdsEnums.TransactionManagerRequestType.Save:
7761                         Debug.Assert(IsYukonOrNewer, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");
7762
7763                         stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
7764                         WriteString(transactionName, stateObj);
7765                         break;
7766                     default:
7767                         Debug.Assert(false, "Unexpected TransactionManagerRequest");
7768                         break;
7769                 }
7770
7771                 Task writeTask = stateObj.WritePacket(TdsEnums.HARDFLUSH);
7772                 Debug.Assert(writeTask == null, "Writes should not pend when writing sync");
7773                 stateObj._pendingData = true;
7774                 stateObj._messageStatus = 0;
7775
7776                 SqlDataReader dtcReader = null;
7777                 stateObj.SniContext = SniContext.Snix_Read;
7778                 if (returnReader) {
7779                     dtcReader = new SqlDataReader(null, CommandBehavior.Default);
7780                     Debug.Assert(this == stateObj.Parser, "different parser");
7781 #if DEBUG
7782                     // Remove the current owner of stateObj - otherwise we will hit asserts
7783                     stateObj.Owner = null;
7784 #endif
7785                     dtcReader.Bind(stateObj);
7786
7787                     // force consumption of metadata
7788                     _SqlMetaDataSet metaData = dtcReader.MetaData;
7789                 }
7790                 else {
7791                     Run(RunBehavior.UntilDone, null, null, null, stateObj);
7792                 }
7793
7794                 // If the retained ID is no longer valid (because we are enlisting in null or a new transaction) then it should be cleared
7795                 if (((request == TdsEnums.TransactionManagerRequestType.Begin) || (request == TdsEnums.TransactionManagerRequestType.Propagate)) && ((transaction == null) || (transaction.TransactionId != _retainedTransactionId))) {
7796                     _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
7797                 }
7798
7799                 return dtcReader;
7800             }
7801             catch (Exception e) {
7802                 // 
7803                 if (!ADP.IsCatchableExceptionType(e)) {
7804                     throw;
7805                 }
7806
7807                 FailureCleanup(stateObj, e);
7808
7809                 throw;
7810             }
7811             finally {
7812                 // SQLHotfix 50000518
7813                 // make sure we don't leave temporary fields set when leaving this function
7814                 _pendingTransaction = null;
7815
7816                 _asyncWrite = hadAsyncWrites;
7817
7818                 if (!callerHasConnectionLock) {
7819                     _connHandler.ThreadHasParserLockForClose = false;
7820                     _connHandler._parserLock.Release();
7821                 }
7822             }
7823         }
7824
7825         internal void FailureCleanup(TdsParserStateObject stateObj, Exception e) {
7826             int old_outputPacketNumber = stateObj._outputPacketNumber;
7827
7828             if (Bid.TraceOn) {
7829                 Bid.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception caught on ExecuteXXX: '%ls' \n", e.ToString());
7830             }
7831
7832             if (stateObj.HasOpenResult) { // SQL BU DT 383773 - need to decrement openResultCount if operation failed.
7833                 stateObj.DecrementOpenResultCount();                
7834             }
7835
7836             // be sure to wipe out our buffer if we started sending stuff
7837             stateObj.ResetBuffer();
7838             stateObj._outputPacketNumber = 1;  // end of message - reset to 1 - per ramas
7839
7840             if (old_outputPacketNumber != 1 && _state == TdsParserState.OpenLoggedIn) {
7841                 Debug.Assert(_connHandler._parserLock.ThreadMayHaveLock(), "Should not be calling into FailureCleanup without first taking the parser lock");
7842
7843                 bool originalThreadHasParserLock = _connHandler.ThreadHasParserLockForClose;
7844                 try {
7845                     // Dev11 Bug 385286 : ExecuteNonQueryAsync hangs when trying to write a parameter which generates ArgumentException and while handling that exception the server disconnects the connection
7846                     // Need to set this to true such that if we have an error sending\processing the attention, we won't deadlock ourselves
7847                     _connHandler.ThreadHasParserLockForClose = true;
7848
7849                     // If _outputPacketNumber prior to ResetBuffer was not equal to 1, a packet was already
7850                     // sent to the server and so we need to send an attention and process the attention ack.
7851                     stateObj.SendAttention();
7852                     ProcessAttention(stateObj);
7853                 }
7854                 finally {
7855                     // Reset the ThreadHasParserLock value incase our caller expects it to be set\not set
7856                     _connHandler.ThreadHasParserLockForClose = originalThreadHasParserLock;
7857                 }
7858             }
7859
7860             Bid.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception rethrown. \n");
7861         }
7862
7863         internal Task TdsExecuteSQLBatch(string text, int timeout, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool sync, bool callerHasConnectionLock = false) {
7864             if (TdsParserState.Broken == State || TdsParserState.Closed == State) {
7865                 return null;
7866             }
7867
7868             if (stateObj.BcpLock) {
7869                 throw SQL.ConnectionLockedForBcpEvent();
7870             }
7871
7872             // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7873             // delegated transactions often happen while there is an open result
7874             // set, so we need to handle them by using a different MARS session, 
7875             // otherwise we'll write on the physical state objects while someone
7876             // else is using it.  When we don't have MARS enabled, we need to 
7877             // lock the physical state object to syncronize it's use at least 
7878             // until we increment the open results count.  Once it's been 
7879             // incremented the delegated transaction requests will fail, so they
7880             // won't stomp on anything.
7881
7882             // Only need to take the lock if neither the thread nor the caller claims to already have it
7883             bool needToTakeParserLock = (!callerHasConnectionLock) && (!_connHandler.ThreadHasParserLockForClose);
7884             Debug.Assert(!_connHandler.ThreadHasParserLockForClose || sync, "Thread shouldn't claim to have the parser lock if we are doing async writes");     // Since we have the possibility of pending with async writes, make sure the thread doesn't claim to already have the lock
7885             Debug.Assert(needToTakeParserLock || _connHandler._parserLock.ThreadMayHaveLock(), "Thread or caller claims to have connection lock, but lock is not taken");
7886
7887             bool releaseConnectionLock = false;
7888             if (needToTakeParserLock) {
7889                 _connHandler._parserLock.Wait(canReleaseFromAnyThread: !sync);
7890                 releaseConnectionLock = true;
7891             }
7892
7893             // Switch the writing mode
7894             // NOTE: We are not turning off async writes when we complete since SqlBulkCopy uses this method and expects _asyncWrite to not change
7895             _asyncWrite = !sync;
7896
7897             try {
7898                 // Check that the connection is still alive
7899                 if ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken)) {
7900                     throw ADP.ClosedConnectionError();
7901                 }
7902
7903                 // This validation step MUST be done after locking the connection to guarantee we don't 
7904                 //  accidentally execute after the transaction has completed on a different thread.
7905                 _connHandler.CheckEnlistedTransactionBinding();
7906
7907                 stateObj.SetTimeoutSeconds(timeout);
7908                 if ((!_fMARS) && (_physicalStateObj.HasOpenResult)) 
7909                 {
7910                     Bid.Trace("<sc.TdsParser.TdsExecuteSQLBatch|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID);
7911                 }
7912                 stateObj.SniContext = SniContext.Snix_Execute;
7913
7914                 if (_isYukon) {
7915
7916                     WriteRPCBatchHeaders(stateObj, notificationRequest);
7917                 }
7918
7919                 stateObj._outputMessageType = TdsEnums.MT_SQL;
7920
7921                 WriteString(text, text.Length, 0, stateObj);
7922
7923                 Task executeTask = stateObj.ExecuteFlush();
7924                 if (executeTask == null) {
7925                     stateObj.SniContext = SniContext.Snix_Read;
7926                 }
7927                 else {
7928                     Debug.Assert(!sync, "Should not have gotten a Task when writing in sync mode");
7929
7930                     // Need to wait for flush - continuation will unlock the connection                    
7931                     bool taskReleaseConnectionLock = releaseConnectionLock;
7932                     releaseConnectionLock = false;
7933                     return executeTask.ContinueWith(t => {
7934                         Debug.Assert(!t.IsCanceled, "Task should not be canceled");
7935                         try {
7936                             if (t.IsFaulted) {
7937                                 FailureCleanup(stateObj, t.Exception.InnerException);
7938                                 throw t.Exception.InnerException;
7939                             }
7940                             else {
7941                                 stateObj.SniContext = SniContext.Snix_Read;
7942                             }
7943                         }
7944                         finally {
7945                             if (taskReleaseConnectionLock) {
7946                                 _connHandler._parserLock.Release();
7947                             }
7948                         }
7949                     }, TaskScheduler.Default);
7950                 }
7951
7952                 // Finished sync
7953                 return null;
7954             }
7955             catch (Exception e) {
7956                 //Debug.Assert(_state == TdsParserState.Broken, "Caught exception in TdsExecuteSQLBatch but connection was not broken!");
7957                 // 
7958                 if (!ADP.IsCatchableExceptionType(e)) {
7959                     throw;
7960                 }
7961
7962                 FailureCleanup(stateObj, e);
7963
7964                 throw;
7965             }
7966             finally {
7967                 if (releaseConnectionLock) {
7968                     _connHandler._parserLock.Release();
7969                 }
7970             }
7971         }      
7972
7973       internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool isCommandProc, bool sync = true, 
7974           TaskCompletionSource<object> completion = null, int startRpc = 0, int startParam = 0) {
7975           bool firstCall = (completion == null);
7976           bool releaseConnectionLock = false;
7977
7978           Debug.Assert(cmd != null, @"cmd cannot be null inside TdsExecuteRPC");
7979           Debug.Assert(!firstCall || startRpc == 0, "startRpc is not 0 on first call");
7980           Debug.Assert(!firstCall || startParam == 0, "startParam is not 0 on first call");
7981           Debug.Assert(!firstCall || !_connHandler.ThreadHasParserLockForClose, "Thread should not already have connection lock");
7982           Debug.Assert(firstCall || _connHandler._parserLock.ThreadMayHaveLock(), "Connection lock not taken after the first call");
7983           try {
7984               _SqlRPC rpcext = null;
7985               int tempLen;
7986
7987               // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7988               // delegated transactions often happen while there is an open result
7989               // set, so we need to handle them by using a different MARS session, 
7990               // otherwise we'll write on the physical state objects while someone
7991               // else is using it.  When we don't have MARS enabled, we need to 
7992               // lock the physical state object to syncronize it's use at least 
7993               // until we increment the open results count.  Once it's been 
7994               // incremented the delegated transaction requests will fail, so they
7995               // won't stomp on anything.
7996
7997
7998               if (firstCall) {
7999                   _connHandler._parserLock.Wait(canReleaseFromAnyThread:!sync);
8000                   releaseConnectionLock = true;
8001               }
8002               try {
8003                   // Ensure that connection is alive
8004                   if ((TdsParserState.Broken == State) || (TdsParserState.Closed == State)) {
8005                       throw ADP.ClosedConnectionError();
8006                   }
8007
8008                   // This validation step MUST be done after locking the connection to guarantee we don't 
8009                   //  accidentally execute after the transaction has completed on a different thread.
8010                   if (firstCall) {
8011                       _asyncWrite = !sync;
8012
8013                       _connHandler.CheckEnlistedTransactionBinding();
8014
8015                       stateObj.SetTimeoutSeconds(timeout);
8016                       if ((!_fMARS) && (_physicalStateObj.HasOpenResult)) 
8017                       {
8018                           Bid.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID);
8019                       }
8020                       stateObj.SniContext = SniContext.Snix_Execute;
8021
8022                       if (_isYukon) {
8023
8024                           WriteRPCBatchHeaders(stateObj, notificationRequest);
8025                       }
8026
8027                       stateObj._outputMessageType = TdsEnums.MT_RPC;
8028                   }
8029
8030                   for (int ii = startRpc; ii < rpcArray.Length; ii++) {
8031                       rpcext = rpcArray[ii];
8032
8033                       if (startParam == 0 || ii > startRpc) {
8034                           if (rpcext.ProcID != 0 && _isShiloh) {
8035                               // Perf optimization for Shiloh and later,
8036                               Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255");
8037                               WriteShort(0xffff, stateObj);
8038                               WriteShort((short)(rpcext.ProcID), stateObj);
8039                           }
8040                           else {
8041                               Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name");
8042                               tempLen = rpcext.rpcName.Length;
8043                               WriteShort(tempLen, stateObj);
8044                               WriteString(rpcext.rpcName, tempLen, 0, stateObj);
8045                           }
8046
8047                           // Options
8048                           WriteShort((short)rpcext.options, stateObj);
8049                       }
8050
8051                       // Stream out parameters
8052                       SqlParameter[] parameters = rpcext.parameters;
8053
8054                       for (int i = (ii == startRpc) ? startParam : 0; i < parameters.Length; i++) {
8055                           //                Debug.WriteLine("i:  " + i.ToString(CultureInfo.InvariantCulture));
8056                           // parameters can be unnamed
8057                           SqlParameter param = parameters[i];
8058                           // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters.
8059                           if (param == null)
8060                               break;      // End of parameters for this execute
8061
8062                           // Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
8063                           if (param.ForceColumnEncryption && 
8064                               !(cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled || 
8065                               (cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && cmd.Connection.IsColumnEncryptionSettingEnabled))) {
8066                               throw SQL.ParamInvalidForceColumnEncryptionSetting(param.ParameterName, rpcext.GetCommandTextOrRpcName());
8067                           }
8068
8069                           // Check if the applications wants to force column encryption to avoid sending sensitive data to server
8070                           if (param.ForceColumnEncryption && param.CipherMetadata == null
8071                               && (param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput)) {
8072                               // Application wants a parameter to be encrypted before sending it to server, however server doesnt think this parameter needs encryption.
8073                               throw SQL.ParamUnExpectedEncryptionMetadata(param.ParameterName, rpcext.GetCommandTextOrRpcName());
8074                           }
8075
8076                           // Validate parameters are not variable length without size and with null value.  MDAC 66522
8077                           param.Validate(i, isCommandProc);
8078
8079                           // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
8080                           MetaType mt = param.InternalMetaType;
8081
8082                           if (mt.IsNewKatmaiType) {
8083                               WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj);
8084                               continue;
8085                           }
8086
8087                           if ((!_isShiloh && !mt.Is70Supported) ||
8088                               (!_isYukon && !mt.Is80Supported) ||
8089                               (!_isKatmai && !mt.Is90Supported)) {
8090                               throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
8091                           }
8092                           object value = null;
8093                           bool isNull = true;
8094                           bool isSqlVal = false;
8095                           bool isDataFeed = false;
8096                           // if we have an output param, set the value to null so we do not send it across to the server
8097                           if (param.Direction == ParameterDirection.Output) {
8098                               isSqlVal = 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.
8099                               param.Value = null;
8100                               param.ParamaterIsSqlType = isSqlVal;
8101                           }
8102                           else {
8103                               value = param.GetCoercedValue();
8104                               isNull = param.IsNull;
8105                               if (!isNull) {
8106                                   isSqlVal = param.CoercedValueIsSqlType;
8107                                   isDataFeed = param.CoercedValueIsDataFeed;
8108                               }
8109                           }
8110
8111                           WriteParameterName(param.ParameterNameFixed, stateObj);
8112
8113                           // Write parameter status
8114                           stateObj.WriteByte(rpcext.paramoptions[i]);
8115
8116                           // MaxLen field is only written out for non-fixed length data types
8117                           // use the greater of the two sizes for maxLen
8118                           int actualSize;
8119                           int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize();
8120
8121                           //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
8122                           if (mt.TDSType != TdsEnums.SQLUDT)
8123                               // getting the actualSize is expensive, cache here and use below
8124                               actualSize = param.GetActualSize();
8125                           else
8126                               actualSize = 0; //get this later
8127
8128                           byte precision = 0;
8129                           byte scale = 0;
8130
8131                           // scale and precision are only relevant for numeric and decimal types
8132                           // adjust the actual value scale and precision to match the user specified
8133                           if (mt.SqlDbType == SqlDbType.Decimal) {
8134                               precision = param.GetActualPrecision();
8135                               scale = param.GetActualScale();
8136
8137                               if (precision > TdsEnums.MAX_NUMERIC_PRECISION) {
8138                                   throw SQL.PrecisionValueOutOfRange(precision);
8139                               }
8140
8141                               // bug 49512, make sure the value matches the scale the user enters
8142                               if (!isNull) {
8143                                   if (isSqlVal) {
8144                                       value = AdjustSqlDecimalScale((SqlDecimal)value, scale);
8145
8146                                       // If Precision is specified, verify value precision vs param precision
8147                                       if (precision != 0) {
8148                                           if (precision < ((SqlDecimal)value).Precision) {
8149                                               throw ADP.ParameterValueOutOfRange((SqlDecimal)value);
8150                                           }
8151                                       }
8152                                   }
8153                                   else {
8154                                       value = AdjustDecimalScale((Decimal)value, scale);
8155
8156                                       SqlDecimal sqlValue = new SqlDecimal((Decimal)value);
8157
8158                                       // If Precision is specified, verify value precision vs param precision
8159                                       if (precision != 0) {
8160                                           if (precision < sqlValue.Precision) {
8161                                               throw ADP.ParameterValueOutOfRange((Decimal)value);
8162                                           }
8163                                       }
8164                                   }
8165                               }
8166                           }
8167
8168                           bool isParameterEncrypted = 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_ENCRYPTED);
8169
8170                           // Additional information we need to send over wire to the server when writing encrypted parameters.
8171                           SqlColumnEncryptionInputParameterInfo encryptedParameterInfoToWrite = null;
8172
8173                           // If the parameter is encrypted, we need to encrypt the value.
8174                           if (isParameterEncrypted) {
8175                               Debug.Assert(mt.TDSType != TdsEnums.SQLVARIANT &&
8176                                   mt.TDSType != TdsEnums.SQLUDT &&
8177                                   mt.TDSType != TdsEnums.SQLXMLTYPE &&
8178                                   mt.TDSType != TdsEnums.SQLIMAGE &&
8179                                   mt.TDSType != TdsEnums.SQLTEXT &&
8180                                   mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");
8181
8182                               byte[] serializedValue = null;
8183                               byte[] encryptedValue = null;
8184
8185                               if (!isNull) {
8186                                   try {
8187                                     if (isSqlVal) {
8188                                         serializedValue = SerializeUnencryptedSqlValue(value, mt, actualSize, param.Offset, param.NormalizationRuleVersion, stateObj);
8189                                     }
8190                                     else {
8191                                         // for codePageEncoded types, WriteValue simply expects the number of characters
8192                                         // For plp types, we also need the encoded byte size
8193                                         serializedValue = SerializeUnencryptedValue(value, mt, param.GetActualScale(), actualSize, param.Offset, isDataFeed, param.NormalizationRuleVersion, stateObj);
8194                                     }
8195
8196                                     Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC.");
8197                                     encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.ConnectionOptions.DataSource);
8198                                 }
8199                                 catch (Exception e) {
8200                                     throw SQL.ParamEncryptionFailed(param.ParameterName, null, e);
8201                                 }
8202
8203                                   Debug.Assert(encryptedValue != null && encryptedValue.Length > 0,
8204                                       "encryptedValue should not be null or empty in TdsExecuteRPC.");
8205                               }
8206                               else {
8207                                   encryptedValue = null;
8208                               }
8209
8210                               // Change the datatype to varbinary(max).
8211                               // Since we don't know the size of the encrypted parameter on the server side, always set to (max).
8212                               //
8213                               mt = MetaType.MetaMaxVarBinary;
8214                               size = -1;
8215                               actualSize = (encryptedValue == null) ? 0 : encryptedValue.Length;
8216
8217                               encryptedParameterInfoToWrite = new SqlColumnEncryptionInputParameterInfo(param.GetMetadataForTypeInfo(),
8218                                                                                                         param.CipherMetadata);
8219
8220                               // Set the value to the encrypted value and mark isSqlVal as false for VARBINARY encrypted value.
8221                               value = encryptedValue;
8222                               isSqlVal = false;
8223                           }
8224
8225                           Debug.Assert(isParameterEncrypted == (encryptedParameterInfoToWrite != null),
8226                                             "encryptedParameterInfoToWrite can be not null if and only if isParameterEncrypted is true.");
8227
8228                           Debug.Assert(!isSqlVal || !isParameterEncrypted, "isParameterEncrypted can be true only if isSqlVal is false.");
8229
8230                           //
8231                           // fixup the types by using the NullableType property of the MetaType class
8232                           //
8233                           // following rules should be followed based on feedback from the M-SQL team
8234                           // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
8235                           // 2) always use nullable types (ex: instead of SQLINT use SQLINTN)
8236                           // 3) DECIMALN should always be sent as NUMERICN
8237                           //
8238                           stateObj.WriteByte(mt.NullableType);
8239
8240                           // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
8241                           if (mt.TDSType == TdsEnums.SQLVARIANT) {
8242                               // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant
8243                               // param.GetActualSize is not used
8244                               WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj);
8245                               continue;
8246                           }
8247
8248                           int codePageByteSize = 0;
8249                           int maxsize = 0;
8250
8251                           if (mt.IsAnsiType) {
8252                               // Avoid the following code block if ANSI but unfilled LazyMat blob
8253                               if ((!isNull) && (!isDataFeed)) {
8254                                   string s;
8255
8256                                   if (isSqlVal) {
8257                                       if (value is SqlString) {
8258                                           s = ((SqlString)value).Value;
8259                                       }
8260                                       else {
8261                                           Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype");
8262                                           s = new String(((SqlChars)value).Value);
8263                                       }
8264                                   }
8265                                   else {
8266                                       s = (string)value;
8267                                   }
8268
8269                                   codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding);
8270                               }
8271
8272                               if (mt.IsPlp) {
8273                                   WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
8274                               }
8275                               else {
8276                                   maxsize = (size > codePageByteSize) ? size : codePageByteSize;
8277                                   if (maxsize == 0) {
8278                                       // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
8279                                       if (mt.IsNCharType)
8280                                           maxsize = 2;
8281                                       else
8282                                           maxsize = 1;
8283                                   }
8284
8285                                   WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
8286                               }
8287                           }
8288                           else {
8289                               // If type timestamp - treat as fixed type and always send over timestamp length, which is 8.
8290                               // For fixed types, we either send null or fixed length for type length.  We want to match that
8291                               // behavior for timestamps.  However, in the case of null, we still must send 8 because if we
8292                               // send null we will not receive a output val.  You can send null for fixed types and still
8293                               // receive a output value, but not for variable types.  So, always send 8 for timestamp because
8294                               // while the user sees it as a fixed type, we are actually representing it as a bigbinary which
8295                               // is variable.
8296                               if (mt.SqlDbType == SqlDbType.Timestamp) {
8297                                   WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj);
8298                               }
8299                               else if (mt.SqlDbType == SqlDbType.Udt) {
8300                                   byte[] udtVal = null;
8301                                   Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;
8302
8303                                   Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!");
8304
8305                                   if (!isNull) {
8306                                       udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize);
8307
8308                                       Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value");
8309                                       size = udtVal.Length;
8310
8311                                       //it may be legitimate, but we dont support it yet
8312                                       if (size < 0 || (size >= UInt16.MaxValue && maxsize != -1))
8313                                           throw new IndexOutOfRangeException();
8314                                   }
8315
8316                                   //if this is NULL value, write special null value
8317                                   byte[] lenBytes = BitConverter.GetBytes((Int64)size);
8318
8319                                   if (ADP.IsEmpty(param.UdtTypeName))
8320                                       throw SQL.MustSetUdtTypeNameForUdtParams();
8321
8322                                   // Split the input name. TypeName is returned as single 3 part name during DeriveParameters.
8323                                   // NOTE: ParseUdtTypeName throws if format is incorrect
8324                                   String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, true /* is UdtTypeName */);
8325                                   if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) {
8326                                       throw ADP.ArgumentOutOfRange("names");
8327                                   }
8328                                   if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) {
8329                                       throw ADP.ArgumentOutOfRange("names");
8330                                   }
8331                                   if (TdsEnums.MAX_SERVERNAME < names[2].Length) {
8332                                       throw ADP.ArgumentOutOfRange("names");
8333                                   }
8334
8335                                   WriteUDTMetaData(value, names[0], names[1], names[2], stateObj);
8336
8337                                   // 
8338                                   if (!isNull) {
8339                                       WriteUnsignedLong((ulong)udtVal.Length, stateObj); // PLP length
8340                                       if (udtVal.Length > 0) { // Only write chunk length if its value is greater than 0
8341                                           WriteInt(udtVal.Length, stateObj);                  // Chunk length
8342                                           stateObj.WriteByteArray(udtVal, udtVal.Length, 0); // Value
8343                                       }
8344                                       WriteInt(0, stateObj);                              // Terminator
8345                                   }
8346                                   else {
8347                                       WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); // PLP Null.
8348                                   }
8349                                   continue; // End of UDT - continue to next parameter.
8350                                   // 
8351                               }
8352                               else if (mt.IsPlp) {
8353                                   if (mt.SqlDbType != SqlDbType.Xml)
8354                                       WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
8355                               }
8356                               else if ((!mt.IsVarTime) && (mt.SqlDbType != SqlDbType.Date)) {   // Time, Date, DateTime2, DateTimeoffset do not have the size written out
8357                                   maxsize = (size > actualSize) ? size : actualSize;
8358                                   if (maxsize == 0 && IsYukonOrNewer) {
8359                                       // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
8360                                       if (mt.IsNCharType)
8361                                           maxsize = 2;
8362                                       else
8363                                           maxsize = 1;
8364                                   }
8365
8366                                   WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
8367                               }
8368                           }
8369
8370                           // scale and precision are only relevant for numeric and decimal types
8371                           if (mt.SqlDbType == SqlDbType.Decimal) {
8372                               if (0 == precision) {
8373                                   if (_isShiloh)
8374                                       stateObj.WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION);
8375                                   else
8376                                       stateObj.WriteByte(TdsEnums.SPHINX_DEFAULT_NUMERIC_PRECISION);
8377                               }
8378                               else
8379                                   stateObj.WriteByte(precision);
8380
8381                               stateObj.WriteByte(scale);
8382                           }
8383                           else if (mt.IsVarTime) {
8384                               stateObj.WriteByte(param.GetActualScale());
8385                           }
8386
8387                           // write out collation or xml metadata
8388
8389                           if (_isYukon && (mt.SqlDbType == SqlDbType.Xml)) {
8390                               if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) ||
8391                                   ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) ||
8392                                   ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))) {
8393                                   stateObj.WriteByte(1);   //Schema present flag
8394
8395                                   if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) {
8396                                       tempLen = (param.XmlSchemaCollectionDatabase).Length;
8397                                       stateObj.WriteByte((byte)(tempLen));
8398                                       WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
8399                                   }
8400                                   else {
8401                                       stateObj.WriteByte(0);       // No dbname
8402                                   }
8403
8404                                   if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) {
8405                                       tempLen = (param.XmlSchemaCollectionOwningSchema).Length;
8406                                       stateObj.WriteByte((byte)(tempLen));
8407                                       WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
8408                                   }
8409                                   else {
8410                                       stateObj.WriteByte(0);      // no xml schema name
8411                                   }
8412                                   if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)) {
8413                                       tempLen = (param.XmlSchemaCollectionName).Length;
8414                                       WriteShort((short)(tempLen), stateObj);
8415                                       WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj);
8416                                   }
8417                                   else {
8418                                       WriteShort(0, stateObj);       // No xml schema collection name
8419                                   }
8420
8421                               }
8422                               else {
8423                                   stateObj.WriteByte(0);       // No schema
8424                               }
8425                           }
8426                           else if (_isShiloh && mt.IsCharType) {
8427                               // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
8428                               SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation;
8429                               Debug.Assert(_defaultCollation != null, "_defaultCollation is null!");
8430
8431                               WriteUnsignedInt(outCollation.info, stateObj);
8432                               stateObj.WriteByte(outCollation.sortId);
8433                           }
8434
8435                           if (0 == codePageByteSize)
8436                               WriteParameterVarLen(mt, actualSize, isNull, stateObj, isDataFeed);
8437                           else
8438                               WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj, isDataFeed);
8439
8440                           Task writeParamTask = null;
8441                           // write the value now
8442                           if (!isNull) {
8443                               if (isSqlVal) {
8444                                   writeParamTask = WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj);
8445                               }
8446                               else {
8447                                   // for codePageEncoded types, WriteValue simply expects the number of characters
8448                                   // For plp types, we also need the encoded byte size
8449                                   writeParamTask = WriteValue(value, mt, isParameterEncrypted ? (byte)0 : param.GetActualScale(), actualSize, codePageByteSize, isParameterEncrypted ? 0 : param.Offset, stateObj, isParameterEncrypted ? 0 : param.Size, isDataFeed);
8450                               }
8451                           }
8452
8453                           // Send encryption metadata for encrypted parameters.
8454                           if (isParameterEncrypted) {
8455                               writeParamTask = WriteEncryptionMetadata(writeParamTask, encryptedParameterInfoToWrite, stateObj);
8456                           }
8457
8458                           if (!sync) {
8459                               if (writeParamTask == null) {
8460                                   writeParamTask = stateObj.WaitForAccumulatedWrites();                             
8461                               }
8462
8463                               if (writeParamTask != null) {
8464                                   Task task = null;
8465                                   if (completion == null) {
8466                                       completion = new TaskCompletionSource<object>();
8467                                       task = completion.Task;
8468                                   }
8469
8470                                   AsyncHelper.ContinueTask(writeParamTask, completion,
8471                                       () => TdsExecuteRPC(cmd, rpcArray, timeout, inSchema, notificationRequest, stateObj, isCommandProc, sync, completion,
8472                                                             startRpc: ii, startParam: i + 1),
8473                                       connectionToDoom: _connHandler,
8474                                       onFailure: exc => TdsExecuteRPC_OnFailure(exc, stateObj));
8475
8476                                   // Take care of releasing the locks
8477                                   if (releaseConnectionLock) {
8478                                       task.ContinueWith(_ => {
8479                                           _connHandler._parserLock.Release();
8480                                       }, TaskScheduler.Default);
8481                                       releaseConnectionLock = false;
8482                                   }
8483
8484                                   return task;
8485                               }
8486                           }
8487 #if DEBUG
8488                           else {
8489                               Debug.Assert(writeParamTask == null, "Should not have a task when executing sync");
8490                           }
8491 #endif
8492                       } // parameter for loop
8493
8494                       // If this is not the last RPC we are sending, add the batch flag
8495                       if (ii < (rpcArray.Length - 1)) {
8496                           if (_isYukon) {
8497                               stateObj.WriteByte(TdsEnums.YUKON_RPCBATCHFLAG);
8498
8499                           }
8500                           else {
8501                               stateObj.WriteByte(TdsEnums.SHILOH_RPCBATCHFLAG);
8502                           }
8503                       }
8504                   } // rpc for loop
8505
8506                   Task execFlushTask = stateObj.ExecuteFlush();
8507                   Debug.Assert(!sync || execFlushTask == null, "Should not get a task when executing sync");
8508                   if (execFlushTask != null) {
8509                       Task task = null;
8510
8511                       if (completion == null) {
8512                           completion = new TaskCompletionSource<object>();
8513                           task = completion.Task;
8514                       }
8515
8516                       bool taskReleaseConnectionLock = releaseConnectionLock;
8517                       execFlushTask.ContinueWith(tsk => ExecuteFlushTaskCallback(tsk, stateObj, completion, taskReleaseConnectionLock), TaskScheduler.Default);
8518                       
8519                       // ExecuteFlushTaskCallback will take care of the locks for us
8520                       releaseConnectionLock = false;
8521                       
8522                       return task;
8523                   }                  
8524               }
8525               catch (Exception e) {
8526                   // 
8527                   if (!ADP.IsCatchableExceptionType(e)) {
8528                       throw;
8529                   }
8530
8531                   FailureCleanup(stateObj, e);
8532
8533                   throw;
8534               }
8535               FinalizeExecuteRPC(stateObj);
8536               if (completion != null) {
8537                   completion.SetResult(null);
8538               }
8539               return null;
8540           }
8541           catch (Exception e) {
8542               FinalizeExecuteRPC(stateObj);
8543               if (completion != null) {
8544                   completion.SetException(e);
8545                   return null;
8546               }
8547               else {
8548                   throw e;
8549               }
8550           }
8551           finally {
8552               Debug.Assert(firstCall || !releaseConnectionLock, "Shouldn't be releasing locks synchronously after the first call");
8553               if (releaseConnectionLock) {
8554                   _connHandler._parserLock.Release();
8555               }
8556           }
8557       }
8558         
8559       private void FinalizeExecuteRPC(TdsParserStateObject stateObj) {
8560           stateObj.SniContext = SniContext.Snix_Read;
8561           _asyncWrite = false;
8562       }
8563
8564       private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateObj) {
8565           RuntimeHelpers.PrepareConstrainedRegions();
8566           try {
8567 #if DEBUG
8568               TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
8569
8570               RuntimeHelpers.PrepareConstrainedRegions();
8571               try {
8572                   tdsReliabilitySection.Start();
8573 #else
8574                   {
8575 #endif //DEBUG
8576                   FailureCleanup(stateObj, exc);
8577               }
8578 #if DEBUG
8579               finally {
8580                   tdsReliabilitySection.Stop();
8581               }
8582 #endif //DEBUG
8583           }
8584           catch (System.OutOfMemoryException) {
8585               _connHandler.DoomThisConnection();
8586               throw;
8587           }
8588           catch (System.StackOverflowException) {
8589               _connHandler.DoomThisConnection();
8590               throw;
8591           }
8592           catch (System.Threading.ThreadAbortException) {
8593               _connHandler.DoomThisConnection();
8594               throw;
8595           }
8596       }
8597
8598      private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, TaskCompletionSource<object> completion, bool releaseConnectionLock) {
8599          try {
8600                 FinalizeExecuteRPC(stateObj);
8601                 if (tsk.Exception != null) {
8602                     Exception exc = tsk.Exception.InnerException;
8603                     RuntimeHelpers.PrepareConstrainedRegions();
8604                     try {
8605     #if DEBUG
8606                         TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
8607
8608                         RuntimeHelpers.PrepareConstrainedRegions();
8609                         try {
8610                             tdsReliabilitySection.Start();
8611     #else
8612                             {
8613     #endif //DEBUG
8614                             FailureCleanup(stateObj, tsk.Exception);
8615                         }
8616     #if DEBUG
8617                         finally {
8618                             tdsReliabilitySection.Stop();
8619                         }
8620     #endif //DEBUG
8621                     }
8622                     catch (System.OutOfMemoryException e) {
8623                         _connHandler.DoomThisConnection();
8624                         completion.SetException(e);
8625                         throw;
8626                     }
8627                     catch (System.StackOverflowException e) {
8628                         _connHandler.DoomThisConnection();
8629                         completion.SetException(e);
8630                         throw;
8631                     }
8632                     catch (System.Threading.ThreadAbortException e) {
8633                         _connHandler.DoomThisConnection();
8634                         completion.SetException(e);
8635                         throw;
8636                     }
8637                     catch (Exception e) {
8638                         exc = e;
8639                     }
8640                     completion.SetException(exc);
8641                 }
8642                 else {
8643                     completion.SetResult(null);
8644                 }
8645         }
8646         finally {
8647             if (releaseConnectionLock) {
8648                 _connHandler._parserLock.Release();
8649             }
8650          }
8651       }
8652
8653
8654       private void WriteParameterName(string parameterName, TdsParserStateObject stateObj) {
8655           // paramLen
8656           // paramName
8657           if (!ADP.IsEmpty(parameterName)) {
8658               Debug.Assert(parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
8659               int tempLen = parameterName.Length & 0xff;
8660               stateObj.WriteByte((byte)tempLen);
8661               WriteString(parameterName, tempLen, 0, stateObj);
8662           }
8663           else {
8664               stateObj.WriteByte(0);
8665           }
8666       }
8667
8668         private static readonly IEnumerable<MSS.SqlDataRecord> __tvpEmptyValue = new List<MSS.SqlDataRecord>().AsReadOnly();
8669         private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj) {
8670             //
8671             // Determine Metadata
8672             //
8673             ParameterPeekAheadValue peekAhead;
8674             MSS.SmiParameterMetaData metaData = param.MetaDataForSmi(out peekAhead);
8675
8676             if (!_isKatmai) {
8677                 MetaType mt = MetaType.GetMetaTypeFromSqlDbType(metaData.SqlDbType, metaData.IsMultiValued);
8678                 throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
8679             }
8680
8681             //
8682             //  Determine value to send
8683             //
8684             object value;
8685             MSS.ExtendedClrTypeCode typeCode;
8686
8687             // if we have an output or default param, set the value to null so we do not send it across to the server
8688             if (sendDefault) {
8689                 // Value for TVP default is empty list, not NULL
8690                 if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued) {
8691                     value = __tvpEmptyValue;
8692                     typeCode = MSS.ExtendedClrTypeCode.IEnumerableOfSqlDataRecord;
8693                 }
8694                 else {
8695                     // Need to send null value for default
8696                     value = null;
8697                     typeCode = MSS.ExtendedClrTypeCode.DBNull;
8698                 }
8699             }
8700             else if (param.Direction == ParameterDirection.Output) {
8701                 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.
8702                 param.Value = null;
8703                 value = null;
8704                 typeCode = MSS.ExtendedClrTypeCode.DBNull;
8705                 param.ParamaterIsSqlType = isCLRType;
8706             }
8707             else {
8708                 value = param.GetCoercedValue();
8709                 typeCode = MSS.MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(
8710                                                     metaData.SqlDbType, metaData.IsMultiValued, value, null, MSS.SmiContextFactory.KatmaiVersion);
8711             }
8712
8713             if (Bid.AdvancedOn) {
8714                 Bid.Trace("<sc.TdsParser.WriteSmiParameter|ADV> %d#, Sending parameter '%ls', default flag=%d, metadata:\n", ObjectID, param.ParameterName, sendDefault?1:0);
8715                 Bid.PutStr(metaData.TraceString(3));
8716                 Bid.Trace("\n");
8717             }
8718
8719             //
8720             // Write parameter metadata
8721             //
8722             WriteSmiParameterMetaData(metaData, sendDefault, stateObj);
8723
8724             //
8725             // Now write the value
8726             //
8727             TdsParameterSetter paramSetter = new TdsParameterSetter(stateObj, metaData);
8728             MSS.ValueUtilsSmi.SetCompatibleValueV200(
8729                                         new MSS.SmiEventSink_Default(),  // TDS Errors/events dealt with at lower level for now, just need an object for processing
8730                                         paramSetter,
8731                                         0,          // ordinal.  TdsParameterSetter only handles one parameter at a time
8732                                         metaData,
8733                                         value,
8734                                         typeCode,
8735                                         param.Offset,
8736                                         0 < param.Size ? param.Size : -1,
8737                                         peekAhead);
8738         }
8739
8740         // Writes metadata portion of parameter stream from an SmiParameterMetaData object.
8741         private void WriteSmiParameterMetaData(MSS.SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj) {
8742             // Determine status
8743             byte status = 0;
8744             if (ParameterDirection.Output == metaData.Direction || ParameterDirection.InputOutput == metaData.Direction) {
8745                 status |= TdsEnums.RPC_PARAM_BYREF;
8746             }
8747
8748             if (sendDefault) {
8749                 status |= TdsEnums.RPC_PARAM_DEFAULT;
8750             }
8751
8752             // Write everything out
8753             WriteParameterName(metaData.Name, stateObj);
8754             stateObj.WriteByte(status);
8755             WriteSmiTypeInfo(metaData, stateObj);
8756         }
8757
8758         // Write a TypeInfo stream
8759         // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR,  SQLNVARCHAR, and SQLBIGVARBINARY
8760         private void WriteSmiTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
8761             switch(metaData.SqlDbType) {
8762                 case SqlDbType.BigInt:
8763                     stateObj.WriteByte(TdsEnums.SQLINTN);
8764                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8765                     break;
8766                 case SqlDbType.Binary:
8767                     stateObj.WriteByte(TdsEnums.SQLBIGBINARY);
8768                     WriteUnsignedShort(checked((ushort)metaData.MaxLength), stateObj);
8769                     break;
8770                 case SqlDbType.Bit:
8771                     stateObj.WriteByte(TdsEnums.SQLBITN);
8772                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8773                     break;
8774                 case SqlDbType.Char:
8775                     stateObj.WriteByte(TdsEnums.SQLBIGCHAR);
8776                     WriteUnsignedShort(checked((ushort)(metaData.MaxLength)), stateObj);
8777                     WriteUnsignedInt(_defaultCollation.info, stateObj); // 
8778                     stateObj.WriteByte(_defaultCollation.sortId);
8779                     break;
8780                 case SqlDbType.DateTime:
8781                     stateObj.WriteByte(TdsEnums.SQLDATETIMN);
8782                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8783                     break;
8784                 case SqlDbType.Decimal:
8785                     stateObj.WriteByte(TdsEnums.SQLNUMERICN);
8786                     stateObj.WriteByte(checked((byte)MetaType.MetaDecimal.FixedLength));   // SmiMetaData's length and actual wire format's length are different
8787                     stateObj.WriteByte(0 == metaData.Precision ? (byte)1 : metaData.Precision);
8788                     stateObj.WriteByte(metaData.Scale);
8789                     break;
8790                 case SqlDbType.Float:
8791                     stateObj.WriteByte(TdsEnums.SQLFLTN);
8792                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8793                     break;
8794                 case SqlDbType.Image:
8795                     stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY);
8796                     WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
8797                     break;
8798                 case SqlDbType.Int:
8799                     stateObj.WriteByte(TdsEnums.SQLINTN);
8800                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8801                     break;
8802                 case SqlDbType.Money:
8803                     stateObj.WriteByte(TdsEnums.SQLMONEYN);
8804                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8805                     break;
8806                 case SqlDbType.NChar:
8807                     stateObj.WriteByte(TdsEnums.SQLNCHAR);
8808                     WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
8809                     WriteUnsignedInt(_defaultCollation.info, stateObj); // 
8810                     stateObj.WriteByte(_defaultCollation.sortId);
8811                     break;
8812                 case SqlDbType.NText:
8813                     stateObj.WriteByte(TdsEnums.SQLNVARCHAR);
8814                     WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
8815                     WriteUnsignedInt(_defaultCollation.info, stateObj); // 
8816                     stateObj.WriteByte(_defaultCollation.sortId);
8817                     break;
8818                 case SqlDbType.NVarChar:
8819                     stateObj.WriteByte(TdsEnums.SQLNVARCHAR);
8820                     if (MSS.SmiMetaData.UnlimitedMaxLengthIndicator == metaData.MaxLength) {
8821                         WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
8822                     }
8823                     else {
8824                         WriteUnsignedShort(checked((ushort)(metaData.MaxLength*2)), stateObj);
8825                     }
8826                     WriteUnsignedInt(_defaultCollation.info, stateObj); // 
8827                     stateObj.WriteByte(_defaultCollation.sortId);
8828                     break;
8829                 case SqlDbType.Real:
8830                     stateObj.WriteByte(TdsEnums.SQLFLTN);
8831                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8832                     break;
8833                 case SqlDbType.UniqueIdentifier:
8834                     stateObj.WriteByte(TdsEnums.SQLUNIQUEID);
8835                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8836                     break;
8837                 case SqlDbType.SmallDateTime:
8838                     stateObj.WriteByte(TdsEnums.SQLDATETIMN);
8839                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8840                     break;
8841                 case SqlDbType.SmallInt:
8842                     stateObj.WriteByte(TdsEnums.SQLINTN);
8843                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8844                     break;
8845                 case SqlDbType.SmallMoney:
8846                     stateObj.WriteByte(TdsEnums.SQLMONEYN);
8847                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8848                     break;
8849                 case SqlDbType.Text:
8850                     stateObj.WriteByte(TdsEnums.SQLBIGVARCHAR);
8851                     WriteUnsignedShort(unchecked((ushort)MSS.SmiMetaData.UnlimitedMaxLengthIndicator), stateObj);
8852                     WriteUnsignedInt(_defaultCollation.info, stateObj); // 
8853                     stateObj.WriteByte(_defaultCollation.sortId);
8854                     break;
8855                 case SqlDbType.Timestamp:
8856                     stateObj.WriteByte(TdsEnums.SQLBIGBINARY);
8857                     WriteShort(checked((int)metaData.MaxLength), stateObj);
8858                     break;
8859                 case SqlDbType.TinyInt:
8860                     stateObj.WriteByte(TdsEnums.SQLINTN);
8861                     stateObj.WriteByte(checked((byte)metaData.MaxLength));
8862                     break;
8863                 case SqlDbType.VarBinary:
8864                     stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY);
8865                     WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj);
8866                     break;
8867                 case SqlDbType.VarChar:
8868                     stateObj.WriteByte(TdsEnums.SQLBIGVARCHAR);
8869                     WriteUnsignedShort(unchecked((ushort)metaData.MaxLength), stateObj);
8870                     WriteUnsignedInt(_defaultCollation.info, stateObj); // 
8871                     stateObj.WriteByte(_defaultCollation.sortId);
8872                     break;
8873                 case SqlDbType.Variant:
8874                     stateObj.WriteByte(TdsEnums.SQLVARIANT);
8875                     WriteInt(checked((int)metaData.MaxLength), stateObj);
8876                     break;
8877                 case SqlDbType.Xml:
8878                     stateObj.WriteByte(TdsEnums.SQLXMLTYPE);
8879                     // Is there a schema
8880                     if (ADP.IsEmpty(metaData.TypeSpecificNamePart1) && ADP.IsEmpty(metaData.TypeSpecificNamePart2) &&
8881                             ADP.IsEmpty(metaData.TypeSpecificNamePart3)) {
8882                         stateObj.WriteByte(0);  // schema not present
8883                     }
8884                     else {
8885                         stateObj.WriteByte(1); // schema present
8886                         WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
8887                         WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
8888                         WriteIdentifierWithShortLength(metaData.TypeSpecificNamePart3, stateObj);
8889                     }
8890                     break;
8891                 case SqlDbType.Udt:
8892                     stateObj.WriteByte(TdsEnums.SQLUDT);
8893                     WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
8894                     WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
8895                     WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj);
8896                     break;
8897                 case SqlDbType.Structured:
8898                     if (metaData.IsMultiValued) {
8899                         WriteTvpTypeInfo(metaData, stateObj);
8900                     }
8901                     else {
8902                         Debug.Assert(false, "SUDTs not yet supported.");
8903                     }
8904                     break;
8905                 case SqlDbType.Date:
8906                     stateObj.WriteByte(TdsEnums.SQLDATE);
8907                     break;
8908                 case SqlDbType.Time:
8909                     stateObj.WriteByte(TdsEnums.SQLTIME);
8910                     stateObj.WriteByte(metaData.Scale);
8911                     break;
8912                 case SqlDbType.DateTime2:
8913                     stateObj.WriteByte(TdsEnums.SQLDATETIME2);
8914                     stateObj.WriteByte(metaData.Scale);
8915                     break;
8916                 case SqlDbType.DateTimeOffset:
8917                     stateObj.WriteByte(TdsEnums.SQLDATETIMEOFFSET);
8918                     stateObj.WriteByte(metaData.Scale);
8919                     break;
8920                 default:
8921                     Debug.Assert(false, "Unknown SqlDbType should have been caught earlier!");
8922                     break;
8923             }
8924         }
8925
8926         private void WriteTvpTypeInfo(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
8927             Debug.Assert(SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued, 
8928                         "Invalid metadata for TVPs. Type=" + metaData.SqlDbType);
8929             // Type token
8930             stateObj.WriteByte((byte)TdsEnums.SQLTABLE);
8931
8932             // 3-part name (DB, Schema, TypeName)
8933             WriteIdentifier(metaData.TypeSpecificNamePart1, stateObj);
8934             WriteIdentifier(metaData.TypeSpecificNamePart2, stateObj);
8935             WriteIdentifier(metaData.TypeSpecificNamePart3, stateObj);
8936
8937             // TVP_COLMETADATA
8938             if (0 == metaData.FieldMetaData.Count) {
8939                 WriteUnsignedShort((ushort)TdsEnums.TVP_NOMETADATA_TOKEN, stateObj);
8940             }
8941             else {
8942                 // COUNT of columns
8943                 WriteUnsignedShort(checked((ushort) metaData.FieldMetaData.Count), stateObj);
8944
8945                 // TvpColumnMetaData for each column (look for defaults in this loop
8946                 MSS.SmiDefaultFieldsProperty defaults = (MSS.SmiDefaultFieldsProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.DefaultFields];
8947                 for(int i=0; i<metaData.FieldMetaData.Count; i++) {
8948                     WriteTvpColumnMetaData(metaData.FieldMetaData[i], defaults[i], stateObj);
8949                 }
8950
8951                 // optional OrderUnique metadata
8952                 WriteTvpOrderUnique(metaData, stateObj);
8953
8954             }
8955
8956             // END of optional metadata
8957             stateObj.WriteByte(TdsEnums.TVP_END_TOKEN);
8958         }
8959
8960         // Write a single TvpColumnMetaData stream to the server
8961         private void WriteTvpColumnMetaData(MSS.SmiExtendedMetaData md, bool isDefault, TdsParserStateObject stateObj) {
8962             // User Type
8963             if (SqlDbType.Timestamp == md.SqlDbType) {
8964                 WriteUnsignedInt(TdsEnums.SQLTIMESTAMP, stateObj);
8965             } else {
8966                 WriteUnsignedInt(0, stateObj);
8967             }
8968
8969             // Flags
8970             ushort status = TdsEnums.Nullable;
8971             if (isDefault) {
8972                 status |= TdsEnums.TVP_DEFAULT_COLUMN;
8973             }
8974             WriteUnsignedShort(status, stateObj);
8975
8976             // Type info
8977             WriteSmiTypeInfo(md, stateObj);
8978
8979             // Column name
8980             // per spec, "ColName is never sent to server or client for TVP, it is required within a TVP to be zero length."
8981             WriteIdentifier(null, stateObj);
8982         }
8983
8984         // temporary-results structure used only by WriteTvpOrderUnique
8985         //  use class to avoid List<T>'s per-struct-instantiated memory costs.
8986         private class TdsOrderUnique {
8987             internal short ColumnOrdinal;
8988             internal byte  Flags;
8989
8990             internal TdsOrderUnique(short ordinal, byte flags) {
8991                 ColumnOrdinal = ordinal;
8992                 Flags = flags;
8993             }
8994         }
8995
8996         private void WriteTvpOrderUnique(MSS.SmiExtendedMetaData metaData, TdsParserStateObject stateObj) {
8997             // TVP_ORDER_UNIQUE token (uniqueness and sort order)
8998
8999             // Merge order and unique keys into a single token stream
9000
9001             MSS.SmiOrderProperty orderProperty = (MSS.SmiOrderProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.SortOrder];
9002             MSS.SmiUniqueKeyProperty uniqueKeyProperty = (MSS.SmiUniqueKeyProperty) metaData.ExtendedProperties[MSS.SmiPropertySelector.UniqueKey];
9003
9004             // Build list from
9005             List<TdsOrderUnique> columnList = new List<TdsOrderUnique>(metaData.FieldMetaData.Count);
9006             for(int i=0; i<metaData.FieldMetaData.Count; i++) {
9007
9008                 // Add appropriate SortOrder flag
9009                 byte flags = 0;
9010                 MSS.SmiOrderProperty.SmiColumnOrder columnOrder = orderProperty[i];
9011                 if (SortOrder.Ascending == columnOrder.Order) {
9012                     flags = TdsEnums.TVP_ORDERASC_FLAG;
9013                 }
9014                 else if (SortOrder.Descending == columnOrder.Order) {
9015                     flags = TdsEnums.TVP_ORDERDESC_FLAG;
9016                 }
9017
9018                 // Add unique key flage if appropriate
9019                 if (uniqueKeyProperty[i]) {
9020                     flags |= TdsEnums.TVP_UNIQUE_FLAG;
9021                 }
9022
9023                 // Remember this column if any flags were set
9024                 if (0 != flags) {
9025                     columnList.Add(new TdsOrderUnique(checked((short)(i+1)), flags));
9026                 }
9027             }
9028
9029             // Write flagged columns to wire...
9030             if (0 < columnList.Count) {
9031                 stateObj.WriteByte(TdsEnums.TVP_ORDER_UNIQUE_TOKEN);
9032                 WriteShort(columnList.Count, stateObj);
9033                 foreach(TdsOrderUnique column in columnList) {
9034                     WriteShort(column.ColumnOrdinal, stateObj);
9035                     stateObj.WriteByte(column.Flags);
9036                 }
9037             }
9038         }
9039
9040         internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) {
9041             // Write DONE packet
9042             //
9043             if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) {
9044                 throw ADP.ClosedConnectionError();
9045             }
9046             stateObj.WriteByte(TdsEnums.SQLDONE);
9047             WriteShort(0, stateObj);
9048             WriteShort(0, stateObj);
9049             WriteInt(0, stateObj);
9050
9051             stateObj._pendingData = true;
9052             stateObj._messageStatus = 0;
9053             return stateObj.WritePacket(TdsEnums.HARDFLUSH);
9054         }
9055
9056         /// <summary>
9057         /// Loads the column encryptions keys into cache. This will read the master key info, 
9058         /// decrypt the CEK and keep it ready for encryption.
9059         /// </summary>
9060         /// <returns></returns>
9061         internal void LoadColumnEncryptionKeys (_SqlMetaDataSet metadataCollection, string serverName) {
9062             if (_serverSupportsColumnEncryption && ShouldEncryptValuesForBulkCopy()) {
9063                 for (int col = 0; col < metadataCollection.Length; col++) {
9064                     if (null != metadataCollection[col]) {
9065                         _SqlMetaData md = metadataCollection[col];
9066                         if (md.isEncrypted) {
9067                             SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, serverName);
9068                         }
9069                     }
9070                 }
9071             }
9072         }
9073
9074         /// <summary>
9075         /// Writes a single entry of CEK Table into TDS Stream (for bulk copy).
9076         /// </summary>
9077         /// <returns></returns>
9078         internal void WriteEncryptionEntries (ref SqlTceCipherInfoTable cekTable, TdsParserStateObject stateObj) {
9079             for (int i =0; i < cekTable.Size; i++) {
9080                 // Write Db ID
9081                 WriteInt(cekTable[i].DatabaseId, stateObj);
9082
9083                 // Write Key ID
9084                 WriteInt(cekTable[i].CekId, stateObj);
9085
9086                 // Write Key Version
9087                 WriteInt(cekTable[i].CekVersion, stateObj);
9088
9089                 // Write 8 bytes of key MD Version
9090                 Debug.Assert (8 == cekTable[i].CekMdVersion.Length);
9091                 stateObj.WriteByteArray (cekTable[i].CekMdVersion, 8, 0);
9092
9093                 // We don't really need to send the keys
9094                 stateObj.WriteByte(0x00);
9095             }
9096         }
9097
9098         /// <summary>
9099         /// Writes a CEK Table (as part of  COLMETADATA token) for bulk copy.
9100         /// </summary>
9101         /// <returns></returns>
9102         internal void WriteCekTable (_SqlMetaDataSet metadataCollection, TdsParserStateObject stateObj) {
9103             if (!_serverSupportsColumnEncryption) {
9104                 return;
9105             }
9106
9107             // If no cek table is present, send a count of 0 for table size
9108             //     Note- Cek table (with 0 entries) will be present if TCE
9109             //     was enabled and server supports it!
9110             // OR if encryption was disabled in connection options
9111             if (!metadataCollection.cekTable.HasValue ||
9112                 !ShouldEncryptValuesForBulkCopy()) {
9113                 WriteShort(0x00, stateObj);
9114                 return;
9115             }
9116
9117             SqlTceCipherInfoTable cekTable = metadataCollection.cekTable.Value;
9118             ushort count = (ushort)cekTable.Size;
9119
9120             WriteShort(count, stateObj);
9121
9122             WriteEncryptionEntries(ref cekTable, stateObj);
9123         }
9124
9125         /// <summary>
9126         /// Writes the UserType and TYPE_INFO values for CryptoMetadata (for bulk copy).
9127         /// </summary>
9128         /// <returns></returns>
9129         internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserStateObject stateObj) {
9130             // Write the UserType (4 byte value)
9131             WriteInt(0x0, stateObj); // 
9132
9133             Debug.Assert(SqlDbType.Xml != mdPriv.type);
9134             Debug.Assert(SqlDbType.Udt != mdPriv.type);
9135
9136             stateObj.WriteByte(mdPriv.tdsType);
9137
9138             switch (mdPriv.type) {
9139                 case SqlDbType.Decimal:
9140                     WriteTokenLength(mdPriv.tdsType, mdPriv.length, stateObj);
9141                     stateObj.WriteByte(mdPriv.precision);
9142                     stateObj.WriteByte(mdPriv.scale);
9143                     break;
9144                 case SqlDbType.Date:
9145                     // Nothing more to write!
9146                     break;
9147                 case SqlDbType.Time:
9148                 case SqlDbType.DateTime2:
9149                 case SqlDbType.DateTimeOffset:
9150                     stateObj.WriteByte(mdPriv.scale);
9151                     break;
9152                 default:
9153                     WriteTokenLength(mdPriv.tdsType, mdPriv.length, stateObj);
9154                     if (mdPriv.metaType.IsCharType && _isShiloh) {
9155                         WriteUnsignedInt(mdPriv.collation.info, stateObj);
9156                         stateObj.WriteByte(mdPriv.collation.sortId);
9157                     }
9158                     break;
9159             }
9160         }
9161
9162         /// <summary>
9163         /// Writes the crypto metadata (as part of COLMETADATA token) for encrypted columns.
9164         /// </summary>
9165         /// <returns></returns>
9166         internal void WriteCryptoMetadata(_SqlMetaData md, TdsParserStateObject stateObj) {
9167             if (!_serverSupportsColumnEncryption || // TCE Feature supported
9168                 !md.isEncrypted || // Column is not encrypted
9169                 !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string
9170                 return; 
9171             }
9172
9173             // Write the ordinal
9174             WriteShort (md.cipherMD.CekTableOrdinal, stateObj);
9175
9176             // Write UserType and TYPEINFO
9177             WriteTceUserTypeAndTypeInfo(md.baseTI, stateObj);
9178
9179             // Write Encryption Algo
9180             stateObj.WriteByte(md.cipherMD.CipherAlgorithmId);
9181
9182             if (TdsEnums.CustomCipherAlgorithmId == md.cipherMD.CipherAlgorithmId) {
9183                 // Write the algorithm name
9184                 Debug.Assert (md.cipherMD.CipherAlgorithmName.Length < 256);
9185                 stateObj.WriteByte((byte)md.cipherMD.CipherAlgorithmName.Length);
9186                 WriteString(md.cipherMD.CipherAlgorithmName, stateObj);
9187             }
9188
9189             // Write Encryption Algo Type
9190             stateObj.WriteByte(md.cipherMD.EncryptionType);
9191
9192             // Write Normalization Version
9193             stateObj.WriteByte(md.cipherMD.NormalizationRuleVersion);
9194         }
9195
9196         internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int count, TdsParserStateObject stateObj) {
9197             if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) {
9198                 throw ADP.ClosedConnectionError();
9199             }
9200
9201             stateObj.WriteByte(TdsEnums.SQLCOLMETADATA);
9202             WriteShort(count, stateObj);
9203
9204             // Write CEK table - 0 count
9205             WriteCekTable(metadataCollection, stateObj);
9206
9207             for (int i = 0; i < metadataCollection.Length; i++) {
9208                 if (metadataCollection[i] != null) {
9209                     _SqlMetaData md = metadataCollection[i];
9210
9211                     // read user type - 4 bytes Yukon, 2 backwards
9212                     if (IsYukonOrNewer) {
9213                         WriteInt(0x0, stateObj);
9214                     }
9215                     else {
9216                         WriteShort(0x0000, stateObj);
9217                     }
9218
9219                     // Write the flags 
9220                     UInt16 flags;
9221                     flags = (UInt16)(md.updatability << 2);
9222                     flags |= (UInt16)(md.isNullable ? (UInt16)TdsEnums.Nullable : (UInt16)0);
9223                     flags |= (UInt16)(md.isIdentity ? (UInt16)TdsEnums.Identity : (UInt16)0);
9224
9225                     // Write the next byte of flags
9226                     if (_serverSupportsColumnEncryption) { // TCE Supported
9227                         if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options
9228                             flags |= (UInt16)(md.isEncrypted ? (UInt16)(TdsEnums.IsEncrypted << 8) : (UInt16)0);
9229                         }
9230                     }
9231
9232                     WriteShort(flags, stateObj);// write the flags
9233
9234                     // todo:
9235                     // for xml WriteTokenLength results in a no-op
9236                     // discuss this with blaine ...
9237                     // (Microsoft) xml datatype does not have token length in its metadata. So it should be a noop.
9238
9239                     switch (md.type) {
9240                         case SqlDbType.Decimal:
9241                             stateObj.WriteByte(md.tdsType);
9242                             WriteTokenLength(md.tdsType, md.length, stateObj);
9243                             stateObj.WriteByte(md.precision);
9244                             stateObj.WriteByte(md.scale);
9245                             break;
9246                         case SqlDbType.Xml:
9247                             // 
9248                             stateObj.WriteByteArray(s_xmlMetadataSubstituteSequence, s_xmlMetadataSubstituteSequence.Length, 0);
9249                             break;
9250                         case SqlDbType.Udt:
9251                             stateObj.WriteByte(TdsEnums.SQLBIGVARBINARY);
9252                             WriteTokenLength(TdsEnums.SQLBIGVARBINARY, md.length, stateObj);
9253                             break;
9254                         case SqlDbType.Date:
9255                             stateObj.WriteByte(md.tdsType);
9256                             break;
9257                         case SqlDbType.Time:
9258                         case SqlDbType.DateTime2:
9259                         case SqlDbType.DateTimeOffset:
9260                             stateObj.WriteByte(md.tdsType);
9261                             stateObj.WriteByte(md.scale);
9262                             break;
9263                         default:
9264                             stateObj.WriteByte(md.tdsType);
9265                             WriteTokenLength(md.tdsType, md.length, stateObj);
9266                             if (md.metaType.IsCharType && _isShiloh) {
9267                                 WriteUnsignedInt(md.collation.info, stateObj);
9268                                 stateObj.WriteByte(md.collation.sortId);
9269                             }
9270                             break;
9271                     }
9272
9273                     if (md.metaType.IsLong && !md.metaType.IsPlp) {
9274                         WriteShort(md.tableName.Length, stateObj);
9275                         WriteString(md.tableName, stateObj);
9276                     }
9277
9278                     WriteCryptoMetadata(md, stateObj);
9279
9280                     stateObj.WriteByte((byte)md.column.Length);
9281                     WriteString(md.column, stateObj);
9282                 }
9283             } // end for loop
9284         }
9285
9286         /// <summary>
9287         /// Determines if a column value should be encrypted when using BulkCopy (based on connectionstring setting).
9288         /// </summary>
9289         /// <returns></returns>
9290         internal bool ShouldEncryptValuesForBulkCopy () {
9291             if (null != _connHandler && 
9292                 null != _connHandler.ConnectionOptions &&
9293                 SqlConnectionColumnEncryptionSetting.Enabled == _connHandler.ConnectionOptions.ColumnEncryptionSetting) {
9294                 return true;
9295             }
9296
9297             return false;
9298         }
9299
9300         /// <summary>
9301         /// Encrypts a column value (for SqlBulkCopy) 
9302         /// </summary>
9303         /// <returns></returns>
9304         internal object EncryptColumnValue (object value, SqlMetaDataPriv metadata, string column, TdsParserStateObject stateObj, bool isDataFeed, bool isSqlType) {
9305             Debug.Assert (_serverSupportsColumnEncryption, "Server doesn't support encryption, yet we received encryption metadata");
9306             Debug.Assert (ShouldEncryptValuesForBulkCopy(), "Encryption attempted when not requested");
9307
9308             if (isDataFeed) { // can't encrypt a stream column
9309                 SQL.StreamNotSupportOnEncryptedColumn(column);
9310             }
9311
9312             int actualLengthInBytes;
9313             switch(metadata.baseTI.metaType.NullableType) {
9314                 case TdsEnums.SQLBIGBINARY:
9315                 case TdsEnums.SQLBIGVARBINARY:
9316                 case TdsEnums.SQLIMAGE:
9317                     // For some datatypes, engine does truncation before storing the value. (For example, when
9318                     // trying to insert a varbinary(7000) into a varbinary(3000) column). Since we encrypt the
9319                     // column values, engine has no way to tell the size of the plaintext datatype. Therefore,
9320                     // we truncate the values based on target column sizes here before encrypting them. This 
9321                     // truncation is only needed if we exceed the max column length or if the target column is
9322                     // not a blob type (eg. varbinary(max)). The actual work of truncating the column happens
9323                     // when we normalize and serialize the data buffers. The serialization routine expects us 
9324                     // to report the size of data to be copied out (for serialization). If we underreport the
9325                     // size, truncation will happen for us!
9326                     actualLengthInBytes = (isSqlType) ? ((SqlBinary)value).Length : ((byte[])value).Length;
9327                     if (metadata.baseTI.length > 0 &&
9328                         actualLengthInBytes > metadata.baseTI.length) { // see comments agove
9329                         actualLengthInBytes = metadata.baseTI.length;
9330                     }
9331                     break;
9332
9333                 case TdsEnums.SQLUNIQUEID:
9334                     actualLengthInBytes = GUID_SIZE;   // that's a constant for guid
9335                     break;
9336                 case TdsEnums.SQLBIGCHAR:
9337                 case TdsEnums.SQLBIGVARCHAR:
9338                 case TdsEnums.SQLTEXT:
9339                     if (null == _defaultEncoding)
9340                     {
9341                         ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
9342                     }
9343
9344                     string stringValue = (isSqlType) ? ((SqlString)value).Value : (string)value;
9345                     actualLengthInBytes = _defaultEncoding.GetByteCount(stringValue);
9346
9347                     // If the string length is > max length, then use the max length (see comments above)
9348                     if (metadata.baseTI.length > 0 && 
9349                         actualLengthInBytes > metadata.baseTI.length) {
9350                         actualLengthInBytes = metadata.baseTI.length; // this ensure truncation!
9351                     }
9352
9353                     break;
9354                 case TdsEnums.SQLNCHAR:
9355                 case TdsEnums.SQLNVARCHAR:
9356                 case TdsEnums.SQLNTEXT:
9357                     actualLengthInBytes = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
9358
9359                     if (metadata.baseTI.length > 0 &&
9360                         actualLengthInBytes > metadata.baseTI.length) { // see comments above
9361                         actualLengthInBytes = metadata.baseTI.length;
9362                     }
9363
9364                     break;
9365
9366                 default:
9367                     actualLengthInBytes = metadata.baseTI.length;
9368                     break;
9369             }
9370
9371             byte[] serializedValue;
9372             if (isSqlType) {
9373                 // SqlType
9374                 serializedValue = SerializeUnencryptedSqlValue (value, 
9375                                             metadata.baseTI.metaType, 
9376                                             actualLengthInBytes,
9377                                             offset : 0, 
9378                                             normalizationVersion: metadata.cipherMD.NormalizationRuleVersion, 
9379                                             stateObj: stateObj);
9380             }
9381             else {
9382                 serializedValue = SerializeUnencryptedValue (value,
9383                                             metadata.baseTI.metaType, 
9384                                             metadata.baseTI.scale,
9385                                             actualLengthInBytes, 
9386                                             offset: 0, 
9387                                             isDataFeed: isDataFeed, 
9388                                             normalizationVersion: metadata.cipherMD.NormalizationRuleVersion, 
9389                                             stateObj: stateObj);
9390             }
9391
9392             Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC.");
9393             return SqlSecurityUtility.EncryptWithKey(
9394                     serializedValue, 
9395                     metadata.cipherMD, 
9396                     _connHandler.ConnectionOptions.DataSource);
9397         }
9398
9399         internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) {
9400             Debug.Assert(!isSqlType || value is INullable, "isSqlType is true, but value can not be type cast to an INullable");
9401             Debug.Assert(!isDataFeed ^ value is DataFeed, "Incorrect value for isDataFeed");
9402
9403             Encoding saveEncoding = _defaultEncoding;
9404             SqlCollation saveCollation = _defaultCollation;
9405             int saveCodePage = _defaultCodePage;
9406             int saveLCID = _defaultLCID;
9407             Task resultTask = null;
9408             Task internalWriteTask = null;
9409
9410             if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) {
9411                 throw ADP.ClosedConnectionError();
9412             }
9413             try {
9414                 if (metadata.encoding != null) {
9415                     _defaultEncoding = metadata.encoding;
9416                 }
9417                 if (metadata.collation != null) {
9418                     _defaultCollation = metadata.collation;
9419                     _defaultLCID = _defaultCollation.LCID;
9420                 }
9421                 _defaultCodePage = metadata.codePage;
9422
9423                 MetaType metatype = metadata.metaType;
9424                 int ccb = 0;
9425                 int ccbStringBytes = 0;
9426
9427                 if (isNull) {
9428                     // For UDT, remember we treat as binary even though it is a PLP
9429                     if (metatype.IsPlp && (metatype.NullableType != TdsEnums.SQLUDT || metatype.IsLong)) {
9430                         WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
9431                     }
9432                     else if (!metatype.IsFixed && !metatype.IsLong && !metatype.IsVarTime) {
9433                         WriteShort(TdsEnums.VARNULL, stateObj);
9434                     }
9435                     else {
9436                         stateObj.WriteByte(TdsEnums.FIXEDNULL);
9437                     }
9438                     return resultTask;
9439                 }
9440
9441                 if (!isDataFeed) {
9442                     switch (metatype.NullableType) {
9443                         case TdsEnums.SQLBIGBINARY:
9444                         case TdsEnums.SQLBIGVARBINARY:
9445                         case TdsEnums.SQLIMAGE:
9446                         case TdsEnums.SQLUDT:
9447                             ccb = (isSqlType) ? ((SqlBinary)value).Length : ((byte[])value).Length;
9448                             break;
9449                         case TdsEnums.SQLUNIQUEID:
9450                             ccb = GUID_SIZE;   // that's a constant for guid
9451                             break;
9452                         case TdsEnums.SQLBIGCHAR:
9453                         case TdsEnums.SQLBIGVARCHAR:
9454                         case TdsEnums.SQLTEXT:
9455                             if (null == _defaultEncoding) {
9456                                 ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
9457                             }
9458
9459                             string stringValue = null;
9460                             if (isSqlType) {
9461                                 stringValue = ((SqlString)value).Value;
9462                             }
9463                             else {
9464                                 stringValue = (string)value;
9465                             }
9466
9467                             ccb = stringValue.Length;
9468                             ccbStringBytes = _defaultEncoding.GetByteCount(stringValue);
9469                             break;
9470                         case TdsEnums.SQLNCHAR:
9471                         case TdsEnums.SQLNVARCHAR:
9472                         case TdsEnums.SQLNTEXT:
9473                             ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
9474                             break;
9475                         case TdsEnums.SQLXMLTYPE:
9476                             // Value here could be string or XmlReader
9477                             if (value is XmlReader) {
9478                                 value = MetaType.GetStringFromXml((XmlReader)value);
9479                             }
9480                             ccb = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2;
9481                             break;
9482
9483                         default:
9484                             ccb = metadata.length;
9485                             break;
9486                     }
9487                 }
9488                 else {
9489                      Debug.Assert(metatype.IsLong &&
9490                          ((metatype.SqlDbType == SqlDbType.VarBinary && value is StreamDataFeed) ||
9491                           ((metatype.SqlDbType == SqlDbType.VarChar || metatype.SqlDbType == SqlDbType.NVarChar) && value is TextDataFeed) || 
9492                           (metatype.SqlDbType == SqlDbType.Xml && value is XmlDataFeed)), 
9493                     "Stream data feed should only be assigned to VarBinary(max), Text data feed should only be assigned to [N]VarChar(max), Xml data feed should only be assigned to XML(max)");
9494                }
9495
9496
9497                 // Expected the text length in data stream for bulk copy of text, ntext, or image data.
9498                 //
9499                 if (metatype.IsLong) {
9500                     switch (metatype.SqlDbType) {
9501                         case SqlDbType.Text:
9502                         case SqlDbType.NText:
9503                         case SqlDbType.Image:
9504                             stateObj.WriteByteArray(s_longDataHeader, s_longDataHeader.Length, 0);
9505                             WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj);
9506                             break;
9507
9508                         case SqlDbType.VarChar:
9509                         case SqlDbType.NVarChar:
9510                         case SqlDbType.VarBinary:
9511                         case SqlDbType.Xml:
9512                         case SqlDbType.Udt:
9513                             // plp data
9514                             WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
9515                             break;
9516                     }
9517                 }
9518                 else {
9519                     WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj);
9520                 }
9521
9522                 if (isSqlType) {
9523                     internalWriteTask = WriteSqlValue(value, metatype, ccb, ccbStringBytes, 0, stateObj);
9524                 }
9525                 else if (metatype.SqlDbType != SqlDbType.Udt || metatype.IsLong) {
9526                     internalWriteTask = WriteValue(value, metatype, metadata.scale, ccb, ccbStringBytes, 0, stateObj, metadata.length, isDataFeed);
9527                     if ((internalWriteTask == null) && (_asyncWrite)) {
9528                         internalWriteTask = stateObj.WaitForAccumulatedWrites();
9529                     }
9530                     Debug.Assert(_asyncWrite || stateObj.WaitForAccumulatedWrites() == null, "Should not have accumulated writes when writing sync");
9531                 }
9532                 else {
9533                     WriteShort(ccb, stateObj);
9534                     internalWriteTask = stateObj.WriteByteArray((byte[])value, ccb, 0);
9535                 }
9536
9537 #if DEBUG
9538                 //In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths.
9539                 if (_asyncWrite && internalWriteTask == null && SqlBulkCopy.SetAlwaysTaskOnWrite == true) {
9540                     internalWriteTask = Task.FromResult<object>(null);
9541                 }
9542 #endif
9543                 if (internalWriteTask != null) { //i.e. the write was async.
9544                     resultTask = WriteBulkCopyValueSetupContinuation(internalWriteTask, saveEncoding, saveCollation, saveCodePage, saveLCID);
9545                 }
9546             }
9547             finally {
9548                 if (internalWriteTask == null) {
9549                     _defaultEncoding = saveEncoding;
9550                     _defaultCollation = saveCollation;
9551                     _defaultCodePage = saveCodePage;
9552                     _defaultLCID = saveLCID;
9553                 }
9554             }
9555             return resultTask;
9556         }
9557
9558         // This is in its own method to avoid always allocating the lambda in WriteBulkCopyValue
9559         private Task WriteBulkCopyValueSetupContinuation(Task internalWriteTask, Encoding saveEncoding, SqlCollation saveCollation, int saveCodePage, int saveLCID) {
9560             return internalWriteTask.ContinueWith<Task>(t => {
9561                 _defaultEncoding = saveEncoding;
9562                 _defaultCollation = saveCollation;
9563                 _defaultCodePage = saveCodePage;
9564                 _defaultLCID = saveLCID;
9565                 return t;
9566             }, TaskScheduler.Default).Unwrap();
9567         }
9568
9569         // Write mars header data, not including the mars header length
9570         private void WriteMarsHeaderData(TdsParserStateObject stateObj, SqlInternalTransaction transaction) {
9571             // Function to send over additional payload header data for Yukon and beyond only.
9572             Debug.Assert(_isYukon, "WriteMarsHeaderData called on a non-Yukon server");
9573
9574             // These are not necessary - can have local started in distributed.
9575             // Debug.Assert(!(null != sqlTransaction && null != distributedTransaction), "Error to have local (api started) and distributed transaction at the same time!");
9576             // Debug.Assert(!(null != _userStartedLocalTransaction && null != distributedTransaction), "Error to have local (started outside of the api) and distributed transaction at the same time!");
9577
9578             // We may need to update the mars header length if mars header is changed in the future
9579
9580             WriteShort(TdsEnums.HEADERTYPE_MARS, stateObj);
9581
9582             if (null != transaction && SqlInternalTransaction.NullTransactionId != transaction.TransactionId) {
9583                 WriteLong(transaction.TransactionId, stateObj);
9584                 WriteInt(stateObj.IncrementAndObtainOpenResultCount(transaction), stateObj);
9585             }
9586             else {
9587                 // If no transaction, send over retained transaction descriptor (empty if none retained)
9588                 //  and always 1 for result count.
9589                 WriteLong(_retainedTransactionId, stateObj);
9590                 WriteInt(stateObj.IncrementAndObtainOpenResultCount(null), stateObj);
9591             }
9592         }
9593
9594         private int GetNotificationHeaderSize(SqlNotificationRequest notificationRequest) {
9595             if (null != notificationRequest) {
9596                 string callbackId = notificationRequest.UserData;
9597                 string service = notificationRequest.Options;
9598                 int timeout = notificationRequest.Timeout;
9599
9600                 if (null == callbackId) {
9601                     throw ADP.ArgumentNull("CallbackId");
9602                 }
9603                 else if (UInt16.MaxValue < callbackId.Length) {
9604                     throw ADP.ArgumentOutOfRange("CallbackId");
9605                 }
9606
9607                 if (null == service) {
9608                     throw ADP.ArgumentNull("Service");
9609                 }
9610                 else if (UInt16.MaxValue < service.Length) {
9611                     throw ADP.ArgumentOutOfRange("Service");
9612                 }
9613
9614                 if (-1 > timeout) {
9615                     throw ADP.ArgumentOutOfRange("Timeout");
9616                 }
9617
9618                 // Header Length (uint) (included in size) (already written to output buffer)
9619                 // Header Type (ushort)
9620                 // NotifyID Length (ushort)
9621                 // NotifyID UnicodeStream (unicode text)
9622                 // SSBDeployment Length (ushort)
9623                 // SSBDeployment UnicodeStream (unicode text)
9624                 // Timeout (uint) -- optional
9625                 // WEBDATA 102263: Don't send timeout value if it is 0
9626
9627                 int headerLength = 4 + 2 + 2 + (callbackId.Length * 2) + 2 + (service.Length * 2);
9628                 if (timeout > 0)
9629                     headerLength += 4;
9630                 return headerLength;
9631             }
9632             else {
9633                 return 0;
9634             }
9635         }
9636
9637         // Write query notificaiton header data, not including the notificaiton header length
9638         private void WriteQueryNotificationHeaderData(SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj) {
9639             Debug.Assert(_isYukon, "WriteQueryNotificationHeaderData called on a non-Yukon server");
9640
9641             // We may need to update the notification header length if the header is changed in the future
9642
9643             Debug.Assert (null != notificationRequest, "notificaitonRequest is null");
9644               
9645             string callbackId = notificationRequest.UserData;
9646             string service = notificationRequest.Options;
9647             int timeout = notificationRequest.Timeout;
9648
9649             // we did verification in GetNotificationHeaderSize, so just assert here.
9650             Debug.Assert(null != callbackId, "CallbackId is null");
9651             Debug.Assert(UInt16.MaxValue >= callbackId.Length, "CallbackId length is out of range");
9652             Debug.Assert(null != service, "Service is null");
9653             Debug.Assert(UInt16.MaxValue >= service.Length, "Service length is out of range");
9654             Debug.Assert(-1 <= timeout, "Timeout");
9655                 
9656
9657             Bid.NotificationsTrace("<sc.TdsParser.WriteQueryNotificationHeader|DEP> NotificationRequest: userData: '%ls', options: '%ls', timeout: '%d'\n", notificationRequest.UserData, notificationRequest.Options, notificationRequest.Timeout);
9658
9659             WriteShort(TdsEnums.HEADERTYPE_QNOTIFICATION, stateObj);      // Query notifications Type
9660
9661             WriteShort(callbackId.Length * 2, stateObj); // Length in bytes
9662             WriteString(callbackId, stateObj);
9663
9664             WriteShort(service.Length * 2, stateObj); // Length in bytes
9665             WriteString(service, stateObj);
9666             if (timeout > 0)
9667                 WriteInt(timeout, stateObj);
9668         }
9669
9670         // Write the trace header data, not including the trace header length
9671         private void WriteTraceHeaderData(TdsParserStateObject stateObj) {
9672             Debug.Assert(this.IncludeTraceHeader, "WriteTraceHeaderData can only be called on a Denali or higher version server and bid trace with the control bit are on");
9673
9674             // We may need to update the trace header length if trace header is changed in the future
9675
9676             ActivityCorrelator.ActivityId actId = ActivityCorrelator.Current;
9677
9678             WriteShort(TdsEnums.HEADERTYPE_TRACE, stateObj);      // Trace Header Type
9679
9680             stateObj.WriteByteArray(actId.Id.ToByteArray(), GUID_SIZE, 0); // Id (Guid)
9681             WriteUnsignedInt(actId.Sequence, stateObj); // sequence number
9682
9683             Bid.Trace("<sc.TdsParser.WriteTraceHeaderData|INFO> ActivityID %ls\n", actId.ToString());
9684         }
9685
9686         private void WriteRPCBatchHeaders(TdsParserStateObject stateObj, SqlNotificationRequest notificationRequest) {
9687             Debug.Assert(_isYukon, "WriteRPCBatchHeaders can only be called on Yukon or higher version servers");
9688             
9689             /* Header:
9690                TotalLength  - DWORD  - including all headers and lengths, including itself
9691                Each Data Session:
9692                {
9693                      HeaderLength - DWORD  - including all header length fields, including itself
9694                      HeaderType   - USHORT
9695                      HeaderData
9696                }
9697             */
9698
9699             int notificationHeaderSize = GetNotificationHeaderSize(notificationRequest);
9700
9701             const int marsHeaderSize = 18; // 4 + 2 + 8 + 4
9702
9703             // Header Length (DWORD)
9704             // Header Type (ushort)
9705             // Trace Data Guid
9706             // Trace Data Sequence Number (uint)
9707             const int traceHeaderSize = 26;  // 4 + 2 + GUID_SIZE + sizeof(UInt32);
9708
9709             // TotalLength  - DWORD  - including all headers and lengths, including itself
9710             int totalHeaderLength = this.IncludeTraceHeader ? (4 + marsHeaderSize + notificationHeaderSize + traceHeaderSize) : (4 + marsHeaderSize + notificationHeaderSize);
9711             Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length");
9712             // Write total header length
9713             WriteInt(totalHeaderLength, stateObj);
9714
9715             // Write Mars header length
9716             WriteInt(marsHeaderSize, stateObj);
9717             // Write Mars header data
9718             WriteMarsHeaderData(stateObj, CurrentTransaction);
9719
9720             if (0 != notificationHeaderSize) {
9721                 // Write Notification header length
9722                 WriteInt(notificationHeaderSize, stateObj);
9723                 // Write notificaiton header data
9724                 WriteQueryNotificationHeaderData(notificationRequest, stateObj);
9725             }
9726
9727             if (IncludeTraceHeader) {
9728                 
9729                 // Write trace header length
9730                 WriteInt(traceHeaderSize, stateObj);
9731                 // Write trace header data
9732                 WriteTraceHeaderData(stateObj);
9733             }
9734         }
9735
9736
9737         //
9738         // Reverse function of GetTokenLength
9739         //
9740         private void WriteTokenLength(byte token, int length, TdsParserStateObject stateObj) {
9741             int tokenLength = 0;
9742
9743             Debug.Assert(token != 0, "0 length token!");
9744
9745             // For Plp fields, this should only be used when writing to metadata header.
9746             // For actual data length, WriteDataLength should be used.
9747             // For Xml fields, there is no token length field. For MAX fields it is 0xffff.
9748             if (_isYukon) {     // Handle Yukon specific exceptions
9749                 if (TdsEnums.SQLUDT == token) {
9750                     tokenLength = 8;
9751                 }
9752                 else if (token == TdsEnums.SQLXMLTYPE) {
9753                     tokenLength = 8;
9754                 }
9755             }
9756
9757             if (tokenLength == 0) {
9758                 switch (token & TdsEnums.SQLLenMask) {
9759                     case TdsEnums.SQLFixedLen:
9760                         Debug.Assert(length == 0x01 << ((token & 0x0c) >> 2), "length does not match encoded length in token");
9761                         tokenLength = 0;
9762                         break;
9763
9764                     case TdsEnums.SQLZeroLen:
9765                         tokenLength = 0;
9766                         break;
9767
9768                     case TdsEnums.SQLVarLen:
9769                     case TdsEnums.SQLVarCnt:
9770                         if (0 != (token & 0x80))
9771                             tokenLength = 2;
9772                         else if (0 == (token & 0x0c))
9773
9774                             // 
9775                             tokenLength = 4;
9776                         else
9777                             tokenLength = 1;
9778
9779                         break;
9780
9781                     default:
9782                         Debug.Assert(false, "Unknown token length!");
9783                         break;
9784                 }
9785
9786                 switch (tokenLength) {
9787                     case 1:
9788                         stateObj.WriteByte((byte)length);
9789                         break;
9790
9791                     case 2:
9792                         WriteShort(length, stateObj);
9793                         break;
9794
9795                     case 4:
9796                         WriteInt(length, stateObj);
9797                         break;
9798
9799                     case 8:
9800                         // In the metadata case we write 0xffff for partial length prefixed types.
9801                         //  For actual data length preceding data, WriteDataLength should be used.
9802                         WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
9803                         break;
9804                 } // end switch
9805             }
9806         }
9807
9808         // Returns true if BOM byte mark is needed for an XML value
9809         private bool IsBOMNeeded(MetaType type, object value) {
9810             if (type.NullableType == TdsEnums.SQLXMLTYPE) {
9811                 Type currentType = value.GetType();
9812                 
9813                 if(currentType == typeof(SqlString)) {
9814                     if (!((SqlString)value).IsNull && ((((SqlString)value).Value).Length > 0)) {
9815                         if ((((SqlString)value).Value[0] & 0xff) != 0xff)
9816                             return true;
9817                     }
9818                 }
9819                 else if ((currentType == typeof(String)) && (((String)value).Length > 0)) {
9820                     if ((value != null) && (((String)value)[0] & 0xff) != 0xff)
9821                         return true;
9822                 }
9823                 else if (currentType == typeof(SqlXml)) {
9824                     if (!((SqlXml)value).IsNull) 
9825                         return true;
9826                 }
9827                 else if (currentType == typeof(XmlDataFeed)) {
9828                     return true;             // Values will eventually converted to unicode string here
9829                 }
9830             }
9831             return false;
9832         }
9833
9834         private Task GetTerminationTask(Task unterminatedWriteTask, object value, MetaType type, int actualLength, TdsParserStateObject stateObj, bool isDataFeed) {
9835             if (type.IsPlp && ((actualLength > 0) || isDataFeed)) {
9836                 if (unterminatedWriteTask == null) {
9837                     WriteInt(0, stateObj);
9838                     return null;
9839                 }
9840                 else {
9841                     return AsyncHelper.CreateContinuationTask<int, TdsParserStateObject>(unterminatedWriteTask,
9842                         WriteInt, 0, stateObj,
9843                         connectionToDoom: _connHandler);
9844                 }
9845             }
9846             else {
9847                 return unterminatedWriteTask;
9848             }
9849         }
9850
9851
9852         private Task WriteSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) {
9853             return GetTerminationTask(
9854                 WriteUnterminatedSqlValue(value, type, actualLength, codePageByteSize, offset, stateObj),
9855                 value, type, actualLength, stateObj, false);
9856         }  
9857         
9858         // For MAX types, this method can only write everything in one big chunk. If multiple
9859         // chunk writes needed, please use WritePlpBytes/WritePlpChars
9860         private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLength, int codePageByteSize, int offset, TdsParserStateObject stateObj) {
9861             Debug.Assert(((type.NullableType == TdsEnums.SQLXMLTYPE) ||
9862                    (value is INullable && !((INullable)value).IsNull)),
9863                    "unexpected null SqlType!");
9864
9865             // parameters are always sent over as BIG or N types
9866             switch (type.NullableType) {
9867                 case TdsEnums.SQLFLTN:
9868                     if (type.FixedLength == 4)
9869                         WriteFloat(((SqlSingle)value).Value, stateObj);
9870                     else {
9871                         Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
9872                         WriteDouble(((SqlDouble)value).Value, stateObj);
9873                     }
9874
9875                     break;
9876
9877                 case TdsEnums.SQLBIGBINARY:
9878                 case TdsEnums.SQLBIGVARBINARY:
9879                 case TdsEnums.SQLIMAGE:
9880                     {
9881                         if (type.IsPlp) {
9882                             WriteInt(actualLength, stateObj);               // chunk length
9883                         }
9884
9885                         if (value is SqlBinary) {
9886                             return stateObj.WriteByteArray(((SqlBinary)value).Value, actualLength, offset, canAccumulate:false);
9887                         }
9888                         else {
9889                             Debug.Assert(value is SqlBytes);
9890                             return stateObj.WriteByteArray(((SqlBytes)value).Value, actualLength, offset, canAccumulate:false);
9891                         }
9892                     }
9893
9894                 case TdsEnums.SQLUNIQUEID: 
9895                     {
9896                         byte[] b = ((SqlGuid)value).ToByteArray();
9897
9898                         Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object");
9899                         stateObj.WriteByteArray(b, actualLength, 0);
9900                         break;
9901                     }
9902
9903                 case TdsEnums.SQLBITN: 
9904                     {
9905                         Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
9906                         if (((SqlBoolean)value).Value == true)
9907                             stateObj.WriteByte(1);
9908                         else
9909                             stateObj.WriteByte(0);
9910
9911                         break;
9912                     }
9913
9914                 case TdsEnums.SQLINTN:
9915                     if (type.FixedLength == 1)
9916                         stateObj.WriteByte(((SqlByte)value).Value);
9917                     else
9918                         if (type.FixedLength == 2)
9919                             WriteShort(((SqlInt16)value).Value, stateObj);
9920                         else
9921                             if (type.FixedLength == 4)
9922                                 WriteInt(((SqlInt32)value).Value, stateObj);
9923                             else {
9924                                 Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
9925                                 WriteLong(((SqlInt64)value).Value, stateObj);
9926                             }
9927
9928                     break;
9929
9930                 case TdsEnums.SQLBIGCHAR:
9931                 case TdsEnums.SQLBIGVARCHAR:
9932                 case TdsEnums.SQLTEXT:
9933                     if (type.IsPlp) {
9934                         WriteInt(codePageByteSize, stateObj);               // chunk length
9935                     }
9936                     if (value is SqlChars) {
9937                         String sch = new String(((SqlChars)value).Value);
9938
9939                         return WriteEncodingChar(sch, actualLength, offset, _defaultEncoding, stateObj, canAccumulate:false);
9940                     }
9941                     else {
9942                         Debug.Assert(value is SqlString);
9943                         return WriteEncodingChar(((SqlString)value).Value, actualLength, offset, _defaultEncoding, stateObj, canAccumulate:false);
9944                     }
9945
9946
9947                 case TdsEnums.SQLNCHAR:
9948                 case TdsEnums.SQLNVARCHAR:
9949                 case TdsEnums.SQLNTEXT:
9950                 case TdsEnums.SQLXMLTYPE:
9951
9952                     if (type.IsPlp) {
9953                         if(IsBOMNeeded(type, value)) {
9954                             WriteInt(actualLength+2, stateObj);               // chunk length
9955                             WriteShort(TdsEnums.XMLUNICODEBOM , stateObj);
9956                         } else {
9957                             WriteInt(actualLength, stateObj);               // chunk length
9958                         }
9959                     }
9960
9961                     // convert to cchars instead of cbytes
9962                     // Xml type is already converted to string through GetCoercedValue
9963                     if (actualLength != 0)
9964                         actualLength >>= 1;
9965
9966                     if (value is SqlChars) {
9967                         return WriteCharArray(((SqlChars)value).Value, actualLength, offset, stateObj, canAccumulate:false);
9968                     }
9969                     else {
9970                         Debug.Assert(value is SqlString);
9971                         return WriteString(((SqlString)value).Value, actualLength, offset, stateObj, canAccumulate:false);
9972                     }
9973
9974                 case TdsEnums.SQLNUMERICN:
9975                     Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
9976                     WriteSqlDecimal((SqlDecimal)value, stateObj);
9977                     break;
9978
9979                 case TdsEnums.SQLDATETIMN:
9980                     SqlDateTime dt = (SqlDateTime)value;
9981
9982                     if (type.FixedLength == 4) {
9983                         if (0 > dt.DayTicks || dt.DayTicks > UInt16.MaxValue)
9984                             throw SQL.SmallDateTimeOverflow(dt.ToString());
9985
9986                         WriteShort(dt.DayTicks, stateObj);
9987                         WriteShort(dt.TimeTicks / SqlDateTime.SQLTicksPerMinute, stateObj);
9988                     }
9989                     else {
9990                         WriteInt(dt.DayTicks, stateObj);
9991                         WriteInt(dt.TimeTicks, stateObj);
9992                     }
9993
9994                     break;
9995
9996                 case TdsEnums.SQLMONEYN: 
9997                     {
9998                         WriteSqlMoney((SqlMoney)value, type.FixedLength, stateObj);
9999                         break;
10000                     }
10001
10002                 case TdsEnums.SQLUDT:
10003                     Debug.Assert(false, "Called WriteSqlValue on UDT param.Should have already been handled");
10004                     throw SQL.UDTUnexpectedResult(value.GetType().AssemblyQualifiedName);
10005
10006                 default:
10007                     Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null));
10008                     break;
10009             } // switch
10010             // return point for accumualated writes, note: non-accumulated writes returned from their case statements
10011             return null;
10012         }
10013
10014         private class TdsOutputStream : Stream {
10015             TdsParser _parser;
10016             TdsParserStateObject _stateObj;
10017             byte[] _preambleToStrip;
10018
10019             public TdsOutputStream(TdsParser parser, TdsParserStateObject stateObj, byte[] preambleToStrip) {
10020                 _parser = parser;
10021                 _stateObj = stateObj;
10022                 _preambleToStrip = preambleToStrip;
10023             }
10024
10025             public override bool CanRead {
10026                 get { return false; }
10027             }
10028
10029             public override bool CanSeek {
10030                 get { return false; }
10031             }
10032
10033             public override bool CanWrite {
10034                 get { return true; }
10035             }
10036
10037             public override void Flush() {
10038                 // NOOP
10039             }
10040
10041             public override long Length {
10042                 get { throw new NotSupportedException(); }
10043             }
10044
10045             public override long Position {
10046                 get {
10047                     throw new NotSupportedException();
10048                 }
10049                 set {
10050                     throw new NotSupportedException();
10051                 }
10052             }
10053
10054             public override int Read(byte[] buffer, int offset, int count) {
10055                 throw new NotSupportedException();
10056             }
10057
10058             public override long Seek(long offset, SeekOrigin origin) {
10059                 throw new NotSupportedException();
10060             }
10061
10062             public override void SetLength(long value) {
10063                 throw new NotSupportedException();
10064             }
10065
10066             private void StripPreamble(byte[] buffer, ref int offset, ref int count) {
10067                 if (_preambleToStrip != null && count >= _preambleToStrip.Length) {
10068
10069                     for (int idx = 0; idx < _preambleToStrip.Length; idx++) {
10070                         if (_preambleToStrip[idx] != buffer[idx]) {
10071                             _preambleToStrip = null;
10072                             return;
10073                         }
10074                     }
10075
10076                     offset += _preambleToStrip.Length;
10077                     count -= _preambleToStrip.Length;
10078                 }
10079                 _preambleToStrip = null;
10080             }
10081
10082             public override void Write(byte[] buffer, int offset, int count) {
10083                 Debug.Assert(!_parser._asyncWrite);
10084                 ValidateWriteParameters(buffer, offset, count);
10085
10086                 StripPreamble(buffer, ref offset, ref count);
10087
10088                 if (count > 0) {
10089                     _parser.WriteInt(count, _stateObj); // write length of chunk
10090                     _stateObj.WriteByteArray(buffer, count, offset);
10091                 }
10092             }
10093
10094             public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) {
10095                 Debug.Assert(_parser._asyncWrite);
10096                 ValidateWriteParameters(buffer, offset, count);
10097
10098                 StripPreamble(buffer, ref offset, ref count);
10099
10100                 RuntimeHelpers.PrepareConstrainedRegions();
10101                 try {
10102 #if DEBUG
10103                     TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
10104
10105                     RuntimeHelpers.PrepareConstrainedRegions();
10106                     try {
10107                         tdsReliabilitySection.Start();
10108 #else
10109                         {
10110 #endif //DEBUG
10111                         Task task = null;
10112                         if (count > 0) {
10113                             _parser.WriteInt(count, _stateObj); // write length of chunk
10114                             task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false);
10115                         }
10116                         if (task == null) {
10117                             return CompletedTask;
10118                         }
10119                         else {
10120                             return task;
10121                         }
10122                     }
10123 #if DEBUG
10124                     finally {
10125                         tdsReliabilitySection.Stop();
10126                     }
10127 #endif //DEBUG
10128                 }
10129                 catch (System.OutOfMemoryException) {
10130                     _parser._connHandler.DoomThisConnection();
10131                     throw;
10132                 }
10133                 catch (System.StackOverflowException) {
10134                     _parser._connHandler.DoomThisConnection();
10135                     throw;
10136                 }
10137                 catch (System.Threading.ThreadAbortException) {
10138                     _parser._connHandler.DoomThisConnection();
10139                     throw;
10140                 }
10141             }
10142
10143             internal static void ValidateWriteParameters(byte[] buffer, int offset, int count) {
10144                 if (buffer == null) {
10145                     throw ADP.ArgumentNull(ADP.ParameterBuffer);
10146                 }
10147                 if (offset < 0) {
10148                     throw ADP.ArgumentOutOfRange(ADP.ParameterOffset);
10149                 }
10150                 if (count < 0) {
10151                     throw ADP.ArgumentOutOfRange(ADP.ParameterCount);
10152                 }
10153                 try {
10154                     if (checked(offset + count) > buffer.Length) {
10155                         throw ExceptionBuilder.InvalidOffsetLength();
10156                     }
10157                 }
10158                 catch (OverflowException) {
10159                     // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
10160                     throw ExceptionBuilder.InvalidOffsetLength();
10161                 }
10162             }
10163
10164         }
10165
10166         private class ConstrainedTextWriter : TextWriter {
10167             TextWriter _next;
10168             int _size;
10169             int _written;
10170
10171             public ConstrainedTextWriter(TextWriter next, int size) {
10172                 _next = next;
10173                 _size = size;
10174                 _written = 0;
10175
10176                 if (_size < 1) {
10177                     _size = int.MaxValue;
10178                 }
10179             }
10180
10181             public bool IsComplete {
10182                 get {
10183                     return _size > 0 && _written >= _size;
10184                 }
10185             }
10186
10187             public override Encoding Encoding {
10188                 get { return _next.Encoding; }
10189             }
10190
10191             public override void Flush() {
10192                 _next.Flush();
10193             }
10194
10195             public override Task FlushAsync() {
10196                 return _next.FlushAsync();
10197             }
10198
10199             public override void Write(char value) {
10200                 if (_written < _size) {
10201                     _next.Write(value);
10202                     _written++;
10203                 }
10204                 Debug.Assert(_size < 0 || _written <= _size, string.Format("Length of data written exceeds specified length.  Written: {0}, specified: {1}", _written, _size));
10205             }
10206
10207             public override void Write(char[] buffer, int index, int count) {
10208
10209                 ValidateWriteParameters(buffer, index, count);
10210                 
10211                 Debug.Assert(_size >= _written);
10212                 count = Math.Min(_size - _written, count);
10213                 if (count > 0) {
10214                     _next.Write(buffer, index, count);
10215                 }
10216                 _written += count;
10217             }
10218
10219             public override Task WriteAsync(char value) {
10220
10221                 if (_written < _size) {
10222                     _written++;
10223                     return _next.WriteAsync(value);
10224                 }
10225
10226                 return CompletedTask;
10227             }
10228
10229             public override Task WriteAsync(char[] buffer, int index, int count) {
10230
10231                 ValidateWriteParameters(buffer, index, count);
10232
10233                 Debug.Assert(_size >= _written);
10234                 count = Math.Min(_size - _written, count);
10235                 if (count > 0) {
10236                     _written += count;
10237                     return _next.WriteAsync(buffer, index, count);
10238                 }
10239
10240                 return CompletedTask;
10241             }
10242
10243             public override Task WriteAsync(string value) {
10244                 return WriteAsync(value.ToCharArray());
10245             }
10246
10247             internal static void ValidateWriteParameters(char[] buffer, int offset, int count) {
10248                 if (buffer == null) {
10249                     throw ADP.ArgumentNull(ADP.ParameterBuffer);
10250                 }
10251                 if (offset < 0) {
10252                     throw ADP.ArgumentOutOfRange(ADP.ParameterOffset);
10253                 }
10254                 if (count < 0) {
10255                     throw ADP.ArgumentOutOfRange(ADP.ParameterCount);
10256                 }
10257                 try {
10258                     if (checked(offset + count) > buffer.Length) {
10259                         throw ExceptionBuilder.InvalidOffsetLength();
10260                     }
10261                 }
10262                 catch (OverflowException) {
10263                     // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
10264                     throw ExceptionBuilder.InvalidOffsetLength();
10265                 }
10266             }
10267
10268         }
10269
10270         private async Task WriteXmlFeed(XmlDataFeed feed, TdsParserStateObject stateObj, bool needBom, Encoding encoding, int size) {
10271             byte[] preambleToSkip = null;
10272             if (!needBom) {
10273                 preambleToSkip = encoding.GetPreamble();
10274             }
10275             ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, preambleToSkip), encoding), size);
10276
10277             XmlWriterSettings writerSettings = new XmlWriterSettings();
10278             writerSettings.CloseOutput = false;         // don't close the memory stream
10279             writerSettings.ConformanceLevel = ConformanceLevel.Fragment;
10280             if (_asyncWrite) {
10281                 writerSettings.Async = true;
10282             }
10283             XmlWriter ww = XmlWriter.Create(writer, writerSettings);
10284
10285             if (feed._source.ReadState == ReadState.Initial) {
10286                 feed._source.Read();
10287             }
10288
10289             while (!feed._source.EOF && !writer.IsComplete) {
10290
10291                 // We are copying nodes from a reader to a writer.  This will cause the
10292                 // XmlDeclaration to be emitted despite ConformanceLevel.Fragment above.
10293                 // Therefore, we filter out the XmlDeclaration while copying.
10294                 if (feed._source.NodeType == XmlNodeType.XmlDeclaration) {
10295                     feed._source.Read();
10296                     continue;
10297                 }
10298
10299                 if (_asyncWrite) {
10300                     await ww.WriteNodeAsync(feed._source, true).ConfigureAwait(false);
10301                 }
10302                 else {
10303                     ww.WriteNode(feed._source, true);
10304                 }
10305             }
10306
10307             if (_asyncWrite) {
10308                 await ww.FlushAsync().ConfigureAwait(false);
10309             }
10310             else {
10311                 ww.Flush();
10312             }
10313         }
10314        
10315         private async Task WriteTextFeed(TextDataFeed feed, Encoding encoding, bool needBom, TdsParserStateObject stateObj, int size) {
10316             Debug.Assert(encoding == null || !needBom);
10317             char[] inBuff = new char[constTextBufferSize];
10318
10319             encoding = encoding ?? new UnicodeEncoding(false, false);
10320             ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size);
10321
10322             if (needBom) {
10323                 if (_asyncWrite) {
10324                     await writer.WriteAsync((char)TdsEnums.XMLUNICODEBOM).ConfigureAwait(false);
10325                 }
10326                 else {
10327                     writer.Write((char)TdsEnums.XMLUNICODEBOM);
10328                 }
10329             }
10330
10331             int nWritten = 0;
10332             do {
10333                 int nRead = 0;
10334
10335                 if (_asyncWrite) {
10336                     nRead = await feed._source.ReadBlockAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false);
10337                 }
10338                 else {
10339                     nRead = feed._source.ReadBlock(inBuff, 0, constTextBufferSize);
10340                 }
10341
10342                 if (nRead == 0) {
10343                     break; 
10344                 }
10345
10346                 if (_asyncWrite) {
10347                     await writer.WriteAsync(inBuff, 0, nRead).ConfigureAwait(false);
10348                 }
10349                 else {
10350                     writer.Write(inBuff, 0, nRead);
10351                 }
10352
10353                 nWritten += nRead;
10354             } while (!writer.IsComplete);
10355
10356             if (_asyncWrite) {
10357                 await writer.FlushAsync().ConfigureAwait(false);
10358             }
10359             else {
10360                 writer.Flush();
10361             }
10362         }
10363
10364         private async Task WriteStreamFeed(StreamDataFeed feed, TdsParserStateObject stateObj, int len) {
10365             TdsOutputStream output = new TdsOutputStream(this, stateObj, null);
10366             byte[] buff = new byte[constBinBufferSize];
10367             int nWritten = 0;
10368             do {
10369                 int nRead = 0;
10370                 int readSize = constBinBufferSize;
10371                 if (len > 0 && nWritten + readSize > len) {
10372                     readSize = len - nWritten;
10373                 }
10374
10375                 Debug.Assert(readSize >= 0);
10376
10377                 if (_asyncWrite) {
10378                     nRead = await feed._source.ReadAsync(buff, 0, readSize).ConfigureAwait(false);
10379                 }
10380                 else {
10381                     nRead = feed._source.Read(buff, 0, readSize);
10382                 }
10383
10384                 if (nRead == 0) {
10385                     return; 
10386                 }
10387
10388                 if (_asyncWrite) {
10389                     await output.WriteAsync(buff, 0, nRead).ConfigureAwait(false);
10390                 }
10391                 else {
10392                     output.Write(buff, 0, nRead);
10393                 }
10394
10395                 nWritten += nRead;
10396             } while (len <= 0 || nWritten < len);
10397         }
10398
10399         private Task NullIfCompletedWriteTask(Task task) {
10400             if (task == null) {
10401                 return null;
10402             }
10403             switch (task.Status) {
10404                 case TaskStatus.RanToCompletion:
10405                     return null;
10406                 case TaskStatus.Faulted:
10407                     throw task.Exception.InnerException;
10408                 case TaskStatus.Canceled:
10409                     throw SQL.OperationCancelled();
10410                 default:
10411                     return task;
10412             }
10413         }
10414
10415         private Task WriteValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj, int paramSize, bool isDataFeed) {
10416             return GetTerminationTask(WriteUnterminatedValue(value, type, scale, actualLength, encodingByteSize, offset, stateObj, paramSize, isDataFeed),
10417                 value, type, actualLength, stateObj, isDataFeed);
10418         }
10419
10420         // For MAX types, this method can only write everything in one big chunk. If multiple
10421         // chunk writes needed, please use WritePlpBytes/WritePlpChars
10422         private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int actualLength, int encodingByteSize, int offset, TdsParserStateObject stateObj, int paramSize, bool isDataFeed)
10423         {
10424             Debug.Assert((null != value) && (DBNull.Value != value), "unexpected missing or empty object");
10425
10426             // parameters are always sent over as BIG or N types
10427             switch (type.NullableType) {
10428                 case TdsEnums.SQLFLTN:
10429                     if (type.FixedLength == 4)
10430                         WriteFloat((Single)value, stateObj);
10431                     else {
10432                         Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
10433                         WriteDouble((Double)value, stateObj);
10434                     }
10435
10436                     break;
10437
10438                 case TdsEnums.SQLBIGBINARY:
10439                 case TdsEnums.SQLBIGVARBINARY:
10440                 case TdsEnums.SQLIMAGE:
10441                 case TdsEnums.SQLUDT: {
10442                     // An array should be in the object
10443                     Debug.Assert(isDataFeed || value is byte[], "Value should be an array of bytes");
10444                     Debug.Assert(!isDataFeed || value is StreamDataFeed, "Value should be a stream");
10445
10446                     if (isDataFeed) {
10447                         Debug.Assert(type.IsPlp,"Stream assigned to non-PLP was not converted!");
10448                         return NullIfCompletedWriteTask(WriteStreamFeed((StreamDataFeed)value, stateObj, paramSize));
10449                     }
10450                     else {
10451                         if (type.IsPlp) {
10452                             WriteInt(actualLength, stateObj);               // chunk length                        
10453                         }
10454
10455                         return stateObj.WriteByteArray((byte[])value, actualLength, offset, canAccumulate: false);
10456                     }                           
10457                 }
10458
10459                 case TdsEnums.SQLUNIQUEID: {
10460                         System.Guid guid = (System.Guid)value;
10461                         byte[] b = guid.ToByteArray();
10462
10463                         Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object");
10464                         stateObj.WriteByteArray(b, actualLength, 0);
10465                         break;
10466                     }
10467
10468                 case TdsEnums.SQLBITN: {
10469                         Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
10470                         if ((bool)value == true)
10471                             stateObj.WriteByte(1);
10472                         else
10473                             stateObj.WriteByte(0);
10474
10475                         break;
10476                     }
10477
10478                 case TdsEnums.SQLINTN:
10479                     if (type.FixedLength == 1)
10480                         stateObj.WriteByte((byte)value);
10481                     else if (type.FixedLength == 2)
10482                         WriteShort((Int16)value, stateObj);
10483                     else if (type.FixedLength == 4)
10484                         WriteInt((Int32)value, stateObj);
10485                     else {
10486                         Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
10487                         WriteLong((Int64)value, stateObj);
10488                     }
10489
10490                     break;
10491
10492                 case TdsEnums.SQLBIGCHAR:
10493                 case TdsEnums.SQLBIGVARCHAR:
10494                 case TdsEnums.SQLTEXT: {
10495                     Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader");
10496                     Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string");
10497
10498                     if (isDataFeed) {
10499                         Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!");
10500                         TextDataFeed tdf = value as TextDataFeed;
10501                         if (tdf == null) {
10502                             return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed)value, stateObj, needBom:true, encoding:_defaultEncoding, size:paramSize));
10503                         }
10504                         else {
10505                             return NullIfCompletedWriteTask(WriteTextFeed(tdf, _defaultEncoding, false, stateObj, paramSize));
10506                         }
10507                     }
10508                     else {
10509                         if (type.IsPlp) {
10510                             WriteInt(encodingByteSize, stateObj);               // chunk length
10511                         }
10512                         if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10513                             return stateObj.WriteByteArray((byte[])value, actualLength, 0, canAccumulate: false);
10514                         }
10515                         else {
10516                             return WriteEncodingChar((string)value, actualLength, offset, _defaultEncoding, stateObj, canAccumulate: false);
10517                         }
10518                     }
10519                 }
10520                 case TdsEnums.SQLNCHAR:
10521                 case TdsEnums.SQLNVARCHAR:
10522                 case TdsEnums.SQLNTEXT:
10523                 case TdsEnums.SQLXMLTYPE: {
10524                     Debug.Assert(!isDataFeed || (value is TextDataFeed || value is XmlDataFeed), "Value must be a TextReader or XmlReader");
10525                     Debug.Assert(isDataFeed || (value is string || value is byte[]), "Value is a byte array or string");
10526
10527                     if (isDataFeed) {
10528                         Debug.Assert(type.IsPlp, "Stream assigned to non-PLP was not converted!");
10529                         TextDataFeed tdf = value as TextDataFeed;
10530                         if (tdf == null) {
10531                             return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed)value, stateObj, IsBOMNeeded(type, value), Encoding.Unicode, paramSize));
10532                         }
10533                         else {
10534                             return NullIfCompletedWriteTask(WriteTextFeed(tdf, null, IsBOMNeeded(type, value), stateObj, paramSize));
10535                         }
10536                     }
10537                     else {
10538                         if (type.IsPlp) {
10539                             if (IsBOMNeeded(type, value)) {
10540                                 WriteInt(actualLength + 2, stateObj);               // chunk length
10541                                 WriteShort(TdsEnums.XMLUNICODEBOM, stateObj);
10542                             }
10543                             else {
10544                                 WriteInt(actualLength, stateObj);               // chunk length
10545                             }
10546                         }
10547                         if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10548                             return stateObj.WriteByteArray((byte[])value, actualLength, 0, canAccumulate: false);
10549                         }
10550                         else {
10551                             // convert to cchars instead of cbytes
10552                             actualLength >>= 1;
10553                             return WriteString((string)value, actualLength, offset, stateObj, canAccumulate: false);
10554                         }
10555                     }
10556                 }
10557                 case TdsEnums.SQLNUMERICN:
10558                     Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
10559                     WriteDecimal((Decimal)value, stateObj);
10560                     break;
10561
10562                 case TdsEnums.SQLDATETIMN:
10563                     Debug.Assert(type.FixedLength <= 0xff, "Invalid Fixed Length");
10564
10565                     TdsDateTime dt = MetaType.FromDateTime((DateTime)value, (byte)type.FixedLength);
10566
10567                     if (type.FixedLength == 4) {
10568                         if (0 > dt.days || dt.days > UInt16.MaxValue)
10569                             throw SQL.SmallDateTimeOverflow(MetaType.ToDateTime(dt.days, dt.time, 4).ToString(CultureInfo.InvariantCulture));
10570
10571                         WriteShort(dt.days, stateObj);
10572                         WriteShort(dt.time, stateObj);
10573                     }
10574                     else {
10575                         WriteInt(dt.days, stateObj);
10576                         WriteInt(dt.time, stateObj);
10577                     }
10578
10579                     break;
10580
10581                 case TdsEnums.SQLMONEYN: {
10582                         WriteCurrency((Decimal)value, type.FixedLength, stateObj);
10583                         break;
10584                     }
10585
10586                 case TdsEnums.SQLDATE: {
10587                         WriteDate((DateTime)value, stateObj);
10588                         break;
10589                     }
10590
10591                 case TdsEnums.SQLTIME:
10592                     if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
10593                         throw SQL.TimeScaleValueOutOfRange(scale);
10594                     }
10595                     WriteTime((TimeSpan)value, scale, actualLength, stateObj);
10596                     break;
10597
10598                 case TdsEnums.SQLDATETIME2:
10599                     if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
10600                         throw SQL.TimeScaleValueOutOfRange(scale);
10601                     }
10602                     WriteDateTime2((DateTime)value, scale, actualLength, stateObj);
10603                     break;
10604
10605                 case TdsEnums.SQLDATETIMEOFFSET:
10606                     WriteDateTimeOffset((DateTimeOffset)value, scale, actualLength, stateObj);
10607                     break;
10608
10609                 default:
10610                     Debug.Assert(false, "Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null));
10611                     break;
10612             } // switch
10613             // return point for accumualated writes, note: non-accumulated writes returned from their case statements
10614             return null;
10615             // Debug.WriteLine("value:  " + value.ToString(CultureInfo.InvariantCulture));
10616         }
10617
10618         /// <summary>
10619         /// Write parameter encryption metadata and returns a task if necessary.
10620         /// </summary>
10621         private Task WriteEncryptionMetadata(Task terminatedWriteTask, SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo, TdsParserStateObject stateObj) {
10622             Debug.Assert(columnEncryptionParameterInfo != null, @"columnEncryptionParameterInfo cannot be null");
10623             Debug.Assert(stateObj != null, @"stateObj cannot be null");
10624
10625             // If there is not task already, simply write the encryption metadata synchronously.
10626             if (terminatedWriteTask == null) {
10627                 WriteEncryptionMetadata(columnEncryptionParameterInfo, stateObj);
10628                 return null;
10629             }
10630             else {
10631                 // Otherwise, create a continuation task to write the encryption metadata after the previous write completes.
10632                 return AsyncHelper.CreateContinuationTask<SqlColumnEncryptionInputParameterInfo, TdsParserStateObject>(terminatedWriteTask,
10633                     WriteEncryptionMetadata, columnEncryptionParameterInfo, stateObj,
10634                     connectionToDoom: _connHandler);
10635             }
10636         }
10637
10638         /// <summary>
10639         /// Write parameter encryption metadata.
10640         /// </summary>
10641         private void WriteEncryptionMetadata(SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo, TdsParserStateObject stateObj) {
10642             Debug.Assert(columnEncryptionParameterInfo != null, @"columnEncryptionParameterInfo cannot be null");
10643             Debug.Assert(stateObj != null, @"stateObj cannot be null");
10644
10645             // Write the TypeInfo.
10646             WriteSmiTypeInfo(columnEncryptionParameterInfo.ParameterMetadata, stateObj);
10647
10648             // Write the serialized array in columnEncryptionParameterInfo.
10649             stateObj.WriteByteArray(columnEncryptionParameterInfo.SerializedWireFormat,
10650                                     columnEncryptionParameterInfo.SerializedWireFormat.Length,
10651                                     offsetBuffer: 0);
10652         }
10653
10654         // For MAX types, this method can only write everything in one big chunk. If multiple
10655         // chunk writes needed, please use WritePlpBytes/WritePlpChars
10656         private byte[] SerializeUnencryptedValue(object value, MetaType type, byte scale, int actualLength, int offset, bool isDataFeed, byte normalizationVersion, TdsParserStateObject stateObj) {
10657             Debug.Assert((null != value) && (DBNull.Value != value), "unexpected missing or empty object");
10658
10659             if (normalizationVersion != 0x01) {
10660                 throw SQL.UnsupportedNormalizationVersion(normalizationVersion);
10661             }
10662
10663             // parameters are always sent over as BIG or N types
10664             switch (type.NullableType) {
10665                 case TdsEnums.SQLFLTN:
10666                     if (type.FixedLength == 4)
10667                         return SerializeFloat((Single)value);
10668                     else {
10669                         Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
10670                         return SerializeDouble((Double)value);
10671                     }
10672
10673                 case TdsEnums.SQLBIGBINARY:
10674                 case TdsEnums.SQLBIGVARBINARY:
10675                 case TdsEnums.SQLIMAGE:
10676                 case TdsEnums.SQLUDT: {
10677                         Debug.Assert(!isDataFeed, "We cannot seriliaze streams");
10678                         Debug.Assert(value is byte[], "Value should be an array of bytes");
10679
10680                         byte[] b = new byte[actualLength];
10681                         Buffer.BlockCopy((byte[])value, offset, b, 0, actualLength);
10682                         return b;
10683                     }
10684
10685                 case TdsEnums.SQLUNIQUEID: {
10686                         System.Guid guid = (System.Guid)value;
10687                         byte[] b = guid.ToByteArray();
10688
10689                         Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object");
10690                         return b;
10691                     }
10692
10693                 case TdsEnums.SQLBITN: {
10694                         Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
10695
10696                         // We normalize to allow conversion across data types. BIT is serialized into a BIGINT.
10697                         return SerializeLong((bool)value == true ? 1 : 0, stateObj);
10698                     }
10699
10700                 case TdsEnums.SQLINTN:
10701                     if (type.FixedLength == 1)
10702                         return SerializeLong((byte)value, stateObj);
10703                     
10704                     if (type.FixedLength == 2)
10705                         return SerializeLong((Int16)value, stateObj);
10706                     
10707                     if (type.FixedLength == 4)
10708                         return SerializeLong((Int32)value, stateObj);
10709                     
10710                     Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
10711                     return SerializeLong((Int64)value, stateObj);
10712
10713                 case TdsEnums.SQLBIGCHAR:
10714                 case TdsEnums.SQLBIGVARCHAR:
10715                 case TdsEnums.SQLTEXT: {
10716                         Debug.Assert(!isDataFeed, "We cannot seriliaze streams");
10717                         Debug.Assert((value is string || value is byte[]), "Value is a byte array or string");
10718
10719                         if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10720                             byte[] b = new byte[actualLength];
10721                             Buffer.BlockCopy((byte[])value, 0, b, 0, actualLength);
10722                             return b;
10723                         }
10724                         else {
10725                             return SerializeEncodingChar((string)value, actualLength, offset, _defaultEncoding);
10726                         }
10727                     }
10728                 case TdsEnums.SQLNCHAR:
10729                 case TdsEnums.SQLNVARCHAR:
10730                 case TdsEnums.SQLNTEXT:
10731                 case TdsEnums.SQLXMLTYPE: {
10732                         Debug.Assert(!isDataFeed, "We cannot seriliaze streams");
10733                         Debug.Assert((value is string || value is byte[]), "Value is a byte array or string");
10734
10735                         if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10736                             byte[] b = new byte[actualLength];
10737                             Buffer.BlockCopy((byte[])value, 0, b, 0, actualLength);
10738                             return b;
10739                         }
10740                         else { // convert to cchars instead of cbytes
10741                             actualLength >>= 1;
10742                             return SerializeString((string)value, actualLength, offset);
10743                         }
10744                     }
10745                 case TdsEnums.SQLNUMERICN:
10746                     Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
10747                     return SerializeDecimal((Decimal)value, stateObj);
10748
10749                 case TdsEnums.SQLDATETIMN:
10750                     Debug.Assert(type.FixedLength <= 0xff, "Invalid Fixed Length");
10751
10752                     TdsDateTime dt = MetaType.FromDateTime((DateTime)value, (byte)type.FixedLength);
10753
10754                     if (type.FixedLength == 4) {
10755                         if (0 > dt.days || dt.days > UInt16.MaxValue)
10756                             throw SQL.SmallDateTimeOverflow(MetaType.ToDateTime(dt.days, dt.time, 4).ToString(CultureInfo.InvariantCulture));
10757
10758                         if (null == stateObj._bIntBytes) {
10759                             stateObj._bIntBytes = new byte[4];
10760                         }
10761
10762                         byte[] b = stateObj._bIntBytes;
10763                         int current = 0;
10764
10765                         byte[] bPart = SerializeShort(dt.days, stateObj);
10766                         Buffer.BlockCopy(bPart, 0, b, current, 2);
10767                         current += 2;
10768
10769                         bPart = SerializeShort(dt.time, stateObj);
10770                         Buffer.BlockCopy(bPart, 0, b, current, 2);
10771
10772                         return b;
10773                     }
10774                     else {
10775                         if (null == stateObj._bLongBytes) {
10776                             stateObj._bLongBytes = new byte[8];
10777                         }
10778                         byte[] b = stateObj._bLongBytes;
10779                         int current = 0;
10780
10781                         byte[] bPart = SerializeInt(dt.days, stateObj);
10782                         Buffer.BlockCopy(bPart, 0, b, current, 4);
10783                         current += 4;
10784
10785                         bPart = SerializeInt(dt.time, stateObj);
10786                         Buffer.BlockCopy(bPart, 0, b, current, 4);
10787
10788                         return b;
10789                     }
10790
10791                 case TdsEnums.SQLMONEYN: {
10792                         return SerializeCurrency((Decimal)value, type.FixedLength, stateObj);
10793                     }
10794
10795                 case TdsEnums.SQLDATE: {
10796                         return SerializeDate((DateTime)value);
10797                     }
10798
10799                 case TdsEnums.SQLTIME:
10800                     if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
10801                         throw SQL.TimeScaleValueOutOfRange(scale);
10802                     }
10803                     return SerializeTime((TimeSpan)value, scale, actualLength);
10804
10805                 case TdsEnums.SQLDATETIME2:
10806                     if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
10807                         throw SQL.TimeScaleValueOutOfRange(scale);
10808                     }
10809                     return SerializeDateTime2((DateTime)value, scale, actualLength);
10810
10811                 case TdsEnums.SQLDATETIMEOFFSET:
10812                     if (scale > TdsEnums.DEFAULT_VARTIME_SCALE) {
10813                         throw SQL.TimeScaleValueOutOfRange(scale);
10814                     }
10815                     return SerializeDateTimeOffset((DateTimeOffset)value, scale, actualLength);
10816
10817                 default:
10818                     throw SQL.UnsupportedDatatypeEncryption(type.TypeName);
10819             } // switch
10820             // Debug.WriteLine("value:  " + value.ToString(CultureInfo.InvariantCulture));
10821         }
10822
10823         // For MAX types, this method can only write everything in one big chunk. If multiple
10824         // chunk writes needed, please use WritePlpBytes/WritePlpChars
10825         private byte[] SerializeUnencryptedSqlValue(object value, MetaType type, int actualLength, int offset, byte normalizationVersion, TdsParserStateObject stateObj) {
10826             Debug.Assert(((type.NullableType == TdsEnums.SQLXMLTYPE) ||
10827                    (value is INullable && !((INullable)value).IsNull)),
10828                    "unexpected null SqlType!");
10829
10830             if (normalizationVersion != 0x01) {
10831                 throw SQL.UnsupportedNormalizationVersion(normalizationVersion);
10832             }
10833
10834             // parameters are always sent over as BIG or N types
10835             switch (type.NullableType) {
10836                 case TdsEnums.SQLFLTN:
10837                     if (type.FixedLength == 4)
10838                         return SerializeFloat(((SqlSingle)value).Value);
10839                     else {
10840                         Debug.Assert(type.FixedLength == 8, "Invalid length for SqlDouble type!");
10841                         return SerializeDouble(((SqlDouble)value).Value);
10842                     }
10843
10844                 case TdsEnums.SQLBIGBINARY:
10845                 case TdsEnums.SQLBIGVARBINARY:
10846                 case TdsEnums.SQLIMAGE: {
10847                         byte[] b = new byte[actualLength];
10848
10849                         if (value is SqlBinary) {
10850                             Buffer.BlockCopy(((SqlBinary)value).Value, offset, b, 0, actualLength);
10851                         }
10852                         else {
10853                             Debug.Assert(value is SqlBytes);
10854                             Buffer.BlockCopy(((SqlBytes)value).Value, offset, b, 0, actualLength);
10855                         }
10856                         return b;
10857                     }
10858
10859                 case TdsEnums.SQLUNIQUEID: {
10860                         byte[] b = ((SqlGuid)value).ToByteArray();
10861
10862                         Debug.Assert((actualLength == b.Length) && (actualLength == 16), "Invalid length for guid type in com+ object");
10863                         return b;
10864                     }
10865
10866                 case TdsEnums.SQLBITN: {
10867                         Debug.Assert(type.FixedLength == 1, "Invalid length for SqlBoolean type");
10868
10869                         // We normalize to allow conversion across data types. BIT is serialized into a BIGINT.
10870                         return SerializeLong(((SqlBoolean)value).Value == true ? 1 : 0, stateObj);
10871                     }
10872
10873                 case TdsEnums.SQLINTN:
10874                     // We normalize to allow conversion across data types. All data types below are serialized into a BIGINT.
10875                     if (type.FixedLength == 1)
10876                         return SerializeLong(((SqlByte)value).Value, stateObj);
10877                     
10878                     if (type.FixedLength == 2)
10879                         return SerializeLong(((SqlInt16)value).Value, stateObj);
10880                     
10881                     if (type.FixedLength == 4)
10882                         return SerializeLong(((SqlInt32)value).Value, stateObj);
10883                     else {
10884                         Debug.Assert(type.FixedLength == 8, "invalid length for SqlIntN type:  " + type.FixedLength.ToString(CultureInfo.InvariantCulture));
10885                         return SerializeLong(((SqlInt64)value).Value, stateObj);
10886                     }
10887
10888                 case TdsEnums.SQLBIGCHAR:
10889                 case TdsEnums.SQLBIGVARCHAR:
10890                 case TdsEnums.SQLTEXT:
10891                     if (value is SqlChars) {
10892                         String sch = new String(((SqlChars)value).Value);
10893                         return SerializeEncodingChar(sch, actualLength, offset, _defaultEncoding);
10894                     }
10895                     else {
10896                         Debug.Assert(value is SqlString);
10897                         return SerializeEncodingChar(((SqlString)value).Value, actualLength, offset, _defaultEncoding);
10898                     }
10899
10900
10901                 case TdsEnums.SQLNCHAR:
10902                 case TdsEnums.SQLNVARCHAR:
10903                 case TdsEnums.SQLNTEXT:
10904                 case TdsEnums.SQLXMLTYPE:
10905                     // convert to cchars instead of cbytes
10906                     // Xml type is already converted to string through GetCoercedValue
10907                     if (actualLength != 0)
10908                         actualLength >>= 1;
10909
10910                     if (value is SqlChars) {
10911                         return SerializeCharArray(((SqlChars)value).Value, actualLength, offset);
10912                     }
10913                     else {
10914                         Debug.Assert(value is SqlString);
10915                         return SerializeString(((SqlString)value).Value, actualLength, offset);
10916                     }
10917
10918                 case TdsEnums.SQLNUMERICN:
10919                     Debug.Assert(type.FixedLength <= 17, "Decimal length cannot be greater than 17 bytes");
10920                     return SerializeSqlDecimal((SqlDecimal)value, stateObj);
10921
10922                 case TdsEnums.SQLDATETIMN:
10923                     SqlDateTime dt = (SqlDateTime)value;
10924
10925                     if (type.FixedLength == 4) {
10926                         if (0 > dt.DayTicks || dt.DayTicks > UInt16.MaxValue)
10927                             throw SQL.SmallDateTimeOverflow(dt.ToString());
10928
10929                         if (null == stateObj._bIntBytes) {
10930                             stateObj._bIntBytes = new byte[4];
10931                         }
10932
10933                         byte[] b = stateObj._bIntBytes;
10934                         int current = 0;
10935
10936                         byte[] bPart = SerializeShort(dt.DayTicks, stateObj);
10937                         Buffer.BlockCopy(bPart, 0, b, current, 2);
10938                         current += 2;
10939
10940                         bPart = SerializeShort(dt.TimeTicks / SqlDateTime.SQLTicksPerMinute, stateObj);
10941                         Buffer.BlockCopy(bPart, 0, b, current, 2);
10942
10943                         return b;
10944                     }
10945                     else {
10946                         if (null == stateObj._bLongBytes) {
10947                             stateObj._bLongBytes = new byte[8];
10948                         }
10949
10950                         byte[] b = stateObj._bLongBytes;
10951                         int current = 0;
10952
10953                         byte[] bPart = SerializeInt(dt.DayTicks, stateObj);
10954                         Buffer.BlockCopy(bPart, 0, b, current, 4);
10955                         current += 4;
10956
10957                         bPart = SerializeInt(dt.TimeTicks, stateObj);
10958                         Buffer.BlockCopy(bPart, 0, b, current, 4);
10959
10960                         return b;
10961                     }
10962
10963                 case TdsEnums.SQLMONEYN: {
10964                         return SerializeSqlMoney((SqlMoney)value, type.FixedLength, stateObj);
10965                     }
10966
10967                 default:
10968                     throw SQL.UnsupportedDatatypeEncryption(type.TypeName);
10969             } // switch
10970         }
10971
10972         //
10973         // we always send over nullable types for parameters so we always write the varlen fields
10974         //
10975
10976         internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsParserStateObject stateObj, bool unknownLength=false) {
10977             if (type.IsLong) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths
10978                 if (isNull) {
10979                     if (type.IsPlp) {
10980                         WriteLong(unchecked((long)TdsEnums.SQL_PLP_NULL), stateObj);
10981                     }
10982                     else {
10983                         WriteInt(unchecked((int)TdsEnums.VARLONGNULL), stateObj);
10984                     }
10985                 }
10986                 else if (type.NullableType == TdsEnums.SQLXMLTYPE || unknownLength) {
10987                     WriteUnsignedLong(TdsEnums.SQL_PLP_UNKNOWNLEN, stateObj);
10988                 }
10989                 else if (type.IsPlp) {
10990                     // Non-xml plp types
10991                     WriteLong((long)size, stateObj);
10992                 }
10993                 else {
10994                     WriteInt(size, stateObj);
10995                 }
10996             }
10997             else if (type.IsVarTime) {
10998                 if (isNull) {
10999                     stateObj.WriteByte(TdsEnums.FIXEDNULL);
11000                 }
11001                 else {
11002                     stateObj.WriteByte((byte)size);
11003                 }
11004             }
11005             else if (false == type.IsFixed) { // non-long but variable length column, must be a BIG* type: 2 byte length
11006                 if (isNull) {
11007                     WriteShort(TdsEnums.VARNULL, stateObj);
11008                 }
11009                 else {
11010                     WriteShort(size, stateObj);
11011                 }
11012             }
11013             else {
11014                 if (isNull) {
11015                     stateObj.WriteByte(TdsEnums.FIXEDNULL);
11016                 }
11017                 else {
11018                     Debug.Assert(type.FixedLength <= 0xff, "WriteParameterVarLen: invalid one byte length!");
11019                     stateObj.WriteByte((byte)(type.FixedLength & 0xff)); // 1 byte for everything else
11020                 }
11021             }
11022         }
11023
11024         // Reads the next chunk in a nvarchar(max) data stream.
11025         // This call must be preceeded by a call to ReadPlpLength or ReadDataLength.
11026         // Will not start reading into the next chunk if bytes requested is larger than
11027         // the current chunk length. Do another ReadPlpLength, ReadPlpUnicodeChars in that case.
11028         // Returns the actual chars read
11029         private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsParserStateObject stateObj, out int charsRead) {
11030
11031             Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
11032             Debug.Assert((stateObj._longlen != 0) && (stateObj._longlen != TdsEnums.SQL_PLP_NULL),
11033                         "Out of sync plp read request");
11034             if (stateObj._longlenleft == 0) {
11035                 Debug.Assert(false, "Out of sync read request");
11036                 charsRead = 0;
11037                 return true;
11038             }
11039
11040             charsRead = len;
11041
11042             // stateObj._longlenleft is in bytes
11043             if ((stateObj._longlenleft >> 1) < (ulong)len)
11044                 charsRead = (int)(stateObj._longlenleft >> 1);
11045
11046             for (int ii = 0; ii < charsRead; ii++) {
11047                 if (!stateObj.TryReadChar(out buff[offst + ii])) {
11048                     return false;
11049                 }
11050             }
11051
11052             stateObj._longlenleft -= ((ulong)charsRead << 1);
11053             return true;
11054         }
11055
11056         internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj) {
11057             int charsRead;
11058             Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
11059             bool result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead);
11060             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
11061             return charsRead;
11062         }
11063
11064         // Reads the requested number of chars from a plp data stream, or the entire data if
11065         // requested length is -1 or larger than the actual length of data. First call to this method
11066         //  should be preceeded by a call to ReadPlpLength or ReadDataLength.
11067         // Returns the actual chars read.
11068         internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead) {
11069             int charsRead = 0;
11070             int charsLeft = 0;
11071             char[] newbuf;
11072
11073             if (stateObj._longlen == 0) {
11074                 Debug.Assert(stateObj._longlenleft == 0);
11075                 totalCharsRead = 0;
11076                 return true;       // No data
11077             }
11078
11079             Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
11080                     "Out of sync plp read request");
11081
11082             Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
11083             charsLeft = len;
11084
11085             // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time
11086             if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) {
11087                 buff = new char[(int)Math.Min((int)stateObj._longlen, len)];
11088             }
11089
11090             if (stateObj._longlenleft == 0) {
11091                 ulong ignored;
11092                 if (!stateObj.TryReadPlpLength(false, out ignored)) {
11093                     totalCharsRead = 0;
11094                     return false;
11095                 }
11096                 if (stateObj._longlenleft == 0) { // Data read complete
11097                     totalCharsRead = 0;
11098                     return true;
11099                 }
11100             }
11101
11102             totalCharsRead = 0;
11103
11104             while (charsLeft > 0) {
11105                 charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
11106                 if ((buff == null) || (buff.Length < (offst + charsRead))) {
11107                     // Grow the array
11108                     newbuf = new char[offst + charsRead];
11109                     if (buff != null) {
11110                         Buffer.BlockCopy(buff, 0, newbuf, 0, offst*2);
11111                     }
11112                     buff = newbuf;
11113                 }
11114                 if (charsRead > 0) {
11115                     if (!TryReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj, out charsRead)) {
11116                         return false;
11117                     }
11118                     charsLeft -= charsRead;
11119                     offst += charsRead;
11120                     totalCharsRead += charsRead;
11121                 }
11122                 // Special case single byte left
11123                 if (stateObj._longlenleft == 1 && (charsLeft > 0)) {
11124                     byte b1;
11125                     if (!stateObj.TryReadByte(out b1)) {
11126                         return false;
11127                     }
11128                     stateObj._longlenleft--;
11129                     ulong ignored;
11130                     if (!stateObj.TryReadPlpLength(false, out ignored)) {
11131                         return false;
11132                     }
11133                     Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
11134                     byte b2;
11135                     if (!stateObj.TryReadByte(out b2)) {
11136                         return false;
11137                     }
11138                     stateObj._longlenleft--;
11139                     // Put it at the end of the array. At this point we know we have an extra byte.
11140                     buff[offst] = (char)(((b2 & 0xff) << 8) + (b1 & 0xff));
11141                     offst = checked((int)offst + 1);
11142                     charsRead++;
11143                     charsLeft--;
11144                     totalCharsRead++;
11145                 }
11146                 if (stateObj._longlenleft == 0) { // Read the next chunk or cleanup state if hit the end
11147                     ulong ignored;
11148                     if (!stateObj.TryReadPlpLength(false, out ignored)) {
11149                         return false;
11150                     }
11151                 }
11152
11153                 if (stateObj._longlenleft == 0)   // Data read complete
11154                     break;
11155             }
11156             return true;
11157         }
11158
11159         internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj) {
11160             int charsRead = 0;
11161             int charsLeft = 0;
11162             int bytesRead = 0;
11163             int totalcharsRead = 0;
11164
11165             if (stateObj._longlen == 0) {
11166                 Debug.Assert(stateObj._longlenleft == 0);
11167                 return 0;       // No data
11168             }
11169
11170             Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
11171                     "Out of sync plp read request");
11172
11173             Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpAnsiChars()!");
11174             charsLeft = len;
11175
11176             if (stateObj._longlenleft == 0) {
11177                 stateObj.ReadPlpLength(false);
11178                 if (stateObj._longlenleft == 0)  {// Data read complete
11179                     stateObj._plpdecoder = null;
11180                     return 0;
11181                 }
11182             }
11183
11184             if (stateObj._plpdecoder == null) {
11185                 Encoding enc = metadata.encoding;
11186
11187                 if (enc == null)
11188                 {
11189                     if (null == _defaultEncoding) {
11190                         ThrowUnsupportedCollationEncountered(stateObj);
11191
11192                     }
11193
11194                     enc = _defaultEncoding;
11195                 }
11196                 stateObj._plpdecoder = enc.GetDecoder();
11197             }
11198
11199             while (charsLeft > 0) {
11200                 bytesRead = (int)Math.Min(stateObj._longlenleft, (ulong)charsLeft);
11201                 if ((stateObj._bTmp == null) || (stateObj._bTmp.Length < bytesRead)) {
11202                     // Grow the array
11203                     stateObj._bTmp = new byte[bytesRead];
11204                 }
11205
11206                 bytesRead = stateObj.ReadPlpBytesChunk(stateObj._bTmp, 0, bytesRead);
11207
11208                 charsRead = stateObj._plpdecoder.GetChars(stateObj._bTmp, 0, bytesRead, buff, offst);
11209                 charsLeft -= charsRead;
11210                 offst += charsRead;
11211                 totalcharsRead += charsRead;
11212                 if (stateObj._longlenleft == 0)  // Read the next chunk or cleanup state if hit the end
11213                     stateObj.ReadPlpLength(false);
11214
11215                 if (stateObj._longlenleft == 0)  { // Data read complete
11216                     stateObj._plpdecoder = null;
11217                     break;
11218                 }
11219             }
11220             return (totalcharsRead);
11221         }
11222
11223         // ensure value is not null and does not have an NBC bit set for it before using this method
11224         internal ulong SkipPlpValue(ulong cb, TdsParserStateObject stateObj) {
11225             ulong skipped;
11226             Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
11227             bool result = TrySkipPlpValue(cb, stateObj, out skipped);
11228             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
11229             return skipped;
11230         }
11231
11232         internal bool TrySkipPlpValue(ulong cb, TdsParserStateObject stateObj, out ulong totalBytesSkipped) {
11233             // Read and skip cb bytes or until  ReadPlpLength returns 0.
11234             int bytesSkipped;
11235             totalBytesSkipped = 0;
11236
11237             if (stateObj._longlenleft == 0) {
11238                 ulong ignored;
11239                 if (!stateObj.TryReadPlpLength(false, out ignored)) {
11240                     return false;
11241                 }
11242             }
11243
11244             while ((totalBytesSkipped < cb) &&
11245                     (stateObj._longlenleft > 0)) {
11246                 if (stateObj._longlenleft > Int32.MaxValue)
11247                     bytesSkipped = Int32.MaxValue;
11248                 else
11249                     bytesSkipped = (int)stateObj._longlenleft;
11250                 bytesSkipped = ((cb - totalBytesSkipped) < (ulong) bytesSkipped) ? (int)(cb - totalBytesSkipped) : bytesSkipped;
11251
11252                 if (!stateObj.TrySkipBytes(bytesSkipped)) {
11253                     return false;
11254                 }
11255                 stateObj._longlenleft -= (ulong)bytesSkipped;
11256                 totalBytesSkipped += (ulong)bytesSkipped;
11257
11258                 if (stateObj._longlenleft == 0) {
11259                     ulong ignored;
11260                     if (!stateObj.TryReadPlpLength(false, out ignored)) {
11261                         return false;
11262                     }
11263                 }
11264             }
11265
11266             return true;
11267         }
11268
11269         internal ulong PlpBytesLeft(TdsParserStateObject stateObj) {
11270             if ((stateObj._longlen != 0) && (stateObj._longlenleft == 0))
11271                 stateObj.ReadPlpLength(false);
11272
11273             return stateObj._longlenleft;
11274         }
11275
11276         internal bool TryPlpBytesLeft(TdsParserStateObject stateObj, out ulong left) {
11277             if ((stateObj._longlen != 0) && (stateObj._longlenleft == 0)) {
11278                 if (!stateObj.TryReadPlpLength(false, out left)) {
11279                     return false;
11280                 }
11281             }
11282
11283             left = stateObj._longlenleft;
11284             return true;
11285         }
11286
11287         private const ulong _indeterminateSize = 0xffffffffffffffff;        // Represents unknown size
11288
11289         internal ulong PlpBytesTotalLength(TdsParserStateObject stateObj) {
11290             if (stateObj._longlen == TdsEnums.SQL_PLP_UNKNOWNLEN)
11291                 return _indeterminateSize;
11292             else if (stateObj._longlen == TdsEnums.SQL_PLP_NULL)
11293                 return 0;
11294
11295             return stateObj._longlen;
11296         }
11297
11298         const string StateTraceFormatString = "\n\t"
11299                                         + "         _physicalStateObj = {0}\n\t"
11300                                         + "         _pMarsPhysicalConObj = {1}\n\t"
11301                                         + "         _state = {2}\n\t"
11302                                         + "         _server = {3}\n\t"
11303                                         + "         _fResetConnection = {4}\n\t"
11304                                         + "         _defaultCollation = {5}\n\t"
11305                                         + "         _defaultCodePage = {6}\n\t"
11306                                         + "         _defaultLCID = {7}\n\t"
11307                                         + "         _defaultEncoding = {8}\n\t"
11308                                         + "         _encryptionOption = {10}\n\t"
11309                                         + "         _currentTransaction = {11}\n\t"
11310                                         + "         _pendingTransaction = {12}\n\t"
11311                                         + "         _retainedTransactionId = {13}\n\t"
11312                                         + "         _nonTransactedOpenResultCount = {14}\n\t"
11313                                         + "         _connHandler = {15}\n\t"
11314                                         + "         _fMARS = {16}\n\t"
11315                                         + "         _sessionPool = {17}\n\t"
11316                                         + "         _isShiloh = {18}\n\t"
11317                                         + "         _isShilohSP1 = {19}\n\t"
11318                                         + "         _isYukon = {20}\n\t"
11319                                         + "         _sniSpnBuffer = {21}\n\t"
11320                                         + "         _errors = {22}\n\t"
11321                                         + "         _warnings = {23}\n\t"
11322                                         + "         _attentionErrors = {24}\n\t"
11323                                         + "         _attentionWarnings = {25}\n\t"
11324                                         + "         _statistics = {26}\n\t"
11325                                         + "         _statisticsIsInTransaction = {27}\n\t"
11326                                         + "         _fPreserveTransaction = {28}"
11327                                         + "         _fParallel = {29}"
11328                                         ;
11329         internal string TraceString() {
11330             return String.Format(/*IFormatProvider*/ null,
11331                             StateTraceFormatString,
11332                             null == _physicalStateObj,
11333                             null == _pMarsPhysicalConObj,
11334                             _state,
11335                             _server,
11336                             _fResetConnection,
11337                             null == _defaultCollation ? "(null)" : _defaultCollation.TraceString(),
11338                             _defaultCodePage,
11339                             _defaultLCID,
11340                             TraceObjectClass(_defaultEncoding),
11341                             "",
11342                             _encryptionOption,
11343                             null == _currentTransaction ? "(null)" : _currentTransaction.TraceString(),
11344                             null == _pendingTransaction ? "(null)" : _pendingTransaction.TraceString(),
11345                             _retainedTransactionId,
11346                             _nonTransactedOpenResultCount,
11347                             null == _connHandler ? "(null)" : _connHandler.ObjectID.ToString((IFormatProvider)null),
11348                             _fMARS,
11349                             null == _sessionPool ? "(null)" : _sessionPool.TraceString(),
11350                             _isShiloh,
11351                             _isShilohSP1,
11352                             _isYukon,
11353                             null == _sniSpnBuffer ? "(null)" : _sniSpnBuffer.Length.ToString((IFormatProvider)null),
11354                             _physicalStateObj != null ? "(null)" : _physicalStateObj.ErrorCount.ToString((IFormatProvider)null),
11355                             _physicalStateObj != null ? "(null)" : _physicalStateObj.WarningCount.ToString((IFormatProvider)null),
11356                             _physicalStateObj != null ? "(null)" : _physicalStateObj.PreAttentionErrorCount.ToString((IFormatProvider)null),
11357                             _physicalStateObj != null ? "(null)" : _physicalStateObj.PreAttentionWarningCount.ToString((IFormatProvider)null),
11358                             null == _statistics,
11359                             _statisticsIsInTransaction,
11360                             _fPreserveTransaction,
11361                             null == _connHandler ? "(null)" : _connHandler.ConnectionOptions.MultiSubnetFailover.ToString((IFormatProvider)null),
11362                             null == _connHandler ? "(null)" : _connHandler.ConnectionOptions.TransparentNetworkIPResolution.ToString((IFormatProvider)null));
11363         }
11364
11365         private string TraceObjectClass(object instance) {
11366             if (null == instance) {
11367                 return "(null)";
11368             }
11369             else {
11370                 return instance.GetType().ToString();
11371             }
11372         }
11373     }    // tdsparser
11374 }//namespace