1 //------------------------------------------------------------------------------
2 // <copyright file="TdsParser.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System
.Data
.SqlClient
{
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
;
19 using System
.Runtime
.CompilerServices
;
20 using System
.Runtime
.InteropServices
;
22 using System
.Threading
;
23 using System
.Threading
.Tasks
;
26 using MSS
= Microsoft
.SqlServer
.Server
;
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
);
34 static Task completedTask
;
35 static Task CompletedTask
{
37 if (completedTask
== null) {
38 completedTask
= Task
.FromResult
<object>(null);
44 internal int ObjectID
{
51 // ReliabilitySection Usage:
54 // TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
56 // RuntimeHelpers.PrepareConstrainedRegions();
58 // tdsReliabilitySection.Start();
63 // // code that requires reliability
68 // tdsReliabilitySection.Stop();
72 internal struct ReliabilitySection
{
74 // do not allocate TLS data in RETAIL bits
76 private static int s_reliabilityCount
; // initialized to 0 by CLR
78 private bool m_started
; // initialized to false (not started) by CLR
81 [Conditional("DEBUG")]
82 internal void Start() {
84 Debug
.Assert(!m_started
);
86 RuntimeHelpers
.PrepareConstrainedRegions();
96 [Conditional("DEBUG")]
97 internal void Stop() {
99 // cannot assert m_started - ThreadAbortException can be raised before Start is called
102 Debug
.Assert(s_reliabilityCount
> 0);
104 RuntimeHelpers
.PrepareConstrainedRegions();
108 --s_reliabilityCount
;
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
) {
119 Debug
.Assert(s_reliabilityCount
> 0, message
);
124 // Default state object for parser
125 internal TdsParserStateObject _physicalStateObj
= null; // Default stateObj and connection for Dbnetlib and non-MARS SNI.
127 // Also, default logical stateObj and connection for MARS over SNI.
128 internal TdsParserStateObject _pMarsPhysicalConObj
= null; // With MARS enabled, cached physical stateObj and connection.
130 // Must keep this around - especially for callbacks on pre-MARS
131 // ReadAsync which will return if physical connection broken!
133 // Per Instance TDS Parser variables
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
141 internal TdsParserState _state
= TdsParserState
.Closed
; // status flag for connection
143 private string _server
= ""; // name of server that the parser connects to
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
148 private SqlCollation _defaultCollation
; // default collation from the server
150 private int _defaultCodePage
;
152 private int _defaultLCID
;
154 internal Encoding _defaultEncoding
= null; // for sql character data
156 private static EncryptionOptions _sniSupportedEncryptionOption
= SNILoadHandle
.SingletonInstance
.Options
;
158 private EncryptionOptions _encryptionOption
= _sniSupportedEncryptionOption
;
160 private SqlInternalTransaction _currentTransaction
;
161 private SqlInternalTransaction _pendingTransaction
; // pending transaction for Yukon and beyond.
163 // need to hold on to the transaction id if distributed transaction merely rolls back without defecting.
164 private long _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
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;
170 // Connection reference
171 private SqlInternalConnectionTds _connHandler
;
173 // Async/Mars variables
174 private bool _fMARS
= false;
176 internal bool _loginWithFailover
= false; // set to true while connect in failover mode so parser state object can adjust its logic
178 internal AutoResetEvent _resetConnectionEvent
= null; // Used to serialize executes and call reset on first execute only.
180 internal TdsParserSessionPool _sessionPool
= null; // initialized only when we're a MARS parser.
183 private bool _isShiloh
= false; // set to true if we connect to a 8.0 server (SQL 2000) or later
185 private bool _isShilohSP1
= false; // set to true if speaking to Shiloh SP1 or later
187 private bool _isYukon
= false; // set to true if speaking to Yukon or later
189 private bool _isKatmai
= false;
191 private bool _isDenali
= false;
193 private byte[] _sniSpnBuffer
= null;
198 private SqlStatistics _statistics
= null;
200 private bool _statisticsIsInTransaction
= false;
203 // STATIC TDS Parser variables
206 // NIC address caching
207 private static byte[] s_nicAddress
; // cache the NIC address from the registry
210 private static bool s_fSSPILoaded
= false; // bool to indicate whether library has been loaded
212 private volatile static UInt32 s_maxSSPILength
= 0; // variable to hold max SSPI data size, keep for token from server
215 private static bool s_fADALLoaded
= false; // bool to indicate whether ADAL library has been loaded
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 }
;
220 private static object s_tdsParserLock
= new object();
222 // Various other statics
223 private const int ATTENTION_TIMEOUT
= 5000; // internal attention timeout, in ticks
225 // XML metadata substitue sequence
226 private static readonly byte[] s_xmlMetadataSubstituteSequence
= { 0xe7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
;
228 // size of Guid (e.g. _clientConnectionId, ActivityId.Id)
229 private const int GUID_SIZE
= 16;
231 // NOTE: You must take the internal connection's _parserLock before modifying this
232 internal bool _asyncWrite
= false;
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;
239 /// Get or set if column encryption is supported by the server.
241 internal bool IsColumnEncryptionSupported
{
243 return _serverSupportsColumnEncryption
;
246 _serverSupportsColumnEncryption
= value;
250 internal TdsParser(bool MARS
, bool fAsynchronous
) {
251 _fMARS
= MARS
; // may change during Connect to pre Yukon servers
252 _physicalStateObj
= new TdsParserStateObject(this);
255 internal SqlInternalConnectionTds Connection
{
261 internal SqlInternalTransaction CurrentTransaction
{
263 return _currentTransaction
;
266 Debug
.Assert(value == _currentTransaction
267 || null == _currentTransaction
269 || (null != _currentTransaction
&& !_currentTransaction
.IsLocal
), "attempting to change current transaction?");
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;
282 internal int DefaultLCID
{
288 internal EncryptionOptions EncryptionOptions
{
290 return _encryptionOption
;
293 _encryptionOption
= value;
297 internal bool IsYukonOrNewer
{
303 internal bool IsKatmaiOrNewer
{
309 internal bool MARSOn
{
315 internal SqlInternalTransaction PendingTransaction
{
317 return _pendingTransaction
;
320 Debug
.Assert (null != value, "setting a non-null PendingTransaction?");
321 _pendingTransaction
= value;
325 internal string Server
{
331 internal TdsParserState State
{
340 internal SqlStatistics Statistics
{
349 private bool IncludeTraceHeader
{
351 return (_isDenali
&& Bid
.TraceOn
&& Bid
.IsOn(ActivityCorrelator
.CorrelationTracePoints
));
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
);
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");
374 internal void ProcessPendingAck(TdsParserStateObject stateObj
) {
375 if (stateObj
._attentionSent
) {
376 ProcessAttention(stateObj
);
380 internal void Connect(ServerInfo serverInfo
,
381 SqlInternalConnectionTds connHandler
,
382 bool ignoreSniOpenTimeout
,
385 bool trustServerCert
,
386 bool integratedSecurity
,
388 bool isFirstTransparentAttempt
,
389 SqlAuthenticationMethod authType
,
391 if (_state
!= TdsParserState
.Closed
) {
392 Debug
.Assert(false, "TdsParser.Connect called on non-closed connection!");
396 _connHandler
= connHandler
;
397 _loginWithFailover
= withFailover
;
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?");
407 //Create LocalDB instance if necessary
408 if (connHandler
.ConnectionOptions
.LocalDBInstance
!= null)
409 LocalDBAPI
.CreateLocalDBInstance(connHandler
.ConnectionOptions
.LocalDBInstance
);
411 if (integratedSecurity
|| authType
== SqlAuthenticationMethod
.ActiveDirectoryIntegrated
) {
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");
418 _sniSpnBuffer
= null;
419 if (authType
== SqlAuthenticationMethod
.ActiveDirectoryPassword
) {
420 Bid
.Trace("<sc.TdsParser.Connect|SEC> Active Directory Password authentication\n");
422 else if (authType
== SqlAuthenticationMethod
.SqlPassword
) {
423 Bid
.Trace("<sc.TdsParser.Connect|SEC> SQL Password authentication\n");
426 Bid
.Trace("<sc.TdsParser.Connect|SEC> SQL authentication\n");
430 byte[] instanceName
= null;
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
);
436 bool fParallel
= _connHandler
.ConnectionOptions
.MultiSubnetFailover
;
438 TransparentNetworkResolutionState transparentNetworkResolutionState
;
439 if (_connHandler
.ConnectionOptions
.TransparentNetworkIPResolution
&& !disableTnir
)
441 if(isFirstTransparentAttempt
)
442 transparentNetworkResolutionState
= TransparentNetworkResolutionState
.SequentialMode
;
444 transparentNetworkResolutionState
= TransparentNetworkResolutionState
.ParallelMode
;
447 transparentNetworkResolutionState
= TransparentNetworkResolutionState
.DisabledMode
;
449 int totalTimeout
= _connHandler
.ConnectionOptions
.ConnectTimeout
;
451 _physicalStateObj
.CreatePhysicalSNIHandle(serverInfo
.ExtendedServerName
, ignoreSniOpenTimeout
, timerExpire
,
452 out instanceName
, _sniSpnBuffer
, false, true, fParallel
, transparentNetworkResolutionState
, totalTimeout
);
454 if (TdsEnums
.SNI_SUCCESS
!= _physicalStateObj
.Status
) {
455 _physicalStateObj
.AddError(ProcessSNIError(_physicalStateObj
));
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?");
467 _server
= serverInfo
.ResolvedServerName
;
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
);
478 _state
= TdsParserState
.OpenNotLoggedIn
;
479 _physicalStateObj
.SniContext
= SniContext
.Snix_PreLoginBeforeSuccessfullWrite
; // SQL BU DT 376766
480 _physicalStateObj
.TimeoutTime
= timerExpire
;
482 bool marsCapable
= false;
484 _connHandler
.TimeoutErrorInternal
.EndPhase(SqlConnectionTimeoutErrorPhase
.InitializeConnection
);
485 _connHandler
.TimeoutErrorInternal
.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase
.SendPreLoginHandshake
);
487 UInt32 result
= SNINativeMethodWrapper
.SniGetConnectionId(_physicalStateObj
.Handle
, ref _connHandler
._clientConnectionId
);
488 Debug
.Assert(result
== TdsEnums
.SNI_SUCCESS
, "Unexpected failure state upon calling SniGetConnectionId");
491 Bid
.Trace("<sc.TdsParser.Connect|SEC> Sending prelogin handshake\n");
492 SendPreLoginHandshake(instanceName
, encrypt
);
494 _connHandler
.TimeoutErrorInternal
.EndPhase(SqlConnectionTimeoutErrorPhase
.SendPreLoginHandshake
);
495 _connHandler
.TimeoutErrorInternal
.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase
.ConsumePreLoginHandshake
);
497 _physicalStateObj
.SniContext
= SniContext
.Snix_PreLogin
;
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
);
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
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
);
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
);
517 UInt32 retCode
= SNINativeMethodWrapper
.SniGetConnectionId(_physicalStateObj
.Handle
, ref _connHandler
._clientConnectionId
);
518 Debug
.Assert(retCode
== TdsEnums
.SNI_SUCCESS
, "Unexpected failure state upon calling SniGetConnectionId");
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
);
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();
532 Bid
.Trace("<sc.TdsParser.Connect|SEC> Prelogin handshake successful\n");
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);
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.");
546 if (Bid
.AdvancedOn
) {
547 Bid
.Trace("<sc.TdsParser.Connect|SEC> Active directory authentication.Loaded Active Directory Authentication Library for SQL Server\n");
554 internal void RemoveEncryption() {
555 Debug
.Assert(_encryptionOption
== EncryptionOptions
.LOGIN
, "Invalid encryption option state");
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
);
565 // create a new packet encryption changes the internal packet size Bug# 228403
566 try {} // EmptyTry/Finally to avoid FXCop violation
568 _physicalStateObj
.ClearAllWritePackets();
572 internal void EnableMars() {
574 // Cache physical stateObj and connection.
575 _pMarsPhysicalConObj
= _physicalStateObj
;
580 // Add SMUX (MARS) SNI provider.
581 error
= SNINativeMethodWrapper
.SNIAddProvider(_pMarsPhysicalConObj
.Handle
, SNINativeMethodWrapper
.ProviderEnum
.SMUX_PROV
, ref info
);
583 if (error
!= TdsEnums
.SNI_SUCCESS
) {
584 _physicalStateObj
.AddError(ProcessSNIError(_physicalStateObj
));
585 ThrowExceptionAndWarning(_physicalStateObj
);
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.
592 IntPtr temp
= IntPtr
.Zero
;
594 RuntimeHelpers
.PrepareConstrainedRegions();
596 _pMarsPhysicalConObj
.IncrementPendingCallbacks();
598 error
= SNINativeMethodWrapper
.SNIReadAsync(_pMarsPhysicalConObj
.Handle
, ref temp
);
600 if (temp
!= IntPtr
.Zero
) {
601 // Be sure to release packet, otherwise it will be leaked by native.
602 SNINativeMethodWrapper
.SNIPacketRelease(temp
);
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
);
612 _physicalStateObj
= CreateSession(); // Create and open default MARS stateObj and connection.
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
);
624 internal TdsParserStateObject
GetSession(object owner
) {
625 TdsParserStateObject session
= null;
630 session
= _sessionPool
.GetSession(owner
);
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
);
638 session
= _physicalStateObj
;
639 if (Bid
.AdvancedOn
) {
640 Bid
.Trace("<sc.TdsParser.GetSession|ADV> %d# getting physical session %d\n", ObjectID
, session
.ObjectID
);
643 Debug
.Assert(session
._outputPacketNumber
==1, "The packet number is expected to be 1");
647 internal void PutSession(TdsParserStateObject session
) {
648 session
.AssertStateIsClean();
651 // This will take care of disposing if the parser is closed
652 _sessionPool
.PutSession(session
);
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
;
659 _physicalStateObj
.InvalidateDebugOnlyCopyOfSniContext();
661 _physicalStateObj
.Dispose();
664 // Non-MARS, and session is ok - remove its owner
665 _physicalStateObj
.Owner
= null;
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
;
673 var stateObj
= _physicalStateObj
;
674 if (stateObj
!= null) {
675 var stateObjHandle
= stateObj
.Handle
;
676 if (stateObjHandle
!= null) {
677 stateObjHandle
.Dispose();
682 var sessionPool
= _sessionPool
;
683 if (sessionPool
!= null) {
684 sessionPool
.BestEffortCleanup();
687 var marsStateObj
= _pMarsPhysicalConObj
;
688 if (marsStateObj
!= null) {
689 var marsStateObjHandle
= marsStateObj
.Handle
;
690 if (marsStateObjHandle
!= null) {
691 marsStateObjHandle
.Dispose();
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
703 // NOTE: packet data needs to be big endian - not the standard little endian used by
704 // the rest of the parser.
706 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_PRELOGIN
;
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;
712 byte[] payload
= new byte[(int)PreLoginOptions
.NUMOPT
* 5 + TdsEnums
.MAX_PRELOGIN_PAYLOAD_LENGTH
];
713 int payloadLength
= 0;
718 for (int option
= (int)PreLoginOptions
.VERSION
; option
< (int)PreLoginOptions
.NUMOPT
; option
++) {
719 int optionDataSize
= 0;
721 // Fill in the option
722 _physicalStateObj
.WriteByte((byte)option
);
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
729 case (int)PreLoginOptions
.VERSION
:
730 Version systemDataVersion
= ADP
.GetAssemblyVersion();
733 payload
[payloadLength
++] = (byte)(systemDataVersion
.Major
& 0xff);
734 payload
[payloadLength
++] = (byte)(systemDataVersion
.Minor
& 0xff);
736 // Build (Big Endian)
737 payload
[payloadLength
++] = (byte)((systemDataVersion
.Build
& 0xff00) >> 8);
738 payload
[payloadLength
++] = (byte)(systemDataVersion
.Build
& 0xff);
740 // Sub-build (Little Endian)
741 payload
[payloadLength
++] = (byte)(systemDataVersion
.Revision
& 0xff);
742 payload
[payloadLength
++] = (byte)((systemDataVersion
.Revision
& 0xff00) >> 8);
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
;
753 // Else, inform server of user request.
755 payload
[payloadLength
] = (byte)EncryptionOptions
.ON
;
756 _encryptionOption
= EncryptionOptions
.ON
;
759 payload
[payloadLength
] = (byte)EncryptionOptions
.OFF
;
760 _encryptionOption
= EncryptionOptions
.OFF
;
769 case (int)PreLoginOptions
.INSTANCE
:
772 while (instanceName
[i
] != 0) {
773 payload
[payloadLength
] = instanceName
[i
];
778 payload
[payloadLength
] = 0; // null terminate
786 case (int)PreLoginOptions
.THREADID
:
787 Int32 threadID
= TdsParserStaticMethods
.GetCurrentThreadIdForTdsLoginOnly();
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
);
797 case (int)PreLoginOptions
.MARS
:
798 payload
[payloadLength
++] = (byte)(_fMARS
? 1 : 0);
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
;
809 optionDataSize
= GUID_SIZE
;
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
);
821 optionDataSize
+= actIdSize
;
822 Bid
.Trace("<sc.TdsParser.SendPreLoginHandshake|INFO> ClientConnectionID %ls, ActivityID %ls\n", _connHandler
._clientConnectionId
.ToString(), actId
.ToString());
825 case (int)PreLoginOptions
.FEDAUTHREQUIRED
:
826 payload
[payloadLength
++] = 0x01;
832 Debug
.Assert(false, "UNKNOWN option in SendPreLoginHandshake");
837 _physicalStateObj
.WriteByte((byte)((optionDataSize
& 0xff00) >> 8));
838 _physicalStateObj
.WriteByte((byte)(optionDataSize
& 0x00ff));
841 // Write out last option - to let server know the second part of packet completed
842 _physicalStateObj
.WriteByte((byte)PreLoginOptions
.LASTOPT
);
845 _physicalStateObj
.WriteByteArray(payload
, payloadLength
, 0);
848 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
851 private PreLoginHandshakeStatus
ConsumePreLoginHandshake(SqlAuthenticationMethod authType
, bool encrypt
, bool trustServerCert
, bool integratedSecurity
, out bool marsCapable
, out bool fedAuthRequired
) {
853 // Assign default values
854 marsCapable
= _fMARS
;
855 fedAuthRequired
= false;
857 bool isYukonOrLater
= false;
859 Debug
.Assert(_physicalStateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
860 bool result
= _physicalStateObj
.TryReadNetworkPacket();
861 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
863 if (_physicalStateObj
._inBytesRead
== 0) {
864 // If the server did not respond then something has gone wrong and we need to close the connection
865 _physicalStateObj
.AddError(new SqlError(0, (byte)0x00, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.PreloginError(), "", 0));
866 _physicalStateObj
.Dispose();
867 ThrowExceptionAndWarning(_physicalStateObj
);
870 if (!_physicalStateObj
.TryProcessHeader()) { throw SQL.SynchronousCallMayNotPend(); }
872 if (_physicalStateObj
._inBytesPacket
> TdsEnums
.MAX_PACKET_SIZE
|| _physicalStateObj
._inBytesPacket
<= 0) {
873 throw SQL
.ParsingError(ParsingErrorState
.CorruptedTdsStream
);
875 byte[] payload
= new byte[_physicalStateObj
._inBytesPacket
];
877 Debug
.Assert(_physicalStateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
878 result
= _physicalStateObj
.TryReadByteArray(payload
, 0, payload
.Length
);
879 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
881 if (payload
[0] == 0xaa) {
882 // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which
883 // is not supported. SQL BU DT 296425
884 throw SQL
.InvalidSQLServerVersionUnknown();
888 int payloadOffset
= 0;
889 int payloadLength
= 0;
890 int option
= payload
[offset
++];
892 while (option
!= (byte)PreLoginOptions
.LASTOPT
) {
894 case (int)PreLoginOptions
.VERSION
:
895 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
896 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
898 byte majorVersion
= payload
[payloadOffset
];
899 byte minorVersion
= payload
[payloadOffset
+ 1];
900 int level
= (payload
[payloadOffset
+ 2] << 8) |
901 payload
[payloadOffset
+ 3];
903 isYukonOrLater
= majorVersion
>= 9;
904 if (!isYukonOrLater
) {
905 marsCapable
= false; // If pre-Yukon, MARS not supported.
910 case (int)PreLoginOptions
.ENCRYPT
:
911 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
912 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
914 EncryptionOptions serverOption
= (EncryptionOptions
)payload
[payloadOffset
];
916 /* internal enum EncryptionOptions {
924 switch (_encryptionOption
) {
925 case (EncryptionOptions
.ON
):
926 if (serverOption
== EncryptionOptions
.NOT_SUP
) {
927 _physicalStateObj
.AddError(new SqlError(TdsEnums
.ENCRYPTION_NOT_SUPPORTED
, (byte)0x00, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.EncryptionNotSupportedByServer(), "", 0));
928 _physicalStateObj
.Dispose();
929 ThrowExceptionAndWarning(_physicalStateObj
);
934 case (EncryptionOptions
.OFF
):
935 if (serverOption
== EncryptionOptions
.OFF
) {
936 // Only encrypt login.
937 _encryptionOption
= EncryptionOptions
.LOGIN
;
939 else if (serverOption
== EncryptionOptions
.REQ
) {
941 _encryptionOption
= EncryptionOptions
.ON
;
946 case (EncryptionOptions
.NOT_SUP
):
947 if (serverOption
== EncryptionOptions
.REQ
) {
948 _physicalStateObj
.AddError(new SqlError(TdsEnums
.ENCRYPTION_NOT_SUPPORTED
, (byte)0x00, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.EncryptionNotSupportedByClient(), "", 0));
949 _physicalStateObj
.Dispose();
950 ThrowExceptionAndWarning(_physicalStateObj
);
956 Debug
.Assert(false, "Invalid client encryption option detected");
960 if (_encryptionOption
== EncryptionOptions
.ON
||
961 _encryptionOption
== EncryptionOptions
.LOGIN
) {
964 // If we're using legacy server certificate validation behavior (Authentication keyword not provided and not using access token), then validate if
965 // Encrypt=true and Trust Sever Certificate = false.
966 // If using Authentication keyword or access token, validate if Trust Server Certificate=false.
967 bool shouldValidateServerCert
= (encrypt
&& !trustServerCert
) || ((authType
!= SqlAuthenticationMethod
.NotSpecified
|| _connHandler
._accessTokenInBytes
!= null) && !trustServerCert
);
969 UInt32 info
= (shouldValidateServerCert
? TdsEnums
.SNI_SSL_VALIDATE_CERTIFICATE
: 0)
970 | (isYukonOrLater
? TdsEnums
.SNI_SSL_USE_SCHANNEL_CACHE
: 0);
972 if (encrypt
&& !integratedSecurity
) {
973 // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI
974 // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context.
975 info
|= TdsEnums
.SNI_SSL_IGNORE_CHANNEL_BINDINGS
;
978 // Add SSL (Encryption) SNI provider.
979 error
= SNINativeMethodWrapper
.SNIAddProvider(_physicalStateObj
.Handle
, SNINativeMethodWrapper
.ProviderEnum
.SSL_PROV
, ref info
);
981 if (error
!= TdsEnums
.SNI_SUCCESS
) {
982 _physicalStateObj
.AddError(ProcessSNIError(_physicalStateObj
));
983 ThrowExceptionAndWarning(_physicalStateObj
);
986 // in the case where an async connection is made, encryption is used and Windows Authentication is used,
987 // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its
988 // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete
989 // before calling SNISecGenClientContext).
990 error
= SNINativeMethodWrapper
.SNIWaitForSSLHandshakeToComplete(_physicalStateObj
.Handle
, _physicalStateObj
.GetTimeoutRemaining());
991 if (error
!= TdsEnums
.SNI_SUCCESS
)
993 _physicalStateObj
.AddError(ProcessSNIError(_physicalStateObj
));
994 ThrowExceptionAndWarning(_physicalStateObj
);
997 // create a new packet encryption changes the internal packet size Bug# 228403
998 try {} // EmptyTry/Finally to avoid FXCop violation
1000 _physicalStateObj
.ClearAllWritePackets();
1006 case (int)PreLoginOptions
.INSTANCE
:
1007 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
1008 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
1010 byte ERROR_INST
= 0x1;
1011 byte instanceResult
= payload
[payloadOffset
];
1013 if (instanceResult
== ERROR_INST
) {
1014 // Check if server says ERROR_INST. That either means the cached info
1015 // we used to connect is not valid or we connected to a named instance
1016 // listening on default params.
1017 return PreLoginHandshakeStatus
.InstanceFailure
;
1022 case (int)PreLoginOptions
.THREADID
:
1023 // DO NOTHING FOR THREADID
1027 case (int)PreLoginOptions
.MARS
:
1028 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
1029 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
1031 marsCapable
= (payload
[payloadOffset
] == 0 ? false : true);
1033 Debug
.Assert(payload
[payloadOffset
] == 0 || payload
[payloadOffset
] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
1036 case (int)PreLoginOptions
.TRACEID
:
1037 // DO NOTHING FOR TRACEID
1041 case (int)PreLoginOptions
.FEDAUTHREQUIRED
:
1042 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
1043 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
1045 // Only 0x00 and 0x01 are accepted values from the server.
1046 if (payload
[payloadOffset
] != 0x00 && payload
[payloadOffset
] != 0x01) {
1047 Bid
.Trace("<sc.TdsParser.ConsumePreLoginHandshake|ERR> %d#, Server sent an unexpected value for FedAuthRequired PreLogin Option. Value was %d.\n", ObjectID
, (int)payload
[payloadOffset
]);
1048 throw SQL
.ParsingErrorValue(ParsingErrorState
.FedAuthRequiredPreLoginResponseInvalidValue
, (int)payload
[payloadOffset
]);
1051 // We must NOT use the response for the FEDAUTHREQUIRED PreLogin option, if the connection string option
1052 // was not using the new Authentication keyword or in other words, if Authentication=NotSpecified
1053 // Or AccessToken is not null, mean token based authentication is used.
1054 if ((_connHandler
.ConnectionOptions
!= null
1055 && _connHandler
.ConnectionOptions
.Authentication
!= SqlAuthenticationMethod
.NotSpecified
)
1056 || _connHandler
._accessTokenInBytes
!= null)
1058 fedAuthRequired
= payload
[payloadOffset
] == 0x01 ? true : false;
1063 Debug
.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option
);
1065 // DO NOTHING FOR THESE UNKNOWN OPTIONS
1071 if (offset
< payload
.Length
) {
1072 option
= payload
[offset
++];
1079 return PreLoginHandshakeStatus
.Successful
;
1082 internal void Deactivate(bool connectionIsDoomed
) {
1083 // Called when the connection that owns us is deactivated.
1085 if (Bid
.AdvancedOn
) {
1086 Bid
.Trace("<sc.TdsParser.Deactivate|ADV> %d# deactivating\n", ObjectID
);
1089 if (Bid
.IsOn(Bid
.ApiGroup
.StateDump
)) {
1090 Bid
.Trace("<sc.TdsParser.Deactivate|STATE> %d#, %ls\n", ObjectID
, TraceString());
1094 _sessionPool
.Deactivate();
1097 Debug
.Assert(connectionIsDoomed
|| null == _pendingTransaction
, "pending transaction at disconnect?");
1099 if (!connectionIsDoomed
&& null != _physicalStateObj
) {
1100 if (_physicalStateObj
._pendingData
) {
1101 DrainData(_physicalStateObj
);
1104 if (_physicalStateObj
.HasOpenResult
) { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
1105 _physicalStateObj
.DecrementOpenResultCount();
1109 // Any active, non-distributed transaction must be rolled back. We
1110 // need to wait for distributed transactions to be completed by the
1111 // transaction manager -- we don't want to automatically roll them
1114 // Note that when there is a transaction delegated to this connection,
1115 // we will defer the deactivation of this connection until the
1116 // transaction manager completes the transaction.
1117 SqlInternalTransaction currentTransaction
= CurrentTransaction
;
1119 if (null != currentTransaction
&& currentTransaction
.HasParentTransaction
) {
1120 currentTransaction
.CloseFromConnection();
1121 Debug
.Assert(null == CurrentTransaction
, "rollback didn't clear current transaction?");
1124 Statistics
= null; // must come after CleanWire or we won't count the stuff that happens there...
1127 // Used to close the connection and then free the memory allocated for the netlib connection.
1128 internal void Disconnect() {
1129 if (null != _sessionPool
) {
1130 // MARSOn may be true, but _sessionPool not yet created
1131 _sessionPool
.Dispose();
1134 // Can close the connection if its open or broken
1135 if (_state
!= TdsParserState
.Closed
) {
1136 //benign assert - the user could close the connection before consuming all the data
1137 //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
1139 _state
= TdsParserState
.Closed
;
1142 // If the _physicalStateObj has an owner, we will delay the disposal until the owner is finished with it
1143 if (!_physicalStateObj
.HasOwner
) {
1144 _physicalStateObj
.SniContext
= SniContext
.Snix_Close
;
1146 _physicalStateObj
.InvalidateDebugOnlyCopyOfSniContext();
1148 _physicalStateObj
.Dispose();
1151 // Remove the "initial" callback (this will allow the stateObj to be GC collected if need be)
1152 _physicalStateObj
.DecrementPendingCallbacks(false);
1155 // Not allocated until MARS is actually enabled in SNI.
1156 if (null != _pMarsPhysicalConObj
) {
1157 _pMarsPhysicalConObj
.Dispose();
1161 _pMarsPhysicalConObj
= null;
1166 // Fires a single InfoMessageEvent
1167 private void FireInfoMessageEvent(SqlConnection connection
, TdsParserStateObject stateObj
, SqlError error
) {
1169 string serverVersion
= null;
1171 Debug
.Assert(connection
!= null && _connHandler
.Connection
== connection
);
1173 if (_state
== TdsParserState
.OpenLoggedIn
) {
1174 serverVersion
= _connHandler
.ServerVersion
;
1177 SqlErrorCollection sqlErs
= new SqlErrorCollection();
1181 SqlException exc
= SqlException
.CreateException(sqlErs
, serverVersion
, _connHandler
);
1184 connection
.OnInfoMessage(new SqlInfoMessageEventArgs(exc
), out notified
);
1186 // observable side-effects, no retry
1187 stateObj
._syncOverAsync
= true;
1192 internal void DisconnectTransaction(SqlInternalTransaction internalTransaction
) {
1193 Debug
.Assert(_currentTransaction
!= null && _currentTransaction
== internalTransaction
, "disconnecting different transaction");
1195 if (_currentTransaction
!= null && _currentTransaction
== internalTransaction
) {
1196 _currentTransaction
= null;
1200 internal void RollbackOrphanedAPITransactions() {
1201 // Any active, non-distributed transaction must be rolled back.
1202 SqlInternalTransaction currentTransaction
= CurrentTransaction
;
1204 if (null != currentTransaction
&& currentTransaction
.HasParentTransaction
&& currentTransaction
.IsOrphaned
) {
1205 currentTransaction
.CloseFromConnection();
1206 Debug
.Assert(null == CurrentTransaction
, "rollback didn't clear current transaction?");
1210 internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj
, bool callerHasConnectionLock
= false, bool asyncClose
= false) {
1211 Debug
.Assert(!callerHasConnectionLock
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Caller claims to have lock, but connection lock is not taken");
1213 SqlException exception
= null;
1214 bool breakConnection
;
1216 // This function should only be called when there was an error or warning. If there aren't any
1217 // errors, the handler will be called for the warning(s). If there was an error, the warning(s) will
1218 // be copied to the end of the error collection so that the user may see all the errors and also the
1219 // warnings that occurred.
1221 SqlErrorCollection temp
= stateObj
.GetFullErrorAndWarningCollection(out breakConnection
);
1223 Debug
.Assert(temp
.Count
> 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
1224 if (temp
.Count
== 0)
1226 Bid
.Trace("<sc.TdsParser.ThrowExceptionAndWarning|ERR> Potential multi-threaded misuse of connection, unexpectedly empty warnings/errors under lock %d#\n", ObjectID
);
1228 Debug
.Assert(_connHandler
!= null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!");
1230 // Don't break the connection if it is already closed
1231 breakConnection
&= (TdsParserState
.Closed
!= _state
);
1232 if (breakConnection
) {
1233 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
))) {
1234 // 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'
1235 // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/459546
1236 // For Multisubnet Failover we slice the timeout to make reconnecting faster (with the assumption that the server will not failover instantaneously)
1237 // 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
1238 breakConnection
= false;
1242 _state
= TdsParserState
.Broken
;
1246 Debug
.Assert(temp
!= null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection");
1247 if (temp
!= null && temp
.Count
> 0) {
1248 // Construct the exception now that we've collected all the errors
1249 string serverVersion
= null;
1250 if (_state
== TdsParserState
.OpenLoggedIn
) {
1251 serverVersion
= _connHandler
.ServerVersion
;
1253 exception
= SqlException
.CreateException(temp
, serverVersion
, _connHandler
);
1254 if (exception
.Procedure
== TdsEnums
.INIT_ADAL_PACKAGE
|| exception
.Procedure
== TdsEnums
.INIT_SSPI_PACKAGE
) {
1255 exception
._doNotReconnect
= true;
1259 // call OnError outside of _ErrorCollectionLock to avoid deadlock
1260 if (exception
!= null) {
1261 if (breakConnection
) {
1262 // report exception to pending async operation
1263 // before OnConnectionClosed overrides the exception
1264 // due to connection close notification through references
1265 var taskSource
= stateObj
._networkPacketTaskSource
;
1266 if (taskSource
!= null) {
1267 taskSource
.TrySetException(ADP
.ExceptionWithStackTrace(exception
));
1272 // Wait until we have the parser lock, then try to close
1273 var connHandler
= _connHandler
;
1274 Action
<Action
> wrapCloseAction
= closeAction
=> {
1275 Task
.Factory
.StartNew(() => {
1276 connHandler
._parserLock
.Wait(canReleaseFromAnyThread
: false);
1277 connHandler
.ThreadHasParserLockForClose
= true;
1282 connHandler
.ThreadHasParserLockForClose
= false;
1283 connHandler
._parserLock
.Release();
1288 _connHandler
.OnError(exception
, breakConnection
, wrapCloseAction
);
1291 // Let close know that we already have the _parserLock
1292 bool threadAlreadyHadParserLockForClose
= _connHandler
.ThreadHasParserLockForClose
;
1293 if (callerHasConnectionLock
) {
1294 _connHandler
.ThreadHasParserLockForClose
= true;
1297 // the following handler will throw an exception or generate a warning event
1298 _connHandler
.OnError(exception
, breakConnection
);
1301 if (callerHasConnectionLock
) {
1302 _connHandler
.ThreadHasParserLockForClose
= threadAlreadyHadParserLockForClose
;
1309 internal SqlError
ProcessSNIError(TdsParserStateObject stateObj
) {
1311 // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error
1312 Debug
.Assert(SniContext
.Undefined
!=stateObj
.DebugOnlyCopyOfSniContext
|| ((_fMARS
) && ((_state
== TdsParserState
.Closed
) || (_state
== TdsParserState
.Broken
))), "SniContext must not be None");
1314 SNINativeMethodWrapper
.SNI_Error sniError
= new SNINativeMethodWrapper
.SNI_Error();
1315 SNINativeMethodWrapper
.SNIGetLastError(sniError
);
1317 if (sniError
.sniError
!= 0) {
1319 // handle special SNI error codes that are converted into exception which is not a SqlException.
1320 switch (sniError
.sniError
) {
1321 case (int)SNINativeMethodWrapper
.SniSpecialErrors
.MultiSubnetFailoverWithMoreThan64IPs
:
1322 // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
1323 throw SQL
.MultiSubnetFailoverWithMoreThan64IPs();
1325 case (int)SNINativeMethodWrapper
.SniSpecialErrors
.MultiSubnetFailoverWithInstanceSpecified
:
1326 // Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported.
1327 throw SQL
.MultiSubnetFailoverWithInstanceSpecified();
1329 case (int)SNINativeMethodWrapper
.SniSpecialErrors
.MultiSubnetFailoverWithNonTcpProtocol
:
1330 // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
1331 throw SQL
.MultiSubnetFailoverWithNonTcpProtocol();
1333 // continue building SqlError instance
1337 // error.errorMessage is null terminated with garbage beyond that, since fixed length
1338 string errorMessage
;
1339 int MessageLength
= Array
.IndexOf(sniError
.errorMessage
, '\0');
1340 if (MessageLength
== -1) {
1341 errorMessage
= String
.Empty
; // If we don't see the expected null return nothing.
1343 errorMessage
= new String(sniError
.errorMessage
, 0, MessageLength
);
1346 // Format SNI errors and add Context Information
1348 // General syntax is:
1349 // <sqlclient message>
1350 // (provider:<SNIx provider>, error: <SNIx error code> - <SNIx error message>)
1352 // errorMessage | sniError |
1353 // -------------------------------------------
1354 // ==null | x | must never happen
1355 // !=null | != 0 | retrieve corresponding errorMessage from resources
1356 // !=null | == 0 | replace text left of errorMessage
1359 Debug
.Assert(!ADP
.IsEmpty(errorMessage
),"Empty error message received from SNI");
1361 string sqlContextInfo
= Res
.GetString(Enum
.GetName(typeof(SniContext
), stateObj
.SniContext
));
1363 string providerRid
= String
.Format((IFormatProvider
)null,"SNI_PN{0}", (int)sniError
.provider
);
1364 string providerName
= Res
.GetString(providerRid
);
1365 Debug
.Assert(!ADP
.IsEmpty(providerName
), String
.Format((IFormatProvider
)null,"invalid providerResourceId '{0}'", providerRid
));
1366 uint win32ErrorCode
= sniError
.nativeError
;
1368 if (sniError
.sniError
== 0) {
1369 // Provider error. The message from provider is preceeded with non-localizable info from SNI
1370 // strip provider info from SNI
1372 int iColon
= errorMessage
.IndexOf(':');
1373 Debug
.Assert(0<=iColon
, "':' character missing in sni errorMessage");
1374 Debug
.Assert(errorMessage
.Length
>iColon
+1 && errorMessage
[iColon
+1]==' ', "Expecting a space after the ':' character");
1376 // extract the message excluding the colon and trailing cr/lf chars
1378 int len
= errorMessage
.Length
;
1379 len
-=2; // exclude "\r\n" sequence
1380 iColon
+=2; // skip over ": " sequence
1383 The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
1384 Fix Bug 370686, if the message is recieved on a Win9x OS, the error message will not contain MESSAGE TEXT
1385 per Bug: 269574. If we get a errormessage with no message text, just return the entire message otherwise
1386 return just the message text.
1389 errorMessage
= errorMessage
.Substring(iColon
, len
);
1394 // SNI error. Replace the entire message
1396 errorMessage
= SQL
.GetSNIErrorMessage((int)sniError
.sniError
);
1398 // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
1399 if (sniError
.sniError
== (int)SNINativeMethodWrapper
.SniSpecialErrors
.LocalDBErrorCode
) {
1400 errorMessage
+= LocalDBAPI
.GetLocalDBMessage((int)sniError
.nativeError
);
1404 errorMessage
= String
.Format((IFormatProvider
)null, "{0} (provider: {1}, error: {2} - {3})",
1405 sqlContextInfo
, providerName
, (int)sniError
.sniError
, errorMessage
);
1407 return new SqlError((int)sniError
.nativeError
, 0x00, TdsEnums
.FATAL_ERROR_CLASS
,
1408 _server
, errorMessage
, sniError
.function
, (int)sniError
.lineNumber
, win32ErrorCode
);
1411 internal void CheckResetConnection(TdsParserStateObject stateObj
) {
1412 if (_fResetConnection
&& !stateObj
._fResetConnectionSent
) {
1413 Debug
.Assert(stateObj
._outputPacketNumber
== 1 || stateObj
._outputPacketNumber
== 2, "In ResetConnection logic unexpectedly!");
1415 if (_fMARS
&& !stateObj
._fResetEventOwned
) {
1416 // If using Async & MARS and we do not own ResetEvent - grab it. We need to not grab lock here
1417 // for case where multiple packets are sent to server from one execute.
1418 stateObj
._fResetEventOwned
= _resetConnectionEvent
.WaitOne(stateObj
.GetTimeoutRemaining(), false);
1420 if (stateObj
._fResetEventOwned
) {
1421 if (stateObj
.TimeoutHasExpired
) {
1422 // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining.
1423 stateObj
._fResetEventOwned
= !_resetConnectionEvent
.Set();
1424 stateObj
.TimeoutTime
= 0;
1428 if (!stateObj
._fResetEventOwned
) {
1429 // We timed out waiting for ResetEvent. Throw timeout exception and reset
1430 // the buffer. Nothing else to do since we did not actually send anything
1432 stateObj
.ResetBuffer();
1433 Debug
.Assert(_connHandler
!= null, "SqlConnectionInternalTds handler can not be null at this point.");
1434 stateObj
.AddError(new SqlError(TdsEnums
.TIMEOUT_EXPIRED
, (byte)0x00, TdsEnums
.MIN_ERROR_CLASS
, _server
, _connHandler
.TimeoutErrorInternal
.GetErrorMessage(), "", 0, TdsEnums
.SNI_WAIT_TIMEOUT
));
1435 Debug
.Assert(_connHandler
._parserLock
.ThreadMayHaveLock(), "Thread is writing without taking the connection lock");
1436 ThrowExceptionAndWarning(stateObj
, callerHasConnectionLock
: true);
1440 if (_fResetConnection
) {
1441 // Check again to see if we need to send reset.
1443 Debug
.Assert(!stateObj
._fResetConnectionSent
, "Unexpected state for sending reset connection");
1444 Debug
.Assert(_isShiloh
, "TdsParser.cs: Error! _fResetConnection true when not going against Shiloh or later!");
1446 if (_fPreserveTransaction
) {
1447 // if we are reseting, set bit in header by or'ing with other value
1448 stateObj
._outBuff
[1] = (Byte
)(stateObj
._outBuff
[1] | TdsEnums
.ST_RESET_CONNECTION_PRESERVE_TRANSACTION
);
1451 // if we are reseting, set bit in header by or'ing with other value
1452 stateObj
._outBuff
[1] = (Byte
)(stateObj
._outBuff
[1] | TdsEnums
.ST_RESET_CONNECTION
);
1456 _fResetConnection
= false; // If not MARS, can turn off flag now.
1457 _fPreserveTransaction
= false;
1460 stateObj
._fResetConnectionSent
= true; // Otherwise set flag so we don't resend on multiple packet execute.
1463 else if (_fMARS
&& stateObj
._fResetEventOwned
) {
1464 Debug
.Assert(!stateObj
._fResetConnectionSent
, "Unexpected state on WritePacket ResetConnection");
1466 // Otherwise if Yukon and we grabbed the event, free it. Another execute grabbed the event and
1467 // took care of sending the reset.
1468 stateObj
._fResetEventOwned
= !_resetConnectionEvent
.Set();
1469 Debug
.Assert(!stateObj
._fResetEventOwned
, "Invalid AutoResetEvent state!");
1473 if (_fMARS
&& stateObj
._fResetEventOwned
) {
1474 // If exception thrown, and we are on Yukon and own the event, release it!
1475 stateObj
._fResetConnectionSent
= false;
1476 stateObj
._fResetEventOwned
= !_resetConnectionEvent
.Set();
1477 Debug
.Assert(!stateObj
._fResetEventOwned
, "Invalid AutoResetEvent state!");
1485 Debug
.Assert(!_fResetConnection
||
1486 (_fResetConnection
&& stateObj
._fResetConnectionSent
&& stateObj
._fResetEventOwned
),
1487 "Unexpected state on else ResetConnection block in WritePacket");
1493 // Takes a 16 bit short and writes it.
1495 internal byte[] SerializeShort(int v
, TdsParserStateObject stateObj
) {
1496 if (null == stateObj
._bShortBytes
) {
1497 stateObj
._bShortBytes
= new byte[2];
1500 Debug
.Assert(2 == stateObj
._bShortBytes
.Length
);
1503 byte[] bytes
= stateObj
._bShortBytes
;
1505 bytes
[current
++] = (byte)(v
& 0xff);
1506 bytes
[current
++] = (byte)((v
>> 8) & 0xff);
1510 internal void WriteShort(int v
, TdsParserStateObject stateObj
) {
1511 ReliabilitySection
.Assert("unreliable call to WriteShort"); // you need to setup for a thread abort somewhere before you call this method
1513 if ((stateObj
._outBytesUsed
+ 2) > stateObj
._outBuff
.Length
) {
1514 // if all of the short doesn't fit into the buffer
1515 stateObj
.WriteByte((byte)(v
& 0xff));
1516 stateObj
.WriteByte((byte)((v
>> 8) & 0xff));
1519 // all of the short fits into the buffer
1520 stateObj
._outBuff
[stateObj
._outBytesUsed
] = (byte)(v
& 0xff);
1521 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 1] = (byte)((v
>> 8) & 0xff);
1522 stateObj
._outBytesUsed
+= 2;
1526 internal void WriteUnsignedShort(ushort us
, TdsParserStateObject stateObj
) {
1527 WriteShort((short)us
, stateObj
);
1531 // Takes a long and writes out an unsigned int
1533 internal byte[] SerializeUnsignedInt(uint i
, TdsParserStateObject stateObj
) {
1534 return SerializeInt((int)i
, stateObj
);
1537 internal void WriteUnsignedInt(uint i
, TdsParserStateObject stateObj
) {
1538 WriteInt((int)i
, stateObj
);
1542 // Takes an int and writes it as an int.
1544 internal byte[] SerializeInt(int v
, TdsParserStateObject stateObj
) {
1545 if (null == stateObj
._bIntBytes
) {
1546 stateObj
._bIntBytes
= new byte[4];
1549 Debug
.Assert (4 == stateObj
._bIntBytes
.Length
);
1553 byte[] bytes
= stateObj
._bIntBytes
;
1554 bytes
[current
++] = (byte)(v
& 0xff);
1555 bytes
[current
++] = (byte)((v
>> 8) & 0xff);
1556 bytes
[current
++] = (byte)((v
>> 16) & 0xff);
1557 bytes
[current
++] = (byte)((v
>> 24) & 0xff);
1561 internal void WriteInt(int v
, TdsParserStateObject stateObj
) {
1562 ReliabilitySection
.Assert("unreliable call to WriteInt"); // you need to setup for a thread abort somewhere before you call this method
1564 if ((stateObj
._outBytesUsed
+ 4) > stateObj
._outBuff
.Length
) {
1565 // if all of the int doesn't fit into the buffer
1566 for (int shiftValue
= 0; shiftValue
< sizeof(int) * 8; shiftValue
+= 8) {
1567 stateObj
.WriteByte((byte)((v
>> shiftValue
) & 0xff));
1571 // all of the int fits into the buffer
1572 // NOTE: We don't use a loop here for performance
1573 stateObj
._outBuff
[stateObj
._outBytesUsed
] = (byte)(v
& 0xff);
1574 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 1] = (byte)((v
>> 8) & 0xff);
1575 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 2] = (byte)((v
>> 16) & 0xff);
1576 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 3] = (byte)((v
>> 24) & 0xff);
1577 stateObj
._outBytesUsed
+= 4;
1582 // Takes a float and writes it as a 32 bit float.
1584 internal byte[] SerializeFloat(float v
) {
1585 if (Single
.IsInfinity(v
) || Single
.IsNaN(v
)) {
1586 throw ADP
.ParameterValueOutOfRange(v
.ToString());
1589 return BitConverter
.GetBytes(v
);
1592 internal void WriteFloat(float v
, TdsParserStateObject stateObj
) {
1593 byte[] bytes
= BitConverter
.GetBytes(v
);
1595 stateObj
.WriteByteArray(bytes
, bytes
.Length
, 0);
1599 // Takes a long and writes it as a long.
1601 internal byte[] SerializeLong(long v
, TdsParserStateObject stateObj
) {
1603 if (null == stateObj
._bLongBytes
) {
1604 stateObj
._bLongBytes
= new byte[8];
1607 byte[] bytes
= stateObj
._bLongBytes
;
1608 Debug
.Assert (8 == bytes
.Length
, "Cached buffer has wrong size");
1610 bytes
[current
++] = (byte)(v
& 0xff);
1611 bytes
[current
++] = (byte)((v
>> 8) & 0xff);
1612 bytes
[current
++] = (byte)((v
>> 16) & 0xff);
1613 bytes
[current
++] = (byte)((v
>> 24) & 0xff);
1614 bytes
[current
++] = (byte)((v
>> 32) & 0xff);
1615 bytes
[current
++] = (byte)((v
>> 40) & 0xff);
1616 bytes
[current
++] = (byte)((v
>> 48) & 0xff);
1617 bytes
[current
++] = (byte)((v
>> 56) & 0xff);
1622 internal void WriteLong(long v
, TdsParserStateObject stateObj
) {
1623 ReliabilitySection
.Assert("unreliable call to WriteLong"); // you need to setup for a thread abort somewhere before you call this method
1625 if ((stateObj
._outBytesUsed
+ 8) > stateObj
._outBuff
.Length
) {
1626 // if all of the long doesn't fit into the buffer
1627 for (int shiftValue
= 0; shiftValue
< sizeof(long) * 8; shiftValue
+= 8) {
1628 stateObj
.WriteByte((byte)((v
>> shiftValue
) & 0xff));
1632 // all of the long fits into the buffer
1633 // NOTE: We don't use a loop here for performance
1634 stateObj
._outBuff
[stateObj
._outBytesUsed
] = (byte)(v
& 0xff);
1635 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 1] = (byte)((v
>> 8) & 0xff);
1636 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 2] = (byte)((v
>> 16) & 0xff);
1637 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 3] = (byte)((v
>> 24) & 0xff);
1638 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 4] = (byte)((v
>> 32) & 0xff);
1639 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 5] = (byte)((v
>> 40) & 0xff);
1640 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 6] = (byte)((v
>> 48) & 0xff);
1641 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 7] = (byte)((v
>> 56) & 0xff);
1642 stateObj
._outBytesUsed
+= 8;
1647 // Takes a long and writes part of it
1649 internal byte[] SerializePartialLong(long v
, int length
) {
1650 Debug
.Assert(length
<= 8, "Length specified is longer than the size of a long");
1651 Debug
.Assert(length
>= 0, "Length should not be negative");
1653 byte[] bytes
= new byte[length
];
1655 // all of the long fits into the buffer
1656 for (int index
= 0; index
< length
; index
++) {
1657 bytes
[index
] = (byte)((v
>> (index
* 8)) & 0xff);
1663 internal void WritePartialLong(long v
, int length
, TdsParserStateObject stateObj
) {
1664 ReliabilitySection
.Assert("unreliable call to WritePartialLong"); // you need to setup for a thread abort somewhere before you call this method
1665 Debug
.Assert(length
<= 8, "Length specified is longer than the size of a long");
1666 Debug
.Assert(length
>= 0, "Length should not be negative");
1668 if ((stateObj
._outBytesUsed
+ length
) > stateObj
._outBuff
.Length
) {
1669 // if all of the long doesn't fit into the buffer
1670 for (int shiftValue
= 0; shiftValue
< length
* 8; shiftValue
+= 8) {
1671 stateObj
.WriteByte((byte)((v
>> shiftValue
) & 0xff));
1675 // all of the long fits into the buffer
1676 for (int index
= 0; index
< length
; index
++) {
1677 stateObj
._outBuff
[stateObj
._outBytesUsed
+ index
] = (byte)((v
>> (index
* 8)) & 0xff);
1679 stateObj
._outBytesUsed
+= length
;
1684 // Takes a ulong and writes it as a ulong.
1686 internal void WriteUnsignedLong(ulong uv
, TdsParserStateObject stateObj
) {
1687 WriteLong((long)uv
, stateObj
);
1691 // Takes a double and writes it as a 64 bit double.
1693 internal byte[] SerializeDouble(double v
) {
1694 if (Double
.IsInfinity(v
) || Double
.IsNaN(v
)) {
1695 throw ADP
.ParameterValueOutOfRange(v
.ToString());
1698 return BitConverter
.GetBytes(v
);
1701 internal void WriteDouble(double v
, TdsParserStateObject stateObj
) {
1702 byte[] bytes
= BitConverter
.GetBytes(v
);
1704 stateObj
.WriteByteArray(bytes
, bytes
.Length
, 0);
1707 internal void PrepareResetConnection(bool preserveTransaction
) {
1708 // Set flag to reset connection upon next use - only for use on shiloh!
1709 _fResetConnection
= true;
1710 _fPreserveTransaction
= preserveTransaction
;
1713 internal bool RunReliably(RunBehavior runBehavior
, SqlCommand cmdHandler
, SqlDataReader dataStream
, BulkCopySimpleResultSet bulkCopyHandler
, TdsParserStateObject stateObj
) {
1714 RuntimeHelpers
.PrepareConstrainedRegions();
1717 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
1718 RuntimeHelpers
.PrepareConstrainedRegions();
1720 tdsReliabilitySection
.Start();
1722 return Run(runBehavior
, cmdHandler
, dataStream
, bulkCopyHandler
, stateObj
);
1726 tdsReliabilitySection
.Stop();
1730 catch (OutOfMemoryException
) {
1731 _connHandler
.DoomThisConnection();
1734 catch (StackOverflowException
) {
1735 _connHandler
.DoomThisConnection();
1738 catch (ThreadAbortException
) {
1739 _connHandler
.DoomThisConnection();
1745 internal bool Run(RunBehavior runBehavior
, SqlCommand cmdHandler
, SqlDataReader dataStream
, BulkCopySimpleResultSet bulkCopyHandler
, TdsParserStateObject stateObj
) {
1746 bool syncOverAsync
= stateObj
._syncOverAsync
;
1749 stateObj
._syncOverAsync
= true;
1752 bool result
= TryRun(runBehavior
, cmdHandler
, dataStream
, bulkCopyHandler
, stateObj
, out dataReady
);
1753 Debug
.Assert(result
== true, "Should never return false when _syncOverAsync is set");
1758 stateObj
._syncOverAsync
= syncOverAsync
;
1763 /// Checks if the given token is a valid TDS token
1765 /// <param name="token">Token to check</param>
1766 /// <returns>True if the token is a valid TDS token, otherwise false</returns>
1767 internal static bool IsValidTdsToken(byte token
) {
1769 token
== TdsEnums
.SQLERROR
||
1770 token
== TdsEnums
.SQLINFO
||
1771 token
== TdsEnums
.SQLLOGINACK
||
1772 token
== TdsEnums
.SQLENVCHANGE
||
1773 token
== TdsEnums
.SQLRETURNVALUE
||
1774 token
== TdsEnums
.SQLRETURNSTATUS
||
1775 token
== TdsEnums
.SQLCOLNAME
||
1776 token
== TdsEnums
.SQLCOLFMT
||
1777 token
== TdsEnums
.SQLCOLMETADATA
||
1778 token
== TdsEnums
.SQLALTMETADATA
||
1779 token
== TdsEnums
.SQLTABNAME
||
1780 token
== TdsEnums
.SQLCOLINFO
||
1781 token
== TdsEnums
.SQLORDER
||
1782 token
== TdsEnums
.SQLALTROW
||
1783 token
== TdsEnums
.SQLROW
||
1784 token
== TdsEnums
.SQLNBCROW
||
1785 token
== TdsEnums
.SQLDONE
||
1786 token
== TdsEnums
.SQLDONEPROC
||
1787 token
== TdsEnums
.SQLDONEINPROC
||
1788 token
== TdsEnums
.SQLROWCRC
||
1789 token
== TdsEnums
.SQLSECLEVEL
||
1790 token
== TdsEnums
.SQLPROCID
||
1791 token
== TdsEnums
.SQLOFFSET
||
1792 token
== TdsEnums
.SQLSSPI
||
1793 token
== TdsEnums
.SQLFEATUREEXTACK
||
1794 token
== TdsEnums
.SQLSESSIONSTATE
||
1795 token
== TdsEnums
.SQLFEDAUTHINFO
);
1798 // Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces
1799 internal bool TryRun(RunBehavior runBehavior
, SqlCommand cmdHandler
, SqlDataReader dataStream
, BulkCopySimpleResultSet bulkCopyHandler
, TdsParserStateObject stateObj
, out bool dataReady
) {
1800 ReliabilitySection
.Assert("unreliable call to Run"); // you need to setup for a thread abort somewhere before you call this method
1801 Debug
.Assert((SniContext
.Undefined
!= stateObj
.SniContext
) && // SniContext must not be Undefined
1802 ((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)
1803 String
.Format("Unexpected SniContext on call to TryRun; SniContext={0}", stateObj
.SniContext
));
1805 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
) {
1807 return true; // Just in case this is called in a loop, expecting data to be returned.
1813 // If there is data ready, but we didn't exit the loop, then something is wrong
1814 Debug
.Assert(!dataReady
, "dataReady not expected - did we forget to skip the row?");
1816 if (stateObj
._internalTimeout
) {
1817 runBehavior
= RunBehavior
.Attention
;
1820 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
)
1821 break; // jump out of the loop if the state is already broken or closed.
1823 if (!stateObj
._accumulateInfoEvents
&& (stateObj
._pendingInfoEvents
!= null)) {
1824 if (RunBehavior
.Clean
!= (RunBehavior
.Clean
& runBehavior
)) {
1825 SqlConnection connection
= null;
1826 if (_connHandler
!= null)
1827 connection
= _connHandler
.Connection
; // SqlInternalConnection holds the user connection object as a weak ref
1828 // We are omitting checks for error.Class in the code below (see processing of INFO) since we know (and assert) that error class
1829 // error.Class < TdsEnums.MIN_ERROR_CLASS for info message.
1830 // Also we know that TdsEnums.MIN_ERROR_CLASS<TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS
1831 if ((connection
!= null) && connection
.FireInfoMessageEventOnUserErrors
)
1833 foreach (SqlError error
in stateObj
._pendingInfoEvents
)
1834 FireInfoMessageEvent(connection
, stateObj
, error
);
1837 foreach (SqlError error
in stateObj
._pendingInfoEvents
)
1838 stateObj
.AddWarning(error
);
1841 stateObj
._pendingInfoEvents
=null;
1845 if (!stateObj
.TryReadByte(out token
)) {
1849 if (!IsValidTdsToken(token
)) {
1850 Debug
.Assert(false, String
.Format((IFormatProvider
)null, "unexpected token; token = {0,-2:X2}", token
));
1851 _state
= TdsParserState
.Broken
;
1852 _connHandler
.BreakConnection();
1853 Bid
.Trace("<sc.TdsParser.Run|ERR> Potential multi-threaded misuse of connection, unexpected TDS token found %d#\n", ObjectID
);
1854 throw SQL
.ParsingErrorToken(ParsingErrorState
.InvalidTdsTokenReceived
, token
); // MDAC 82443
1858 if (!TryGetTokenLength(token
, stateObj
, out tokenLength
)) {
1863 case TdsEnums
.SQLERROR
:
1864 case TdsEnums
.SQLINFO
:
1866 if (token
== TdsEnums
.SQLERROR
) {
1867 stateObj
._errorTokenReceived
= true; // Keep track of the fact error token was received - for Done processing.
1871 if (!TryProcessError(token
, stateObj
, out error
)) {
1875 if (token
== TdsEnums
.SQLINFO
&& stateObj
._accumulateInfoEvents
)
1877 Debug
.Assert(error
.Class
< TdsEnums
.MIN_ERROR_CLASS
, "INFO with class > TdsEnums.MIN_ERROR_CLASS");
1879 if (stateObj
._pendingInfoEvents
== null)
1880 stateObj
._pendingInfoEvents
= new List
<SqlError
>();
1881 stateObj
._pendingInfoEvents
.Add(error
);
1882 stateObj
._syncOverAsync
= true;
1886 if (RunBehavior
.Clean
!= (RunBehavior
.Clean
& runBehavior
)) {
1887 // If FireInfoMessageEventOnUserErrors is true, we have to fire event without waiting.
1888 // Otherwise we can go ahead and add it to errors/warnings collection.
1889 SqlConnection connection
= null;
1890 if (_connHandler
!= null)
1891 connection
= _connHandler
.Connection
; // SqlInternalConnection holds the user connection object as a weak ref
1893 if ((connection
!= null) &&
1894 (connection
.FireInfoMessageEventOnUserErrors
== true) &&
1895 (error
.Class
<= TdsEnums
.MAX_USER_CORRECTABLE_ERROR_CLASS
)) {
1896 // Fire SqlInfoMessage here
1897 FireInfoMessageEvent(connection
, stateObj
, error
);
1900 // insert error/info into the appropriate exception - warning if info, exception if error
1901 if (error
.Class
< TdsEnums
.MIN_ERROR_CLASS
) {
1902 stateObj
.AddWarning(error
);
1904 else if (error
.Class
< TdsEnums
.FATAL_ERROR_CLASS
) {
1905 // VSTFDEVDIV 479643: continue results processing for all non-fatal errors (<20)
1907 stateObj
.AddError(error
);
1909 // Add it to collection - but do NOT change run behavior UNLESS
1910 // we are in an ExecuteReader call - at which time we will be throwing
1911 // anyways so we need to consume all errors. This is not the case
1912 // if we have already given out a reader. If we have already given out
1913 // a reader we need to throw the error but not halt further processing. We used to
1914 // halt processing and that was a bug preventing the user from
1915 // processing subsequent results.
1917 if (null != dataStream
) { // Webdata 104560
1918 if (!dataStream
.IsInitialized
) {
1919 runBehavior
= RunBehavior
.UntilDone
;
1924 stateObj
.AddError(error
);
1926 // Else we have a fatal error and we need to change the behavior
1927 // since we want the complete error information in the exception.
1928 // Besides - no further results will be received.
1929 runBehavior
= RunBehavior
.UntilDone
;
1933 else if (error
.Class
>= TdsEnums
.FATAL_ERROR_CLASS
) {
1934 stateObj
.AddError(error
);
1939 case TdsEnums
.SQLCOLINFO
:
1941 if (null != dataStream
) {
1942 _SqlMetaDataSet metaDataSet
;
1943 if (!TryProcessColInfo(dataStream
.MetaData
, dataStream
, stateObj
, out metaDataSet
)) {
1946 if (!dataStream
.TrySetMetaData(metaDataSet
, false)) {
1949 dataStream
.BrowseModeInfoConsumed
= true;
1951 else { // no dataStream
1952 if (!stateObj
.TrySkipBytes(tokenLength
)) {
1959 case TdsEnums
.SQLDONE
:
1960 case TdsEnums
.SQLDONEPROC
:
1961 case TdsEnums
.SQLDONEINPROC
:
1963 // RunBehavior can be modified - see SQL BU DT 269516 & 290090
1964 if (!TryProcessDone(cmdHandler
, dataStream
, ref runBehavior
, stateObj
)) {
1967 if ((token
== TdsEnums
.SQLDONEPROC
) && (cmdHandler
!= null)) {
1968 // If the current parse/read is for the results of describe parameter encryption RPC requests,
1969 // call a different handler which will update the describe parameter encryption RPC structures
1970 // with the results, instead of the actual user RPC requests.
1971 if (cmdHandler
.IsDescribeParameterEncryptionRPCCurrentlyInProgress
) {
1972 cmdHandler
.OnDoneDescribeParameterEncryptionProc(stateObj
);
1975 cmdHandler
.OnDoneProc();
1982 case TdsEnums
.SQLORDER
:
1984 // don't do anything with the order token so read off the pipe
1985 if (!stateObj
.TrySkipBytes(tokenLength
)) {
1991 case TdsEnums
.SQLALTMETADATA
:
1993 stateObj
.CloneCleanupAltMetaDataSetArray();
1995 if (stateObj
._cleanupAltMetaDataSetArray
== null) {
1996 // create object on demand (lazy creation)
1997 stateObj
._cleanupAltMetaDataSetArray
= new _SqlMetaDataSetCollection();
2000 _SqlMetaDataSet cleanupAltMetaDataSet
;
2001 if (!TryProcessAltMetaData(tokenLength
, stateObj
, out cleanupAltMetaDataSet
)) {
2005 stateObj
._cleanupAltMetaDataSetArray
.SetAltMetaData(cleanupAltMetaDataSet
);
2006 if (null != dataStream
) {
2007 byte metadataConsumedByte
;
2008 if (!stateObj
.TryPeekByte(out metadataConsumedByte
)) {
2011 if (!dataStream
.TrySetAltMetaDataSet(cleanupAltMetaDataSet
, (TdsEnums
.SQLALTMETADATA
!= metadataConsumedByte
))) {
2019 case TdsEnums
.SQLALTROW
:
2021 if (!stateObj
.TryStartNewRow(isNullCompressed
:false)) { // altrows are not currently null compressed
2025 // read will call run until dataReady. Must not read any data if returnimmetiately set
2026 if (RunBehavior
.ReturnImmediately
!= (RunBehavior
.ReturnImmediately
& runBehavior
)) {
2028 if (!stateObj
.TryReadUInt16(out altRowId
)) { // get altRowId
2032 if (!TrySkipRow(stateObj
._cleanupAltMetaDataSetArray
.GetAltMetaData(altRowId
), stateObj
)) { // skip altRow
2043 case TdsEnums
.SQLENVCHANGE
:
2045 // ENVCHANGE must be processed synchronously (since it can modify the state of many objects)
2046 stateObj
._syncOverAsync
= true;
2049 if (!TryProcessEnvChange(tokenLength
, stateObj
, out env
)) {
2053 for (int ii
= 0; ii
< env
.Length
; ii
++) {
2054 if (env
[ii
] != null && !this.Connection
.IgnoreEnvChange
) {
2055 switch (env
[ii
].type
) {
2056 case TdsEnums
.ENV_BEGINTRAN
:
2057 case TdsEnums
.ENV_ENLISTDTC
:
2058 // When we get notification from the server of a new
2059 // transaction, we move any pending transaction over to
2060 // the current transaction, then we store the token in it.
2061 // if there isn't a pending transaction, then it's either
2062 // a TSQL transaction or a distributed transaction.
2063 Debug
.Assert(null == _currentTransaction
, "non-null current transaction with an ENV Change");
2064 _currentTransaction
= _pendingTransaction
;
2065 _pendingTransaction
= null;
2067 if (null != _currentTransaction
) {
2068 _currentTransaction
.TransactionId
= env
[ii
].newLongValue
; // this is defined as a ULongLong in the server and in the TDS Spec.
2071 TransactionType transactionType
= (TdsEnums
.ENV_BEGINTRAN
== env
[ii
].type
) ? TransactionType
.LocalFromTSQL
: TransactionType
.Distributed
;
2072 _currentTransaction
= new SqlInternalTransaction(_connHandler
, transactionType
, null, env
[ii
].newLongValue
);
2074 if (null != _statistics
&& !_statisticsIsInTransaction
) {
2075 _statistics
.SafeIncrement(ref _statistics
._transactions
);
2077 _statisticsIsInTransaction
= true;
2078 _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
2080 case TdsEnums
.ENV_DEFECTDTC
:
2081 case TdsEnums
.ENV_TRANSACTIONENDED
:
2082 case TdsEnums
.ENV_COMMITTRAN
:
2084 // Must clear the retain id if the server-side transaction ends by anything other
2086 _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
2087 goto case TdsEnums
.ENV_ROLLBACKTRAN
;
2088 case TdsEnums
.ENV_ROLLBACKTRAN
:
2089 // When we get notification of a completed transaction
2090 // we null out the current transaction.
2091 if (null != _currentTransaction
) {
2093 // Check null for case where Begin and Rollback obtained in the same message.
2094 if (SqlInternalTransaction
.NullTransactionId
!= _currentTransaction
.TransactionId
) {
2095 Debug
.Assert(_currentTransaction
.TransactionId
!= env
[ii
].newLongValue
, "transaction id's are not equal!");
2099 if (TdsEnums
.ENV_COMMITTRAN
== env
[ii
].type
) {
2100 _currentTransaction
.Completed(TransactionState
.Committed
);
2102 else if (TdsEnums
.ENV_ROLLBACKTRAN
== env
[ii
].type
) {
2103 // Hold onto transaction id if distributed tran is rolled back. This must
2104 // be sent to the server on subsequent executions even though the transaction
2105 // is considered to be rolled back.
2106 if (_currentTransaction
.IsDistributed
&& _currentTransaction
.IsActive
) {
2107 _retainedTransactionId
= env
[ii
].oldLongValue
;
2109 _currentTransaction
.Completed(TransactionState
.Aborted
);
2113 _currentTransaction
.Completed(TransactionState
.Unknown
);
2115 _currentTransaction
= null;
2117 _statisticsIsInTransaction
= false;
2120 _connHandler
.OnEnvChange(env
[ii
]);
2127 case TdsEnums
.SQLLOGINACK
:
2129 Bid
.Trace("<sc.TdsParser.TryRun|SEC> Received login acknowledgement token\n");
2131 if (!TryProcessLoginAck(stateObj
, out ack
)) {
2135 _connHandler
.OnLoginAck(ack
);
2138 case TdsEnums
.SQLFEATUREEXTACK
:
2140 if (!TryProcessFeatureExtAck(stateObj
)) {
2145 case TdsEnums
.SQLFEDAUTHINFO
:
2147 _connHandler
._federatedAuthenticationInfoReceived
= true;
2148 SqlFedAuthInfo info
;
2149 Bid
.Trace("<sc.TdsParser.TryRun|SEC> Received federated authentication info token\n");
2150 if (!TryProcessFedAuthInfo(stateObj
, tokenLength
, out info
)) {
2153 _connHandler
.OnFedAuthInfo(info
);
2156 case TdsEnums
.SQLSESSIONSTATE
:
2158 if (!TryProcessSessionState(stateObj
, tokenLength
, _connHandler
._currentSessionData
)) {
2163 case TdsEnums
.SQLCOLMETADATA
:
2165 if (tokenLength
!= TdsEnums
.VARNULL
) {
2166 _SqlMetaDataSet metadata
;
2167 if (!TryProcessMetaData(tokenLength
, stateObj
, out metadata
,
2168 cmdHandler
!= null ? cmdHandler
.ColumnEncryptionSetting
: SqlCommandColumnEncryptionSetting
.UseConnectionSetting
)) {
2171 stateObj
._cleanupMetaData
= metadata
;
2174 if (cmdHandler
!= null) {
2175 stateObj
._cleanupMetaData
= cmdHandler
.MetaData
;
2179 if (null != dataStream
) {
2181 if (!stateObj
.TryPeekByte(out peekedToken
)) { // temporarily cache next byte
2185 if (!dataStream
.TrySetMetaData(stateObj
._cleanupMetaData
, (TdsEnums
.SQLTABNAME
== peekedToken
|| TdsEnums
.SQLCOLINFO
== peekedToken
))) {
2189 else if (null != bulkCopyHandler
) {
2190 bulkCopyHandler
.SetMetaData(stateObj
._cleanupMetaData
);
2194 case TdsEnums
.SQLROW
:
2195 case TdsEnums
.SQLNBCROW
:
2197 Debug
.Assert(stateObj
._cleanupMetaData
!= null, "Reading a row, but the metadata is null");
2199 if (token
== TdsEnums
.SQLNBCROW
) {
2200 if (!stateObj
.TryStartNewRow(isNullCompressed
: true, nullBitmapColumnsCount
: stateObj
._cleanupMetaData
.Length
)) {
2205 if (!stateObj
.TryStartNewRow(isNullCompressed
:false)) {
2210 if (null != bulkCopyHandler
) {
2212 if (!TryProcessRow(stateObj
._cleanupMetaData
, bulkCopyHandler
.CreateRowBuffer(), bulkCopyHandler
.CreateIndexMap(), stateObj
)) {
2216 else if (RunBehavior
.ReturnImmediately
!= (RunBehavior
.ReturnImmediately
& runBehavior
)) {
2217 if (!TrySkipRow(stateObj
._cleanupMetaData
, stateObj
)) { // skip rows
2225 if (_statistics
!= null) {
2226 _statistics
.WaitForDoneAfterRow
= true;
2230 case TdsEnums
.SQLRETURNSTATUS
:
2232 if (!stateObj
.TryReadInt32(out status
)) {
2235 if (cmdHandler
!= null) {
2236 cmdHandler
.OnReturnStatus(status
);
2239 case TdsEnums
.SQLRETURNVALUE
:
2241 SqlReturnValue returnValue
;
2242 if (!TryProcessReturnValue(tokenLength
, stateObj
, out returnValue
,
2243 cmdHandler
!= null ? cmdHandler
.ColumnEncryptionSetting
: SqlCommandColumnEncryptionSetting
.UseConnectionSetting
)) {
2246 if (cmdHandler
!= null) {
2247 cmdHandler
.OnReturnValue(returnValue
, stateObj
);
2251 case TdsEnums
.SQLSSPI
:
2253 // token length is length of SSPI data - call ProcessSSPI with it
2255 Debug
.Assert(stateObj
._syncOverAsync
, "ProcessSSPI does not support retry, do not attempt asynchronously");
2256 stateObj
._syncOverAsync
= true;
2258 ProcessSSPI(tokenLength
);
2261 case TdsEnums
.SQLTABNAME
:
2263 if (null != dataStream
) {
2264 MultiPartTableName
[] tableNames
;
2265 if (!TryProcessTableName(tokenLength
, stateObj
, out tableNames
)) {
2268 dataStream
.TableNames
= tableNames
;
2271 if (!stateObj
.TrySkipBytes(tokenLength
)) {
2279 Debug
.Assert(false, "Unhandled token: " + token
.ToString(CultureInfo
.InvariantCulture
));
2283 Debug
.Assert(stateObj
._pendingData
|| !dataReady
, "dataReady is set, but there is no pending data");
2286 // Loop while data pending & runbehavior not return immediately, OR
2287 // if in attention case, loop while no more pending data & attention has not yet been
2289 while ((stateObj
._pendingData
&&
2290 (RunBehavior
.ReturnImmediately
!= (RunBehavior
.ReturnImmediately
& runBehavior
))) ||
2291 (!stateObj
._pendingData
&& stateObj
._attentionSent
&& !stateObj
._attentionReceived
));
2294 if ((stateObj
._pendingData
) && (!dataReady
)) {
2296 if (!stateObj
.TryPeekByte(out token
)) {
2299 Debug
.Assert(IsValidTdsToken(token
), string.Format("DataReady is false, but next token is not valid: {0,-2:X2}", token
));
2303 if (!stateObj
._pendingData
) {
2304 if (null != CurrentTransaction
) {
2305 CurrentTransaction
.Activate();
2309 // if we recieved an attention (but this thread didn't send it) then
2310 // we throw an Operation Cancelled error
2311 if (stateObj
._attentionReceived
) {
2312 // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close
2313 // Spin until SendAttention has cleared _attentionSending, this prevents a race condition between receiving the attention ACK and setting _attentionSent
2314 SpinWait
.SpinUntil(() => !stateObj
._attentionSending
);
2316 Debug
.Assert(stateObj
._attentionSent
, "Attention ACK has been received without attention sent");
2317 if (stateObj
._attentionSent
) {
2318 // Reset attention state.
2319 stateObj
._attentionSent
= false;
2320 stateObj
._attentionReceived
= false;
2322 if (RunBehavior
.Clean
!= (RunBehavior
.Clean
& runBehavior
) && !stateObj
._internalTimeout
) {
2323 // Add attention error to collection - if not RunBehavior.Clean!
2324 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.MIN_ERROR_CLASS
, _server
, SQLMessage
.OperationCancelled(), "", 0));
2329 if (stateObj
.HasErrorOrWarning
) {
2330 ThrowExceptionAndWarning(stateObj
);
2335 private bool TryProcessEnvChange(int tokenLength
, TdsParserStateObject stateObj
, out SqlEnvChange
[] sqlEnvChange
) {
2336 // There could be multiple environment change messages following this token.
2338 int processedLength
= 0;
2340 SqlEnvChange
[] envarray
= new SqlEnvChange
[3]; // Why is this hardcoded to 3?
2342 sqlEnvChange
= null;
2344 while (tokenLength
> processedLength
) {
2346 if (nvalues
>= envarray
.Length
) {
2347 // This is a rare path. Most of the time we will have 1 or 2 envchange data streams.
2348 SqlEnvChange
[] newenvarray
= new SqlEnvChange
[envarray
.Length
+ 3];
2350 for (int ii
= 0; ii
< envarray
.Length
; ii
++)
2351 newenvarray
[ii
] = envarray
[ii
];
2353 envarray
= newenvarray
;
2356 SqlEnvChange env
= new SqlEnvChange();
2358 if (!stateObj
.TryReadByte(out env
.type
)) {
2362 envarray
[nvalues
] = env
;
2366 case TdsEnums
.ENV_DATABASE
:
2367 case TdsEnums
.ENV_LANG
:
2368 if (!TryReadTwoStringFields(env
, stateObj
)) {
2373 case TdsEnums
.ENV_CHARSET
:
2374 // we copied this behavior directly from luxor - see charset envchange
2375 // section from sqlctokn.c
2376 Debug
.Assert(!_isShiloh
, "Received ENV_CHARSET on non 7.0 server!");
2377 if (!TryReadTwoStringFields(env
, stateObj
)) {
2380 if (env
.newValue
== TdsEnums
.DEFAULT_ENGLISH_CODE_PAGE_STRING
) {
2381 _defaultCodePage
= TdsEnums
.DEFAULT_ENGLISH_CODE_PAGE_VALUE
;
2382 _defaultEncoding
= System
.Text
.Encoding
.GetEncoding(_defaultCodePage
);
2385 Debug
.Assert(env
.newValue
.Length
> TdsEnums
.CHARSET_CODE_PAGE_OFFSET
, "TdsParser.ProcessEnvChange(): charset value received with length <=10");
2387 string stringCodePage
= env
.newValue
.Substring(TdsEnums
.CHARSET_CODE_PAGE_OFFSET
);
2389 _defaultCodePage
= Int32
.Parse(stringCodePage
, NumberStyles
.Integer
, CultureInfo
.InvariantCulture
);
2390 _defaultEncoding
= System
.Text
.Encoding
.GetEncoding(_defaultCodePage
);
2395 case TdsEnums
.ENV_PACKETSIZE
:
2396 // take care of packet size right here
2397 Debug
.Assert(stateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
2398 if (!TryReadTwoStringFields(env
, stateObj
)) {
2399 // Changing packet size does not support retry, should not pend"
2400 throw SQL
.SynchronousCallMayNotPend();
2402 // Only set on physical state object - this should only occur on LoginAck prior
2403 // to MARS initialization!
2404 Int32 packetSize
= Int32
.Parse(env
.newValue
, NumberStyles
.Integer
, CultureInfo
.InvariantCulture
);
2406 if (_physicalStateObj
.SetPacketSize(packetSize
)) {
2407 // If packet size changed, we need to release our SNIPackets since
2408 // those are tied to packet size of connection.
2409 _physicalStateObj
.ClearAllWritePackets();
2411 // Update SNI ConsumerInfo value to be resulting packet size
2412 UInt32 unsignedPacketSize
= (UInt32
) packetSize
;
2413 UInt32 result
= SNINativeMethodWrapper
.SNISetInfo(_physicalStateObj
.Handle
, SNINativeMethodWrapper
.QTypes
.SNI_QUERY_CONN_BUFSIZE
, ref unsignedPacketSize
);
2415 Debug
.Assert(result
== TdsEnums
.SNI_SUCCESS
, "Unexpected failure state upon calling SNISetInfo");
2420 case TdsEnums
.ENV_LOCALEID
:
2423 if (!TryReadTwoStringFields(env
, stateObj
)) {
2426 _defaultLCID
= Int32
.Parse(env
.newValue
, NumberStyles
.Integer
, CultureInfo
.InvariantCulture
);
2429 case TdsEnums
.ENV_COMPFLAGS
:
2430 if (!TryReadTwoStringFields(env
, stateObj
)) {
2435 case TdsEnums
.ENV_COLLATION
:
2436 Debug
.Assert(env
.newLength
== 5 || env
.newLength
== 0, "Improper length in new collation!");
2437 if (!stateObj
.TryReadByte(out byteLength
)) {
2440 env
.newLength
= byteLength
;
2441 if (env
.newLength
== 5) {
2442 if (!TryProcessCollation(stateObj
, out env
.newCollation
)) {
2446 // give the parser the new collation values in case parameters don't specify one
2447 _defaultCollation
= env
.newCollation
;
2448 int newCodePage
= GetCodePage(env
.newCollation
, stateObj
);
2449 if (newCodePage
!= _defaultCodePage
) {
2450 _defaultCodePage
= newCodePage
;
2451 _defaultEncoding
= System
.Text
.Encoding
.GetEncoding(_defaultCodePage
);
2453 _defaultLCID
= env
.newCollation
.LCID
;
2456 if (!stateObj
.TryReadByte(out byteLength
)) {
2459 env
.oldLength
= byteLength
;
2460 Debug
.Assert(env
.oldLength
== 5 || env
.oldLength
== 0, "Improper length in old collation!");
2461 if (env
.oldLength
== 5) {
2462 if (!TryProcessCollation(stateObj
, out env
.oldCollation
)) {
2467 env
.length
= 3 + env
.newLength
+ env
.oldLength
;
2470 case TdsEnums
.ENV_BEGINTRAN
:
2471 case TdsEnums
.ENV_COMMITTRAN
:
2472 case TdsEnums
.ENV_ROLLBACKTRAN
:
2473 case TdsEnums
.ENV_ENLISTDTC
:
2474 case TdsEnums
.ENV_DEFECTDTC
:
2475 case TdsEnums
.ENV_TRANSACTIONENDED
:
2476 Debug
.Assert(_isYukon
, "Received new ENVCHANGE transaction/DTC token on pre 9.0 server!");
2478 if (!stateObj
.TryReadByte(out byteLength
)) {
2481 env
.newLength
= byteLength
;
2482 Debug
.Assert(env
.newLength
== 0 || env
.newLength
== 8, "Improper length for new transaction id!");
2484 if (env
.newLength
> 0) {
2485 if (!stateObj
.TryReadInt64(out env
.newLongValue
)) {
2488 Debug
.Assert(env
.newLongValue
!= SqlInternalTransaction
.NullTransactionId
, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
2491 env
.newLongValue
= SqlInternalTransaction
.NullTransactionId
; // the server guarantees that zero is an invalid transaction id.
2494 if (!stateObj
.TryReadByte(out byteLength
)) {
2497 env
.oldLength
= byteLength
;
2498 Debug
.Assert(env
.oldLength
== 0 || env
.oldLength
== 8, "Improper length for old transaction id!");
2500 if (env
.oldLength
> 0) {
2501 if (!stateObj
.TryReadInt64(out env
.oldLongValue
)) {
2504 Debug
.Assert(env
.oldLongValue
!= SqlInternalTransaction
.NullTransactionId
, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
2507 env
.oldLongValue
= SqlInternalTransaction
.NullTransactionId
; // the server guarantees that zero is an invalid transaction id.
2510 // env.length includes 1 byte type token
2511 env
.length
= 3 + env
.newLength
+ env
.oldLength
;
2514 case TdsEnums
.ENV_LOGSHIPNODE
:
2515 // env.newBinValue is secondary node, env.oldBinValue is witness node
2516 // comes before LoginAck so we can't assert this
2517 if (!TryReadTwoStringFields(env
, stateObj
)) {
2522 case TdsEnums
.ENV_PROMOTETRANSACTION
:
2523 Debug
.Assert(_isYukon
, "Received new ENVCHANGE tokens on pre 9.0 server!");
2525 if (!stateObj
.TryReadInt32(out env
.newLength
)) { // new value has 4 byte length
2528 env
.newBinValue
= new byte[env
.newLength
];
2529 if (!stateObj
.TryReadByteArray(env
.newBinValue
, 0, env
.newLength
)) { // read new value with 4 byte length
2533 if (!stateObj
.TryReadByte(out byteLength
)) {
2536 env
.oldLength
= byteLength
;
2537 Debug
.Assert(0 == env
.oldLength
, "old length should be zero");
2539 // env.length includes 1 byte for type token
2540 env
.length
= 5 + env
.newLength
;
2543 case TdsEnums
.ENV_TRANSACTIONMANAGERADDRESS
:
2544 case TdsEnums
.ENV_SPRESETCONNECTIONACK
:
2546 Debug
.Assert(_isYukon
, "Received new ENVCHANGE tokens on pre 9.0 server!");
2547 if (!TryReadTwoBinaryFields(env
, stateObj
)) {
2552 case TdsEnums
.ENV_USERINSTANCE
:
2553 Debug
.Assert(!_isYukon
, "Received ENV_USERINSTANCE on non 9.0 server!");
2554 if (!TryReadTwoStringFields(env
, stateObj
)) {
2559 case TdsEnums
.ENV_ROUTING
:
2561 if (!stateObj
.TryReadUInt16(out newLength
)) {
2564 env
.newLength
= newLength
;
2566 if (!stateObj
.TryReadByte(out protocol
)) {
2570 if (!stateObj
.TryReadUInt16(out port
)) {
2574 if (!stateObj
.TryReadUInt16(out serverLen
)) {
2578 if (!stateObj
.TryReadString(serverLen
, out serverName
)) {
2581 env
.newRoutingInfo
= new RoutingInfo(protocol
, port
, serverName
);
2583 if (!stateObj
.TryReadUInt16(out oldLength
)) {
2586 if (!stateObj
.TrySkipBytes(oldLength
)) {
2589 env
.length
= env
.newLength
+ oldLength
+ 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
2593 Debug
.Assert(false, "Unknown environment change token: " + env
.type
);
2596 processedLength
+= env
.length
;
2599 sqlEnvChange
= envarray
;
2603 private bool TryReadTwoBinaryFields(SqlEnvChange env
, TdsParserStateObject stateObj
) {
2604 // Used by ProcessEnvChangeToken
2606 if (!stateObj
.TryReadByte(out byteLength
)) {
2609 env
.newLength
= byteLength
;
2610 env
.newBinValue
= new byte[env
.newLength
];
2611 if (!stateObj
.TryReadByteArray(env
.newBinValue
, 0, env
.newLength
)) {
2614 if (!stateObj
.TryReadByte(out byteLength
)) {
2617 env
.oldLength
= byteLength
;
2618 env
.oldBinValue
= new byte[env
.oldLength
];
2619 if (!stateObj
.TryReadByteArray(env
.oldBinValue
, 0, env
.oldLength
)) {
2623 // env.length includes 1 byte type token
2624 env
.length
= 3 + env
.newLength
+ env
.oldLength
;
2628 private bool TryReadTwoStringFields(SqlEnvChange env
, TdsParserStateObject stateObj
) {
2629 // Used by ProcessEnvChangeToken
2630 byte newLength
, oldLength
;
2631 string newValue
, oldValue
;
2632 if (!stateObj
.TryReadByte(out newLength
)) {
2635 if (!stateObj
.TryReadString(newLength
, out newValue
)) {
2638 if (!stateObj
.TryReadByte(out oldLength
)) {
2641 if (!stateObj
.TryReadString(oldLength
, out oldValue
)) {
2645 env
.newLength
= newLength
;
2646 env
.newValue
= newValue
;
2647 env
.oldLength
= oldLength
;
2648 env
.oldValue
= oldValue
;
2650 // env.length includes 1 byte type token
2651 env
.length
= 3 + env
.newLength
* 2 + env
.oldLength
* 2;
2655 private bool TryProcessDone(SqlCommand cmd
, SqlDataReader reader
, ref RunBehavior run
, TdsParserStateObject stateObj
) {
2660 // Can't retry TryProcessDone
2661 stateObj
._syncOverAsync
= true;
2665 // rowcount (valid only if DONE_COUNT bit is set)
2667 if (!stateObj
.TryReadUInt16(out status
)) {
2670 if (!stateObj
.TryReadUInt16(out curCmd
)) {
2676 if (!stateObj
.TryReadInt64(out longCount
)) {
2679 count
= (int) longCount
;
2682 if (!stateObj
.TryReadInt32(out count
)) {
2685 // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
2686 // In that case we still have to read another 4 bytes
2687 // But don't try to read beyond the TDS stream in this case, because it generates errors if login failed.
2688 if ( _state
== TdsParserState
.OpenNotLoggedIn
) {
2689 // Login incomplete, if we are reading from Yukon we need to read another int
2690 if (stateObj
._inBytesRead
> stateObj
._inBytesUsed
) {
2692 if (!stateObj
.TryPeekByte(out b
)) {
2696 // This is an invalid token value
2697 if (!stateObj
.TryReadInt32(out count
)) {
2705 // We get a done token with the attention bit set
2706 if (TdsEnums
.DONE_ATTN
== (status
& TdsEnums
.DONE_ATTN
)) {
2707 Debug
.Assert(TdsEnums
.DONE_MORE
!= (status
& TdsEnums
.DONE_MORE
),"Not expecting DONE_MORE when receiving DONE_ATTN");
2708 Debug
.Assert(stateObj
._attentionSent
, "Received attention done without sending one!");
2709 stateObj
._attentionReceived
= true;
2710 Debug
.Assert(stateObj
._inBytesUsed
== stateObj
._inBytesRead
&& stateObj
._inBytesPacket
== 0, "DONE_ATTN received with more data left on wire");
2712 if ((null != cmd
) && (TdsEnums
.DONE_COUNT
== (status
& TdsEnums
.DONE_COUNT
))) {
2713 if (curCmd
!= TdsEnums
.SELECT
) {
2714 if (cmd
.IsDescribeParameterEncryptionRPCCurrentlyInProgress
) {
2715 // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
2716 cmd
.RowsAffectedByDescribeParameterEncryption
= count
;
2719 cmd
.InternalRecordsAffected
= count
;
2722 // Skip the bogus DONE counts sent by the server
2723 if (stateObj
._receivedColMetaData
|| (curCmd
!= TdsEnums
.SELECT
)) {
2724 cmd
.OnStatementCompleted(count
);
2728 stateObj
._receivedColMetaData
= false;
2730 // Surface exception for DONE_ERROR in the case we did not receive an error token
2731 // in the stream, but an error occurred. In these cases, we throw a general server error. The
2732 // situations where this can occur are: an invalid buffer received from client, login error
2733 // and the server refused our connection, and the case where we are trying to log in but
2734 // the server has reached its max connection limit. Bottom line, we need to throw general
2735 // error in the cases where we did not receive a error token along with the DONE_ERROR.
2736 if ((TdsEnums
.DONE_ERROR
== (TdsEnums
.DONE_ERROR
& status
)) && stateObj
.ErrorCount
== 0 &&
2737 stateObj
._errorTokenReceived
== false && (RunBehavior
.Clean
!= (RunBehavior
.Clean
& run
))) {
2738 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.MIN_ERROR_CLASS
, _server
, SQLMessage
.SevereError(), "", 0));
2740 if (null != reader
) { // SQL BU DT 269516
2741 if (!reader
.IsInitialized
) {
2742 run
= RunBehavior
.UntilDone
;
2747 // Similar to above, only with a more severe error. In this case, if we received
2748 // the done_srverror, this exception will be added to the collection regardless.
2749 // MDAC #93896. Also, per Ashwin, the server will always break the connection in this case.
2750 if ((TdsEnums
.DONE_SRVERROR
== (TdsEnums
.DONE_SRVERROR
& status
)) && (RunBehavior
.Clean
!= (RunBehavior
.Clean
& run
))) {
2751 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.SevereError(), "", 0));
2753 if (null != reader
) { // SQL BU DT 269516
2754 if (!reader
.IsInitialized
) {
2755 run
= RunBehavior
.UntilDone
;
2760 ProcessSqlStatistics(curCmd
, status
, count
);
2762 // stop if the DONE_MORE bit isn't set (see above for attention handling)
2763 if (TdsEnums
.DONE_MORE
!= (status
& TdsEnums
.DONE_MORE
)) {
2764 stateObj
._errorTokenReceived
= false;
2765 if (stateObj
._inBytesUsed
>= stateObj
._inBytesRead
) {
2766 stateObj
._pendingData
= false;
2770 // _pendingData set by e.g. 'TdsExecuteSQLBatch'
2771 // _hasOpenResult always set to true by 'WriteMarsHeader'
2773 if (!stateObj
._pendingData
&& stateObj
._hasOpenResult
) {
2775 Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) ||
2776 (_userStartedLocalTransaction != null && _distributedTransaction != null))
2777 , "ProcessDone - have both distributed and local transactions not null!");
2778 */ // WebData 112722
2780 stateObj
.DecrementOpenResultCount();
2786 private void ProcessSqlStatistics(ushort curCmd
, ushort status
, int count
) {
2787 // SqlStatistics bookkeeping stuff
2789 if (null != _statistics
) {
2790 // any done after row(s) counts as a resultset
2791 if (_statistics
.WaitForDoneAfterRow
) {
2792 _statistics
.SafeIncrement(ref _statistics
._sumResultSets
);
2793 _statistics
.WaitForDoneAfterRow
= false;
2796 // clear row count DONE_COUNT flag is not set
2797 if (!(TdsEnums
.DONE_COUNT
== (status
& TdsEnums
.DONE_COUNT
))) {
2802 case TdsEnums
.INSERT
:
2803 case TdsEnums
.DELETE
:
2804 case TdsEnums
.UPDATE
:
2805 case TdsEnums
.MERGE
:
2806 _statistics
.SafeIncrement(ref _statistics
._iduCount
);
2807 _statistics
.SafeAdd(ref _statistics
._iduRows
, count
);
2808 if (!_statisticsIsInTransaction
) {
2809 _statistics
.SafeIncrement(ref _statistics
._transactions
);
2814 case TdsEnums
.SELECT
:
2815 _statistics
.SafeIncrement(ref _statistics
._selectCount
);
2816 _statistics
.SafeAdd(ref _statistics
._selectRows
, count
);
2819 case TdsEnums
.BEGINXACT
:
2820 if (!_statisticsIsInTransaction
) {
2821 _statistics
.SafeIncrement(ref _statistics
._transactions
);
2823 _statisticsIsInTransaction
= true;
2826 case TdsEnums
.OPENCURSOR
:
2827 _statistics
.SafeIncrement(ref _statistics
._cursorOpens
);
2830 case TdsEnums
.ABORT
:
2831 _statisticsIsInTransaction
= false;
2834 case TdsEnums
.ENDXACT
:
2835 _statisticsIsInTransaction
= false;
2841 case TdsEnums
.BEGINXACT
:
2842 _statisticsIsInTransaction
= true;
2845 case TdsEnums
.ABORT
:
2846 case TdsEnums
.ENDXACT
:
2847 _statisticsIsInTransaction
= false;
2853 private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj
) {
2857 if (!stateObj
.TryReadByte(out featureId
)) {
2860 if (featureId
!= TdsEnums
.FEATUREEXT_TERMINATOR
) {
2862 if (!stateObj
.TryReadUInt32(out dataLen
)) {
2865 byte[] data
= new byte[dataLen
];
2867 if (!stateObj
.TryReadByteArray(data
, 0, checked ((int)dataLen
))) {
2871 _connHandler
.OnFeatureExtAck(featureId
, data
);
2873 } while (featureId
!= TdsEnums
.FEATUREEXT_TERMINATOR
);
2875 // Check if column encryption was on and feature wasn't acknowledged.
2876 if (_connHandler
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
&& !IsColumnEncryptionSupported
) {
2877 throw SQL
.TceNotSupported ();
2883 private bool TryProcessSessionState(TdsParserStateObject stateObj
, int length
, SessionData sdata
) {
2885 throw SQL
.ParsingErrorLength(ParsingErrorState
.SessionStateLengthTooShort
, length
);
2888 if (!stateObj
.TryReadUInt32(out seqNum
)) {
2891 if (seqNum
== UInt32
.MaxValue
) {
2892 _connHandler
.DoNotPoolThisConnection();
2895 if (!stateObj
.TryReadByte(out status
)) {
2899 throw SQL
.ParsingErrorStatus(ParsingErrorState
.SessionStateInvalidStatus
, status
);
2901 bool recoverable
= status
!= 0;
2903 while (length
> 0) {
2905 if (!stateObj
.TryReadByte(out stateId
)) {
2910 if (!stateObj
.TryReadByte(out stateLenByte
)) {
2913 if (stateLenByte
< 0xFF) {
2914 stateLen
= stateLenByte
;
2917 if (!stateObj
.TryReadInt32(out stateLen
)) {
2921 byte[] buffer
= null;
2922 lock (sdata
._delta
) {
2923 if (sdata
._delta
[stateId
] == null) {
2924 buffer
= new byte[stateLen
];
2925 sdata
._delta
[stateId
] = new SessionStateRecord { _version = seqNum, _dataLength = stateLen, _data = buffer, _recoverable = recoverable }
;
2926 sdata
._deltaDirty
= true;
2928 checked { sdata._unrecoverableStatesCount++; }
2932 if (sdata
._delta
[stateId
]._version
<= seqNum
) {
2933 SessionStateRecord sv
= sdata
._delta
[stateId
];
2934 sv
._version
= seqNum
;
2935 sv
._dataLength
= stateLen
;
2936 if (sv
._recoverable
!= recoverable
) {
2938 Debug
.Assert(sdata
._unrecoverableStatesCount
> 0, "Unrecoverable states count >0");
2939 sdata
._unrecoverableStatesCount
--;
2942 checked { sdata._unrecoverableStatesCount++; }
2944 sv
._recoverable
= recoverable
;
2947 if (buffer
.Length
< stateLen
) {
2948 buffer
= new byte[stateLen
];
2954 if (buffer
!= null) {
2955 if (!stateObj
.TryReadByteArray(buffer
, 0, stateLen
)) {
2960 if (!stateObj
.TrySkipBytes(stateLen
))
2964 if (stateLenByte
< 0xFF) {
2965 length
-= 2 + stateLen
;
2968 length
-= 6 + stateLen
;
2971 sdata
.AssertUnrecoverableStateCountIsCorrect();
2976 private bool TryProcessLoginAck(TdsParserStateObject stateObj
, out SqlLoginAck sqlLoginAck
) {
2977 SqlLoginAck a
= new SqlLoginAck();
2981 // read past interface type and version
2982 if (!stateObj
.TrySkipBytes(1)) {
2986 byte[] b
= new byte[TdsEnums
.VERSION_SIZE
];
2987 if (!stateObj
.TryReadByteArray(b
, 0, b
.Length
)) {
2990 a
.tdsVersion
= (UInt32
)((((((b
[0]<<8)|b
[1])<<8)|b
[2])<<8)|b
[3]); // bytes are in motorola order (high byte first)
2991 UInt32 majorMinor
= a
.tdsVersion
& 0xff00ffff;
2992 UInt32 increment
= (a
.tdsVersion
>> 16) & 0xff;
2995 // 0x07000000 -> Sphinx // Notice server response format is different for bwd compat
2996 // 0x07010000 -> Shiloh RTM // Notice server response format is different for bwd compat
2997 // 0x71000001 -> Shiloh SP1
2998 // 0x72xx0002 -> Yukon RTM
2999 // information provided by S. Ashwin
3001 switch (majorMinor
) {
3002 case TdsEnums
.SPHINXORSHILOH_MAJOR
<<24|TdsEnums
.DEFAULT_MINOR
: // Sphinx & Shiloh RTM
3003 // note that sphinx and shiloh_rtm can only be distinguished by the increment
3004 switch (increment
) {
3005 case TdsEnums
.SHILOH_INCREMENT
:
3008 case TdsEnums
.SPHINX_INCREMENT
:
3009 // no flag will be set
3012 throw SQL
.InvalidTDSVersion();
3015 case TdsEnums
.SHILOHSP1_MAJOR
<<24|TdsEnums
.SHILOHSP1_MINOR
: // Shiloh SP1
3016 if (increment
!= TdsEnums
.SHILOHSP1_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3017 _isShilohSP1
= true;
3019 case TdsEnums
.YUKON_MAJOR
<<24|TdsEnums
.YUKON_RTM_MINOR
: // Yukon
3020 if (increment
!= TdsEnums
.YUKON_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3023 case TdsEnums
.KATMAI_MAJOR
<<24|TdsEnums
.KATMAI_MINOR
:
3024 if (increment
!= TdsEnums
.KATMAI_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3027 case TdsEnums
.DENALI_MAJOR
<< 24|TdsEnums
.DENALI_MINOR
:
3028 if (increment
!= TdsEnums
.DENALI_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3032 throw SQL
.InvalidTDSVersion();
3035 _isKatmai
|= _isDenali
;
3036 _isYukon
|= _isKatmai
;
3037 _isShilohSP1
|= _isYukon
; // includes all lower versions
3038 _isShiloh
|= _isShilohSP1
; //
3040 a
.isVersion8
= _isShiloh
;
3042 stateObj
._outBytesUsed
= stateObj
._outputHeaderLen
;
3044 if (!stateObj
.TryReadByte(out len
)) {
3048 if (!stateObj
.TryReadString(len
, out a
.programName
)) {
3051 if (!stateObj
.TryReadByte(out a
.majorVersion
)) {
3054 if (!stateObj
.TryReadByte(out a
.minorVersion
)) {
3057 byte buildNumHi
, buildNumLo
;
3058 if (!stateObj
.TryReadByte(out buildNumHi
)) {
3061 if (!stateObj
.TryReadByte(out buildNumLo
)) {
3065 a
.buildNum
= (short)((buildNumHi
<< 8) + buildNumLo
);
3067 Debug
.Assert(_state
== TdsParserState
.OpenNotLoggedIn
, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn");
3068 _state
= TdsParserState
.OpenLoggedIn
;
3072 _resetConnectionEvent
= new AutoResetEvent(true);
3076 // Fail if SSE UserInstance and we have not received this info.
3077 if ( _connHandler
.ConnectionOptions
.UserInstance
&&
3078 ADP
.IsEmpty(_connHandler
.InstanceName
)) {
3079 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.FATAL_ERROR_CLASS
, Server
, SQLMessage
.UserInstanceFailure(), "", 0));
3080 ThrowExceptionAndWarning(stateObj
);
3087 private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj
, int tokenLen
, out SqlFedAuthInfo sqlFedAuthInfo
) {
3088 sqlFedAuthInfo
= null;
3089 SqlFedAuthInfo tempFedAuthInfo
= new SqlFedAuthInfo();
3091 // Skip reading token length, since it has already been read in caller
3093 if (Bid
.AdvancedOn
) {
3094 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FEDAUTHINFO token stream length = {0}\n", tokenLen
);
3097 if (tokenLen
< sizeof(uint)) {
3098 // the token must at least contain a DWORD indicating the number of info IDs
3099 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream length too short for CountOfInfoIDs.\n");
3100 throw SQL
.ParsingErrorLength(ParsingErrorState
.FedAuthInfoLengthTooShortForCountOfInfoIds
, tokenLen
);
3103 // read how many FedAuthInfo options there are
3105 if (!stateObj
.TryReadUInt32(out optionsCount
)) {
3106 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read CountOfInfoIDs in FEDAUTHINFO token stream.\n");
3107 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoFailedToReadCountOfInfoIds
);
3109 tokenLen
-= sizeof(uint); // remaining length is shortened since we read optCount
3111 if (Bid
.AdvancedOn
) {
3112 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> CountOfInfoIDs = {0}\n", optionsCount
.ToString(CultureInfo
.InvariantCulture
));
3116 // read the rest of the token
3117 byte[] tokenData
= new byte[tokenLen
];
3119 bool successfulRead
= stateObj
.TryReadByteArray(tokenData
, 0, tokenLen
, out totalRead
);
3121 if (Bid
.AdvancedOn
) {
3122 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Read rest of FEDAUTHINFO token stream: {0}\n", BitConverter
.ToString(tokenData
, 0, totalRead
));
3125 if (!successfulRead
|| totalRead
!= tokenLen
) {
3126 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read FEDAUTHINFO token stream. Attempted to read {0} bytes, actually read {1}\n", tokenLen
, totalRead
);
3127 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoFailedToReadTokenStream
);
3130 // each FedAuthInfoOpt is 9 bytes:
3131 // 1 byte for FedAuthInfoID
3132 // 4 bytes for FedAuthInfoDataLen
3133 // 4 bytes for FedAuthInfoDataOffset
3134 // So this is the index in tokenData for the i-th option
3135 const uint optionSize
= 9;
3137 // the total number of bytes for all FedAuthInfoOpts together
3138 uint totalOptionsSize
= checked(optionsCount
* optionSize
);
3140 for (uint i
= 0; i
< optionsCount
; i
++) {
3141 uint currentOptionOffset
= checked(i
* optionSize
);
3143 byte id
= tokenData
[currentOptionOffset
];
3144 uint dataLen
= BitConverter
.ToUInt32(tokenData
, checked((int)(currentOptionOffset
+ 1)));
3145 uint dataOffset
= BitConverter
.ToUInt32(tokenData
, checked((int)(currentOptionOffset
+ 5)));
3147 if (Bid
.AdvancedOn
) {
3148 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}\n", id
, dataLen
.ToString(CultureInfo
.InvariantCulture
), dataOffset
.ToString(CultureInfo
.InvariantCulture
));
3151 // offset is measured from optCount, so subtract to make offset measured
3152 // from the beginning of tokenData
3154 dataOffset
-= sizeof(uint);
3157 // if dataOffset points to a region within FedAuthInfoOpt or after the end of the token, throw
3158 if (dataOffset
< totalOptionsSize
|| dataOffset
>= tokenLen
) {
3159 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FedAuthInfoDataOffset points to an invalid location.\n");
3160 throw SQL
.ParsingErrorOffset(ParsingErrorState
.FedAuthInfoInvalidOffset
, unchecked((int)dataOffset
));
3163 // try to read data and throw if the arguments are bad, meaning the server sent us a bad token
3166 data
= System
.Text
.Encoding
.Unicode
.GetString(tokenData
, checked((int)dataOffset
), checked((int)dataLen
));
3168 catch (ArgumentOutOfRangeException e
) {
3169 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read FedAuthInfoData.\n");
3170 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoFailedToReadData
, e
);
3172 catch (ArgumentException e
) {
3173 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FedAuthInfoData is not in unicode format.\n");
3174 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoDataNotUnicode
, e
);
3177 if (Bid
.AdvancedOn
) {
3178 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoData: {0}\n", data
);
3181 // store data in tempFedAuthInfo
3182 switch ((TdsEnums
.FedAuthInfoId
)id
) {
3183 case TdsEnums
.FedAuthInfoId
.Spn
:
3184 tempFedAuthInfo
.spn
= data
;
3186 case TdsEnums
.FedAuthInfoId
.Stsurl
:
3187 tempFedAuthInfo
.stsurl
= data
;
3190 if (Bid
.AdvancedOn
) {
3191 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Ignoring unknown federated authentication info option: {0}\n", id
);
3198 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream is not long enough to contain the data it claims to.\n");
3199 throw SQL
.ParsingErrorLength(ParsingErrorState
.FedAuthInfoLengthTooShortForData
, tokenLen
);
3202 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Processed FEDAUTHINFO token stream: {0}\n", tempFedAuthInfo
.ToString());
3204 if (String
.IsNullOrWhiteSpace(tempFedAuthInfo
.stsurl
) || String
.IsNullOrWhiteSpace(tempFedAuthInfo
.spn
)) {
3205 // We should be receiving both stsurl and spn
3206 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream does not contain both STSURL and SPN.\n");
3207 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoDoesNotContainStsurlAndSpn
);
3210 sqlFedAuthInfo
= tempFedAuthInfo
;
3214 internal bool TryProcessError(byte token
, TdsParserStateObject stateObj
, out SqlError error
) {
3223 if (!stateObj
.TryReadInt32(out number
)) {
3226 if (!stateObj
.TryReadByte(out state
)) {
3229 if (!stateObj
.TryReadByte(out errorClass
)) {
3233 Debug
.Assert(((errorClass
>= TdsEnums
.MIN_ERROR_CLASS
) && token
== TdsEnums
.SQLERROR
) ||
3234 ((errorClass
< TdsEnums
.MIN_ERROR_CLASS
) && token
== TdsEnums
.SQLINFO
), "class and token don't match!");
3236 if (!stateObj
.TryReadUInt16(out shortLen
)) {
3240 if (!stateObj
.TryReadString(shortLen
, out message
)) {
3244 if (!stateObj
.TryReadByte(out byteLen
)) {
3250 // MDAC bug #49307 - server sometimes does not send over server field! In those cases
3251 // we will use our locally cached value.
3256 if (!stateObj
.TryReadString(byteLen
, out server
)) {
3261 if (!stateObj
.TryReadByte(out byteLen
)) {
3265 if (!stateObj
.TryReadString(byteLen
, out procedure
)) {
3271 if (!stateObj
.TryReadInt32(out line
)) {
3277 if (!stateObj
.TryReadUInt16(out shortLine
)) {
3281 // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
3282 // In that case we still have to read another 2 bytes
3283 if ( _state
== TdsParserState
.OpenNotLoggedIn
) {
3286 if (!stateObj
.TryPeekByte(out b
)) {
3290 // This is an invalid token value
3292 if (!stateObj
.TryReadUInt16(out value)) {
3295 line
= (line
<< 16) + value;
3300 error
= new SqlError(number
, state
, errorClass
, _server
, message
, procedure
, line
);
3305 internal bool TryProcessReturnValue(int length
,
3306 TdsParserStateObject stateObj
,
3307 out SqlReturnValue returnValue
,
3308 SqlCommandColumnEncryptionSetting columnEncryptionSetting
) {
3310 SqlReturnValue rec
= new SqlReturnValue();
3311 rec
.length
= length
; // In Yukon this length is -1
3313 if (!stateObj
.TryReadUInt16(out rec
.parmIndex
)) {
3318 if (!stateObj
.TryReadByte(out len
)) { // Length of parameter name
3322 rec
.parameter
= null;
3324 if (!stateObj
.TryReadString(len
, out rec
.parameter
)) {
3329 // read status and ignore
3331 if (!stateObj
.TryReadByte(out ignored
)) {
3337 // read user type - 4 bytes Yukon, 2 backwards
3338 if (IsYukonOrNewer
) {
3339 if (!stateObj
.TryReadUInt32(out userType
)) {
3344 ushort userTypeShort
;
3345 if (!stateObj
.TryReadUInt16(out userTypeShort
)) {
3348 userType
= userTypeShort
;
3351 // Read off the flags.
3352 // The first byte is ignored since it doesn't contain any interesting information.
3354 if (!stateObj
.TryReadByte(out flags
)) {
3358 if (!stateObj
.TryReadByte(out flags
)) {
3362 // Check if the column is encrypted.
3363 if (_serverSupportsColumnEncryption
) {
3364 rec
.isEncrypted
= (TdsEnums
.IsEncrypted
== (flags
& TdsEnums
.IsEncrypted
));
3369 if (!stateObj
.TryReadByte(out tdsType
)) {
3374 // For xml datatpyes, there is no tokenLength
3377 if (tdsType
== TdsEnums
.SQLXMLTYPE
) {
3378 tdsLen
= TdsEnums
.SQL_USHORTVARMAXLEN
;
3380 else if (IsVarTimeTds(tdsType
))
3381 tdsLen
= 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
3382 else if (tdsType
== TdsEnums
.SQLDATE
) {
3386 if (!TryGetTokenLength(tdsType
, stateObj
, out tdsLen
)) {
3391 rec
.metaType
= MetaType
.GetSqlDataType(tdsType
, userType
, tdsLen
);
3392 rec
.type
= rec
.metaType
.SqlDbType
;
3394 // always use the nullable type for parameters if Shiloh or later
3395 // Sphinx sometimes sends fixed length return values
3397 rec
.tdsType
= rec
.metaType
.NullableType
;
3398 rec
.isNullable
= true;
3399 if (tdsLen
== TdsEnums
.SQL_USHORTVARMAXLEN
) {
3400 Debug
.Assert(_isYukon
, "plp data from pre-Yukon server");
3401 rec
.metaType
= MetaType
.GetMaxMetaTypeFromMetaType(rec
.metaType
);
3404 else { // For sphinx, keep the fixed type if that is what is returned
3405 if (rec
.metaType
.NullableType
== tdsType
)
3406 rec
.isNullable
= true;
3408 rec
.tdsType
= (byte)tdsType
;
3411 if (rec
.type
== SqlDbType
.Decimal
) {
3412 if (!stateObj
.TryReadByte(out rec
.precision
)) {
3415 if (!stateObj
.TryReadByte(out rec
.scale
)) {
3420 if (rec
.metaType
.IsVarTime
) {
3421 if (!stateObj
.TryReadByte(out rec
.scale
)) {
3426 if (tdsType
== TdsEnums
.SQLUDT
) {
3427 if (!TryProcessUDTMetaData((SqlMetaDataPriv
) rec
, stateObj
)) {
3432 if (rec
.type
== SqlDbType
.Xml
) {
3435 if (!stateObj
.TryReadByte(out schemapresent
)) {
3439 if ((schemapresent
& 1) != 0) {
3440 if (!stateObj
.TryReadByte(out len
)) {
3444 if (!stateObj
.TryReadString(len
, out rec
.xmlSchemaCollectionDatabase
)) {
3449 if (!stateObj
.TryReadByte(out len
)) {
3453 if (!stateObj
.TryReadString(len
, out rec
.xmlSchemaCollectionOwningSchema
)) {
3459 if (!stateObj
.TryReadInt16(out slen
)) {
3464 if (!stateObj
.TryReadString(slen
, out rec
.xmlSchemaCollectionName
)) {
3471 else if (_isShiloh
&& rec
.metaType
.IsCharType
) {
3472 // read the collation for 8.x servers
3473 if (!TryProcessCollation(stateObj
, out rec
.collation
)) {
3477 int codePage
= GetCodePage(rec
.collation
, stateObj
);
3479 // if the column lcid is the same as the default, use the default encoder
3480 if (codePage
== _defaultCodePage
) {
3481 rec
.codePage
= _defaultCodePage
;
3482 rec
.encoding
= _defaultEncoding
;
3485 rec
.codePage
= codePage
;
3486 rec
.encoding
= System
.Text
.Encoding
.GetEncoding(rec
.codePage
);
3490 // For encrypted parameters, read the unencrypted type and encryption information.
3491 if (_serverSupportsColumnEncryption
&& rec
.isEncrypted
) {
3492 if (!TryProcessTceCryptoMetadata(stateObj
, rec
, cipherTable
: null, columnEncryptionSetting
: columnEncryptionSetting
, isReturnValue
: true)) {
3497 // for now we coerce return values into a SQLVariant, not good...
3498 bool isNull
= false;
3500 if (!TryProcessColumnHeaderNoNBC(rec
, stateObj
, out isNull
, out valLen
)) {
3504 // always read as sql types
3505 Debug
.Assert(valLen
< (ulong)(Int32
.MaxValue
), "ProcessReturnValue received data size > 2Gb");
3507 int intlen
= valLen
> (ulong)(Int32
.MaxValue
) ? Int32
.MaxValue
: (int)valLen
;
3509 if (rec
.metaType
.IsPlp
) {
3510 intlen
= Int32
.MaxValue
; // If plp data, read it all
3514 GetNullSqlValue(rec
.value, rec
, SqlCommandColumnEncryptionSetting
.Disabled
, _connHandler
);
3517 // We should never do any decryption here, so pass disabled as the command encryption override.
3518 // We only read the binary value and decryption will be performed by OnReturnValue().
3519 if (!TryReadSqlValue(rec
.value, rec
, intlen
, stateObj
, SqlCommandColumnEncryptionSetting
.Disabled
, columnName
:null /*Not used*/)) {
3528 internal bool TryProcessTceCryptoMetadata (TdsParserStateObject stateObj
,
3529 SqlMetaDataPriv col
,
3530 SqlTceCipherInfoTable
? cipherTable
,
3531 SqlCommandColumnEncryptionSetting columnEncryptionSetting
,
3532 bool isReturnValue
) {
3533 Debug
.Assert(isReturnValue
== (cipherTable
== null), "Ciphertable is not set iff this is a return value");
3535 // Read the ordinal into cipher table
3539 // For return values there is not cipher table and no ordinal.
3540 if (cipherTable
.HasValue
) {
3541 if (!stateObj
.TryReadUInt16(out index
)) {
3545 // validate the index (ordinal passed)
3546 if (index
>= cipherTable
.Value
.Size
) {
3547 Bid
.Trace("<sc.TdsParser.TryProcessTceCryptoMetadata|TCE> Incorrect ordinal received %d, max tab size: %d\n", index
, cipherTable
.Value
.Size
);
3548 throw SQL
.ParsingErrorValue(ParsingErrorState
.TceInvalidOrdinalIntoCipherInfoTable
, index
);
3552 // Read the user type
3553 if (!stateObj
.TryReadUInt32(out userType
)) {
3557 // Read the base TypeInfo
3558 col
.baseTI
= new SqlMetaDataPriv();
3559 if (!TryProcessTypeInfo(stateObj
, col
.baseTI
, userType
)) {
3563 // Read the cipher algorithm Id
3564 byte cipherAlgorithmId
;
3565 if (!stateObj
.TryReadByte(out cipherAlgorithmId
)) {
3569 string cipherAlgorithmName
= null;
3570 if (TdsEnums
.CustomCipherAlgorithmId
== cipherAlgorithmId
) {
3571 // Custom encryption algorithm, read the name
3573 if (!stateObj
.TryReadByte(out nameSize
)) {
3577 if (!stateObj
.TryReadString(nameSize
, out cipherAlgorithmName
)) {
3582 // Read Encryption Type.
3583 byte encryptionType
;
3584 if (!stateObj
.TryReadByte(out encryptionType
)) {
3588 // Read Normalization Rule Version.
3589 byte normalizationRuleVersion
;
3590 if (!stateObj
.TryReadByte(out normalizationRuleVersion
)) {
3594 Debug
.Assert(col
.cipherMD
== null, "col.cipherMD should be null in TryProcessTceCryptoMetadata.");
3596 // Check if TCE is enable and if it is set the crypto MD for the column.
3597 // TCE is enabled if the command is set to enabled or to resultset only and this is not a return value
3598 // or if it is set to use connection setting and the connection has TCE enabled.
3599 if ((columnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.Enabled
||
3600 (columnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.ResultSetOnly
&& !isReturnValue
)) ||
3601 (columnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.UseConnectionSetting
&&
3602 _connHandler
!= null && _connHandler
.ConnectionOptions
!= null &&
3603 _connHandler
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
)) {
3604 col
.cipherMD
= new SqlCipherMetadata(cipherTable
.HasValue
? (SqlTceCipherInfoEntry
?)cipherTable
.Value
[index
] : null,
3606 cipherAlgorithmId: cipherAlgorithmId
,
3607 cipherAlgorithmName: cipherAlgorithmName
,
3608 encryptionType: encryptionType
,
3609 normalizationRuleVersion: normalizationRuleVersion
);
3612 // If TCE is disabled mark the MD as not encrypted.
3613 col
.isEncrypted
= false;
3619 internal bool TryProcessCollation(TdsParserStateObject stateObj
, out SqlCollation collation
) {
3620 SqlCollation newCollation
= new SqlCollation();
3622 if (!stateObj
.TryReadUInt32(out newCollation
.info
)) {
3626 if (!stateObj
.TryReadByte(out newCollation
.sortId
)) {
3631 collation
= newCollation
;
3635 private void WriteCollation(SqlCollation collation
, TdsParserStateObject stateObj
) {
3636 if (collation
== null) {
3637 _physicalStateObj
.WriteByte(0);
3640 _physicalStateObj
.WriteByte(sizeof(UInt32
)+sizeof(byte));
3641 WriteUnsignedInt(collation
.info
, _physicalStateObj
);
3642 _physicalStateObj
.WriteByte(collation
.sortId
);
3647 internal int GetCodePage(SqlCollation collation
, TdsParserStateObject stateObj
) {
3650 if (0 != collation
.sortId
) {
3651 codePage
= TdsEnums
.CODE_PAGE_FROM_SORT_ID
[collation
.sortId
];
3652 Debug
.Assert(0 != codePage
, "GetCodePage accessed codepage array and produced 0!, sortID =" + ((Byte
)(collation
.sortId
)).ToString((IFormatProvider
)null));
3655 int cultureId
= collation
.LCID
;
3656 bool success
= false;
3659 codePage
= CultureInfo
.GetCultureInfo(cultureId
).TextInfo
.ANSICodePage
;
3661 // SqlHot 50001398: CodePage can be zero, but we should defer such errors until
3662 // we actually MUST use the code page (i.e. don't error if no ANSI data is sent).
3665 catch (ArgumentException e
) {
3666 ADP
.TraceExceptionWithoutRethrow(e
);
3669 // If we failed, it is quite possible this is because certain culture id's
3670 // were removed in Win2k and beyond, however Sql Server still supports them.
3671 // There is a workaround for the culture id's listed below, which is to mask
3672 // off the sort id (the leading 1). If that fails, or we have a culture id
3673 // other than the special cases below, we throw an error and throw away the
3674 // rest of the results. For additional info, see MDAC 65963.
3676 // SqlHot 50001398: Sometimes GetCultureInfo will return CodePage 0 instead of throwing.
3677 // treat this as an error also, and switch into the special-case logic.
3678 if (!success
|| codePage
== 0) {
3679 CultureInfo ci
= null;
3680 switch (cultureId
) {
3681 case 0x10404: // zh-TW
3682 case 0x10804: // zh-CN
3683 case 0x10c04: // zh-HK
3684 case 0x11004: // zh-SG
3685 case 0x11404: // zh-MO
3686 case 0x10411: // ja-JP
3687 case 0x10412: // ko-KR
3688 // If one of the following special cases, mask out sortId and
3690 cultureId
= cultureId
& 0x03fff;
3693 ci
= new CultureInfo(cultureId
);
3696 catch (ArgumentException e
) {
3697 ADP
.TraceExceptionWithoutRethrow(e
);
3700 case 0x827: // Non-supported Lithuanian code page, map it to supported Lithuanian.
3702 ci
= new CultureInfo(0x427);
3705 catch (ArgumentException e
) {
3706 ADP
.TraceExceptionWithoutRethrow(e
);
3713 // I don't believe we should still be in failure case, but just in case.
3715 ThrowUnsupportedCollationEncountered(stateObj
);
3719 codePage
= ci
.TextInfo
.ANSICodePage
;
3728 internal void DrainData(TdsParserStateObject stateObj
) {
3729 RuntimeHelpers
.PrepareConstrainedRegions();
3732 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
3734 RuntimeHelpers
.PrepareConstrainedRegions();
3736 tdsReliabilitySection
.Start();
3741 SqlDataReader
.SharedState sharedState
= stateObj
._readerState
;
3742 if (sharedState
!= null && sharedState
._dataReady
) {
3743 var metadata
= stateObj
._cleanupMetaData
;
3744 if (stateObj
._partialHeaderBytesRead
> 0) {
3745 if (!stateObj
.TryProcessHeader()) {
3746 throw SQL
.SynchronousCallMayNotPend();
3749 if (0 == sharedState
._nextColumnHeaderToRead
) {
3750 // i. user called read but didn't fetch anything
3751 if (!stateObj
.Parser
.TrySkipRow(stateObj
._cleanupMetaData
, stateObj
)) {
3752 throw SQL
.SynchronousCallMayNotPend();
3756 // iia. if we still have bytes left from a partially read column, skip
3757 if (sharedState
._nextColumnDataToRead
< sharedState
._nextColumnHeaderToRead
) {
3758 if ((sharedState
._nextColumnHeaderToRead
> 0) && (metadata
[sharedState
._nextColumnHeaderToRead
- 1].metaType
.IsPlp
)) {
3759 if (stateObj
._longlen
!= 0) {
3761 if (!TrySkipPlpValue(UInt64
.MaxValue
, stateObj
, out ignored
)) {
3762 throw SQL
.SynchronousCallMayNotPend();
3767 else if (0 < sharedState
._columnDataBytesRemaining
) {
3768 if (!stateObj
.TrySkipLongBytes(sharedState
._columnDataBytesRemaining
)) {
3769 throw SQL
.SynchronousCallMayNotPend();
3777 // now read the remaining values off the wire for this row
3778 if (!stateObj
.Parser
.TrySkipRow(metadata
, sharedState
._nextColumnHeaderToRead
, stateObj
)) {
3779 throw SQL
.SynchronousCallMayNotPend();
3784 Run(RunBehavior
.Clean
, null, null, null, stateObj
);
3787 _connHandler
.DoomThisConnection();
3793 tdsReliabilitySection
.Stop();
3797 catch (System
.OutOfMemoryException
) {
3798 _connHandler
.DoomThisConnection();
3801 catch (System
.StackOverflowException
) {
3802 _connHandler
.DoomThisConnection();
3805 catch (System
.Threading
.ThreadAbortException
) {
3806 _connHandler
.DoomThisConnection();
3812 internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj
) {
3813 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.MIN_ERROR_CLASS
, _server
, SQLMessage
.CultureIdError(), "", 0));
3815 if (null != stateObj
) {
3816 DrainData(stateObj
);
3818 stateObj
._pendingData
= false;
3821 ThrowExceptionAndWarning(stateObj
);
3826 internal bool TryProcessAltMetaData(int cColumns
, TdsParserStateObject stateObj
, out _SqlMetaDataSet metaData
) {
3827 Debug
.Assert(cColumns
> 0, "should have at least 1 column in altMetaData!");
3830 _SqlMetaDataSet altMetaDataSet
= new _SqlMetaDataSet(cColumns
, null);
3831 int[] indexMap
= new int[cColumns
];
3833 if (!stateObj
.TryReadUInt16(out altMetaDataSet
.id
)) {
3838 if (!stateObj
.TryReadByte(out byCols
)) {
3842 while (byCols
> 0) {
3843 if (!stateObj
.TrySkipBytes(2)) { // ignore ColNum ...
3849 // pass 1, read the meta data off the wire
3850 for (int i
= 0; i
< cColumns
; i
++) {
3851 // internal meta data class
3852 _SqlMetaData col
= altMetaDataSet
[i
];
3854 if (!stateObj
.TryReadByte(out col
.op
)) {
3857 if (!stateObj
.TryReadUInt16(out col
.operand
)) {
3861 // TCE is not applicable to AltMetadata.
3862 if (!TryCommonProcessMetaData(stateObj
, col
, null, fColMD
: false, columnEncryptionSetting
: SqlCommandColumnEncryptionSetting
.Disabled
)) {
3866 if (ADP
.IsEmpty(col
.column
)) {
3867 // create column name from op
3869 case TdsEnums
.AOPAVG
:
3873 case TdsEnums
.AOPCNT
:
3877 case TdsEnums
.AOPCNTB
:
3878 col
.column
= "cntb";
3881 case TdsEnums
.AOPMAX
:
3885 case TdsEnums
.AOPMIN
:
3889 case TdsEnums
.AOPSUM
:
3893 case TdsEnums
.AOPANY
:
3897 case TdsEnums
.AOPNOOP
:
3898 col
.column
= "noop";
3901 case TdsEnums
.AOPSTDEV
:
3902 col
.column
= "stdev";
3905 case TdsEnums
.AOPSTDEVP
:
3906 col
.column
= "stdevp";
3909 case TdsEnums
.AOPVAR
:
3913 case TdsEnums
.AOPVARP
:
3914 col
.column
= "varp";
3921 altMetaDataSet
.indexMap
= indexMap
;
3922 altMetaDataSet
.visibleColumns
= cColumns
;
3924 metaData
= altMetaDataSet
;
3929 /// <para> Parses the TDS message to read single CIPHER_INFO entry.</para>
3931 internal bool TryReadCipherInfoEntry (TdsParserStateObject stateObj
, out SqlTceCipherInfoEntry entry
) {
3932 byte cekValueCount
= 0;
3933 entry
= new SqlTceCipherInfoEntry(ordinal
: 0);
3937 if (!stateObj
.TryReadInt32(out dbId
)) {
3943 if (!stateObj
.TryReadInt32(out keyId
)) {
3947 // Read the key version
3949 if (!stateObj
.TryReadInt32(out keyVersion
)) {
3953 // Read the key MD Version
3954 byte[] keyMDVersion
= new byte[8];
3955 if (!stateObj
.TryReadByteArray(keyMDVersion
, 0, 8)) {
3959 // Read the value count
3960 if (!stateObj
.TryReadByte (out cekValueCount
)) {
3964 for (int i
= 0; i
< cekValueCount
; i
++) {
3965 // Read individual CEK values
3966 byte[] encryptedCek
;
3968 string keyStoreName
;
3969 byte algorithmLength
;
3970 string algorithmName
;
3975 // Read the length of encrypted CEK
3976 if (!stateObj
.TryReadUInt16 (out shortValue
)) {
3980 length
= shortValue
;
3981 encryptedCek
= new byte[length
];
3983 // Read the actual encrypted CEK
3984 if (!stateObj
.TryReadByteArray (encryptedCek
, 0, length
)) {
3988 // Read the length of key store name
3989 if (!stateObj
.TryReadByte (out byteValue
)) {
3995 // And read the key store name now
3996 if (!stateObj
.TryReadString(length
, out keyStoreName
)) {
4000 // Read the length of key Path
4001 if (!stateObj
.TryReadUInt16 (out shortValue
)) {
4005 length
= shortValue
;
4007 // Read the key path string
4008 if (!stateObj
.TryReadString(length
, out keyPath
)) {
4012 // Read the length of the string carrying the encryption algo
4013 if (!stateObj
.TryReadByte(out algorithmLength
)) {
4017 length
= (int)algorithmLength
;
4019 // Read the string carrying the encryption algo (eg. RSA_PKCS_OAEP)
4020 if (!stateObj
.TryReadString(length
, out algorithmName
)) {
4024 // Add this encrypted CEK blob to our list of encrypted values for the CEK
4025 entry
.Add(encryptedCek
,
4028 cekVersion: keyVersion
,
4029 cekMdVersion: keyMDVersion
,
4031 keyStoreName: keyStoreName
,
4032 algorithmName: algorithmName
);
4039 /// <para> Parses the TDS message to read a single CIPHER_INFO table.</para>
4041 internal bool TryProcessCipherInfoTable (TdsParserStateObject stateObj
, out SqlTceCipherInfoTable
? cipherTable
) {
4043 short tableSize
= 0;
4045 if (!stateObj
.TryReadInt16(out tableSize
)) {
4049 if (0 != tableSize
) {
4050 SqlTceCipherInfoTable tempTable
= new SqlTceCipherInfoTable(tableSize
);
4052 // Read individual entries
4053 for (int i
= 0; i
< tableSize
; i
++) {
4054 SqlTceCipherInfoEntry entry
;
4055 if (!TryReadCipherInfoEntry (stateObj
, out entry
)) {
4059 tempTable
[i
] = entry
;
4062 cipherTable
= tempTable
;
4068 internal bool TryProcessMetaData(int cColumns
, TdsParserStateObject stateObj
, out _SqlMetaDataSet metaData
, SqlCommandColumnEncryptionSetting columnEncryptionSetting
) {
4069 Debug
.Assert(cColumns
> 0, "should have at least 1 column in metadata!");
4071 // Read the cipher info table first
4072 SqlTceCipherInfoTable
? cipherTable
= null;
4073 if (_serverSupportsColumnEncryption
) {
4074 if (!TryProcessCipherInfoTable (stateObj
, out cipherTable
)) {
4080 // Read the ColumnData fields
4081 _SqlMetaDataSet newMetaData
= new _SqlMetaDataSet(cColumns
, cipherTable
);
4082 for (int i
= 0; i
< cColumns
; i
++) {
4083 if (!TryCommonProcessMetaData(stateObj
, newMetaData
[i
], cipherTable
, fColMD
: true, columnEncryptionSetting
: columnEncryptionSetting
)) {
4089 // DEVNOTE: cipherTable is discarded at this point since its no longer needed.
4090 metaData
= newMetaData
;
4094 private bool IsVarTimeTds(byte tdsType
) {
4095 return tdsType
== TdsEnums
.SQLTIME
|| tdsType
== TdsEnums
.SQLDATETIME2
|| tdsType
== TdsEnums
.SQLDATETIMEOFFSET
;
4098 private bool TryProcessTypeInfo (TdsParserStateObject stateObj
, SqlMetaDataPriv col
, UInt32 userType
) {
4101 if (!stateObj
.TryReadByte(out tdsType
)) {
4105 if (tdsType
== TdsEnums
.SQLXMLTYPE
)
4106 col
.length
= TdsEnums
.SQL_USHORTVARMAXLEN
; //Use the same length as other plp datatypes
4107 else if (IsVarTimeTds(tdsType
))
4108 col
.length
= 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
4109 else if (tdsType
== TdsEnums
.SQLDATE
) {
4113 if (!TryGetTokenLength(tdsType
, stateObj
, out col
.length
)) {
4118 col
.metaType
= MetaType
.GetSqlDataType(tdsType
, userType
, col
.length
);
4119 col
.type
= col
.metaType
.SqlDbType
;
4121 // If sphinx, do not change to nullable type
4123 col
.tdsType
= (col
.isNullable
? col
.metaType
.NullableType
: col
.metaType
.TDSType
);
4125 col
.tdsType
= tdsType
;
4128 if (TdsEnums
.SQLUDT
== tdsType
) {
4129 if (!TryProcessUDTMetaData((SqlMetaDataPriv
) col
, stateObj
)) {
4134 if (col
.length
== TdsEnums
.SQL_USHORTVARMAXLEN
) {
4135 Debug
.Assert(tdsType
== TdsEnums
.SQLXMLTYPE
||
4136 tdsType
== TdsEnums
.SQLBIGVARCHAR
||
4137 tdsType
== TdsEnums
.SQLBIGVARBINARY
||
4138 tdsType
== TdsEnums
.SQLNVARCHAR
||
4139 tdsType
== TdsEnums
.SQLUDT
,
4140 "Invalid streaming datatype");
4141 col
.metaType
= MetaType
.GetMaxMetaTypeFromMetaType(col
.metaType
);
4142 Debug
.Assert(col
.metaType
.IsLong
, "Max datatype not IsLong");
4143 col
.length
= Int32
.MaxValue
;
4144 if (tdsType
== TdsEnums
.SQLXMLTYPE
) {
4146 if (!stateObj
.TryReadByte(out schemapresent
)) {
4150 if ((schemapresent
& 1) != 0) {
4151 if (!stateObj
.TryReadByte(out byteLen
)) {
4155 if (!stateObj
.TryReadString(byteLen
, out col
.xmlSchemaCollectionDatabase
)) {
4160 if (!stateObj
.TryReadByte(out byteLen
)) {
4164 if (!stateObj
.TryReadString(byteLen
, out col
.xmlSchemaCollectionOwningSchema
)) {
4170 if (!stateObj
.TryReadInt16(out shortLen
)) {
4174 if (!stateObj
.TryReadString(shortLen
, out col
.xmlSchemaCollectionName
)) {
4183 if (col
.type
== SqlDbType
.Decimal
) {
4184 if (!stateObj
.TryReadByte(out col
.precision
)) {
4187 if (!stateObj
.TryReadByte(out col
.scale
)) {
4192 if (col
.metaType
.IsVarTime
) {
4193 if (!stateObj
.TryReadByte(out col
.scale
)) {
4197 Debug
.Assert(0 <= col
.scale
&& col
.scale
<= 7);
4199 // calculate actual column length here
4201 switch (col
.metaType
.SqlDbType
)
4203 case SqlDbType
.Time
:
4204 col
.length
= MetaType
.GetTimeSizeFromScale(col
.scale
);
4206 case SqlDbType
.DateTime2
:
4207 // Date in number of days (3 bytes) + time
4208 col
.length
= 3 + MetaType
.GetTimeSizeFromScale(col
.scale
);
4210 case SqlDbType
.DateTimeOffset
:
4211 // Date in days (3 bytes) + offset in minutes (2 bytes) + time
4212 col
.length
= 5 + MetaType
.GetTimeSizeFromScale(col
.scale
);
4216 Debug
.Assert(false, "Unknown VariableTime type!");
4221 // read the collation for 7.x servers
4222 if (_isShiloh
&& col
.metaType
.IsCharType
&& (tdsType
!= TdsEnums
.SQLXMLTYPE
)) {
4223 if (!TryProcessCollation(stateObj
, out col
.collation
)) {
4227 int codePage
= GetCodePage(col
.collation
, stateObj
);
4229 if (codePage
== _defaultCodePage
) {
4230 col
.codePage
= _defaultCodePage
;
4231 col
.encoding
= _defaultEncoding
;
4234 col
.codePage
= codePage
;
4235 col
.encoding
= System
.Text
.Encoding
.GetEncoding(col
.codePage
);
4242 private bool TryCommonProcessMetaData(TdsParserStateObject stateObj
, _SqlMetaData col
, SqlTceCipherInfoTable
? cipherTable
, bool fColMD
, SqlCommandColumnEncryptionSetting columnEncryptionSetting
) {
4246 // read user type - 4 bytes Yukon, 2 backwards
4247 if (IsYukonOrNewer
) {
4248 if (!stateObj
.TryReadUInt32(out userType
)) {
4253 ushort userTypeShort
;
4254 if (!stateObj
.TryReadUInt16(out userTypeShort
)) {
4257 userType
= userTypeShort
;
4260 // read flags and set appropriate flags in structure
4262 if (!stateObj
.TryReadByte(out flags
)) {
4266 col
.updatability
= (byte)((flags
& TdsEnums
.Updatability
) >> 2);
4267 col
.isNullable
= (TdsEnums
.Nullable
== (flags
& TdsEnums
.Nullable
));
4268 col
.isIdentity
= (TdsEnums
.Identity
== (flags
& TdsEnums
.Identity
));
4270 // read second byte of column metadata flags
4271 if (!stateObj
.TryReadByte(out flags
)) {
4275 col
.isColumnSet
= (TdsEnums
.IsColumnSet
== (flags
& TdsEnums
.IsColumnSet
));
4276 if (fColMD
&& _serverSupportsColumnEncryption
) {
4277 col
.isEncrypted
= (TdsEnums
.IsEncrypted
== (flags
& TdsEnums
.IsEncrypted
));
4281 if (!TryProcessTypeInfo (stateObj
, col
, userType
)) {
4285 // Read tablename if present
4286 if (col
.metaType
.IsLong
&& !col
.metaType
.IsPlp
) {
4288 int unusedLen
= 0xFFFF; //We ignore this value
4289 if (!TryProcessOneTable(stateObj
, ref unusedLen
, out col
.multiPartTableName
)) {
4294 if (!stateObj
.TryReadUInt16(out shortLen
)) {
4298 if (!stateObj
.TryReadString(shortLen
, out tableName
)) {
4301 // with Sql2000 this is returned as an unquoted mix of catalog.owner.table
4302 // all of which may contain "." and unable to parse correctly from the string alone
4303 // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column
4304 // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios
4305 col
.multiPartTableName
= new MultiPartTableName(tableName
);
4309 // Read the TCE column cryptoinfo
4310 if (fColMD
&& _serverSupportsColumnEncryption
&& col
.isEncrypted
) {
4311 // If the column is encrypted, we should have a valid cipherTable
4312 if (cipherTable
.HasValue
&& !TryProcessTceCryptoMetadata (stateObj
, col
, cipherTable
.Value
, columnEncryptionSetting
, isReturnValue
: false)) {
4317 // Read the column name
4318 if (!stateObj
.TryReadByte(out byteLen
)) {
4321 if (!stateObj
.TryReadString(byteLen
, out col
.column
)) {
4325 // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings.
4326 // We only need to fire this event when we actually have a meta data stream with 0 or more rows.
4327 stateObj
._receivedColMetaData
= true;
4331 private bool TryProcessUDTMetaData(SqlMetaDataPriv metaData
, TdsParserStateObject stateObj
) {
4335 if (!stateObj
.TryReadUInt16(out shortLength
)) { // max byte size
4338 metaData
.length
= shortLength
;
4341 if (!stateObj
.TryReadByte(out byteLength
)) {
4344 if (byteLength
!= 0) {
4345 if (!stateObj
.TryReadString(byteLength
, out metaData
.udtDatabaseName
)) {
4351 if (!stateObj
.TryReadByte(out byteLength
)) {
4354 if (byteLength
!= 0) {
4355 if (!stateObj
.TryReadString(byteLength
, out metaData
.udtSchemaName
)) {
4361 if (!stateObj
.TryReadByte(out byteLength
)) {
4364 if (byteLength
!= 0) {
4365 if (!stateObj
.TryReadString(byteLength
, out metaData
.udtTypeName
)) {
4370 if (!stateObj
.TryReadUInt16(out shortLength
)) {
4373 if (shortLength
!= 0) {
4374 if (!stateObj
.TryReadString(shortLength
, out metaData
.udtAssemblyQualifiedName
)) {
4382 private void WriteUDTMetaData(object value, string database
, string schema
, string type
,
4383 TdsParserStateObject stateObj
) {
4385 if (ADP
.IsEmpty(database
)) {
4386 stateObj
.WriteByte(0);
4389 stateObj
.WriteByte((byte)database
.Length
);
4390 WriteString(database
, stateObj
);
4394 if (ADP
.IsEmpty(schema
)) {
4395 stateObj
.WriteByte(0);
4398 stateObj
.WriteByte((byte)schema
.Length
);
4399 WriteString(schema
, stateObj
);
4403 if (ADP
.IsEmpty(type
)) {
4404 stateObj
.WriteByte(0);
4407 stateObj
.WriteByte((byte)type
.Length
);
4408 WriteString(type
, stateObj
);
4412 internal bool TryProcessTableName(int length
, TdsParserStateObject stateObj
, out MultiPartTableName
[] multiPartTableNames
) {
4413 int tablesAdded
= 0;
4415 MultiPartTableName
[] tables
= new MultiPartTableName
[1];
4416 MultiPartTableName mpt
;
4417 while (length
> 0) {
4421 if (!TryProcessOneTable(stateObj
, ref length
, out mpt
)) {
4422 multiPartTableNames
= null;
4425 if (tablesAdded
== 0) {
4426 tables
[tablesAdded
] = mpt
;
4429 MultiPartTableName
[] newTables
= new MultiPartTableName
[tables
.Length
+ 1];
4430 Array
.Copy(tables
, 0, newTables
, 0, tables
.Length
);
4431 newTables
[tables
.Length
] = mpt
;
4438 multiPartTableNames
= tables
;
4442 private bool TryProcessOneTable(TdsParserStateObject stateObj
, ref int length
, out MultiPartTableName multiPartTableName
) {
4444 MultiPartTableName mpt
;
4447 multiPartTableName
= default(MultiPartTableName
);
4451 mpt
= new MultiPartTableName();
4454 // Find out how many parts in the TDS stream
4455 if (!stateObj
.TryReadByte(out nParts
)) {
4460 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4464 if (!stateObj
.TryReadString(tableLen
, out value)) {
4467 mpt
.ServerName
= value;
4469 length
-= (tableLen
* 2); // wide bytes
4472 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4476 if (!stateObj
.TryReadString(tableLen
, out value)) {
4479 mpt
.CatalogName
= value;
4480 length
-= (tableLen
* 2); // wide bytes
4484 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4488 if (!stateObj
.TryReadString(tableLen
, out value)) {
4491 mpt
.SchemaName
= value;
4492 length
-= (tableLen
* 2); // wide bytes
4496 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4500 if (!stateObj
.TryReadString(tableLen
, out value)) {
4503 mpt
.TableName
= value;
4504 length
-= (tableLen
* 2); // wide bytes
4507 Debug
.Assert(nParts
== 0 , "ProcessTableName:Unidentified parts in the table name token stream!");
4511 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4515 if (!stateObj
.TryReadString(tableLen
, out value)) {
4518 string tableName
= value;
4519 length
-= (tableLen
* 2); // wide bytes
4520 mpt
= new MultiPartTableName(MultipartIdentifier
.ParseMultipartIdentifier(tableName
, "[\"", "]\"", Res
.SQL_TDSParserTableName
, false));
4523 multiPartTableName
= mpt
;
4527 // augments current metadata with table and key information
4528 private bool TryProcessColInfo(_SqlMetaDataSet columns
, SqlDataReader reader
, TdsParserStateObject stateObj
, out _SqlMetaDataSet metaData
) {
4529 Debug
.Assert(columns
!= null && columns
.Length
> 0, "no metadata available!");
4533 for (int i
= 0; i
< columns
.Length
; i
++) {
4534 _SqlMetaData col
= columns
[i
];
4537 if (!stateObj
.TryReadByte(out ignored
)) { // colnum, ignore
4540 if (!stateObj
.TryReadByte(out col
.tableNum
)) {
4546 if (!stateObj
.TryReadByte(out status
)) {
4550 col
.isDifferentName
= (TdsEnums
.SQLDifferentName
== (status
& TdsEnums
.SQLDifferentName
));
4551 col
.isExpression
= (TdsEnums
.SQLExpression
== (status
& TdsEnums
.SQLExpression
));
4552 col
.isKey
= (TdsEnums
.SQLKey
== (status
& TdsEnums
.SQLKey
));
4553 col
.isHidden
= (TdsEnums
.SQLHidden
== (status
& TdsEnums
.SQLHidden
));
4555 // read off the base table name if it is different than the select list column name
4556 if (col
.isDifferentName
) {
4558 if (!stateObj
.TryReadByte(out len
)) {
4561 if (!stateObj
.TryReadString(len
, out col
.baseColumn
)) {
4566 // Fixup column name - only if result of a table - that is if it was not the result of
4568 if ((reader
.TableNames
!= null) && (col
.tableNum
> 0)) {
4569 Debug
.Assert(reader
.TableNames
.Length
>= col
.tableNum
, "invalid tableNames array!");
4570 col
.multiPartTableName
= reader
.TableNames
[col
.tableNum
- 1];
4573 // MDAC 60109: expressions are readonly
4574 if (col
.isExpression
) {
4575 col
.updatability
= 0;
4579 // set the metadata so that the stream knows some metadata info has changed
4584 // takes care of any per data header information:
4585 // for long columns, reads off textptrs, reads length, check nullability
4586 // for other columns, reads length, checks nullability
4587 // returns length and nullability
4588 internal bool TryProcessColumnHeader(SqlMetaDataPriv col
, TdsParserStateObject stateObj
, int columnOrdinal
, out bool isNull
, out ulong length
) {
4589 // query NBC row information first
4590 if (stateObj
.IsNullCompressionBitSet(columnOrdinal
)) {
4592 // column information is not present in TDS if null compression bit is set, return now
4597 return TryProcessColumnHeaderNoNBC(col
, stateObj
, out isNull
, out length
);
4600 private bool TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col
, TdsParserStateObject stateObj
, out bool isNull
, out ulong length
) {
4601 if (col
.metaType
.IsLong
&& !col
.metaType
.IsPlp
) {
4603 // we don't care about TextPtrs, simply go after the data after it
4606 if (!stateObj
.TryReadByte(out textPtrLen
)) {
4612 if (0 != textPtrLen
) {
4613 // read past text pointer
4614 if (!stateObj
.TrySkipBytes(textPtrLen
)) {
4620 // read past timestamp
4621 if (!stateObj
.TrySkipBytes(TdsEnums
.TEXT_TIME_STAMP_LEN
)) {
4628 return TryGetDataLength(col
, stateObj
, out length
);
4640 if (!TryGetDataLength(col
, stateObj
, out longlen
)) {
4645 isNull
= IsNull(col
.metaType
, longlen
);
4646 length
= (isNull
? 0 : longlen
);
4651 // assumes that the current position is at the start of an altrow!
4652 internal bool TryGetAltRowId(TdsParserStateObject stateObj
, out int id
) {
4654 if (!stateObj
.TryReadByte(out token
)) { // skip over ALTROW token
4658 Debug
.Assert((token
== TdsEnums
.SQLALTROW
), "");
4660 // Start a fresh row - disable NBC since Alt Rows are never compressed
4661 if (!stateObj
.TryStartNewRow(isNullCompressed
: false)) {
4667 if (!stateObj
.TryReadUInt16(out shortId
)) {
4676 // Used internally by BulkCopy only
4677 private bool TryProcessRow(_SqlMetaDataSet columns
, object[] buffer
, int[] map
, TdsParserStateObject stateObj
) {
4678 SqlBuffer data
= new SqlBuffer();
4680 for (int i
= 0; i
< columns
.Length
; i
++) {
4681 _SqlMetaData md
= columns
[i
];
4682 Debug
.Assert(md
!= null, "_SqlMetaData should not be null for column " + i
.ToString(CultureInfo
.InvariantCulture
));
4686 if (!TryProcessColumnHeader(md
, stateObj
, i
, out isNull
, out len
)) {
4691 GetNullSqlValue(data
, md
, SqlCommandColumnEncryptionSetting
.Disabled
/*Column Encryption Disabled for Bulk Copy*/, _connHandler
);
4692 buffer
[map
[i
]] = data
.SqlValue
;
4695 // We only read up to 2Gb. Throw if data is larger. Very large data
4696 // should be read in chunks in sequential read mode
4697 // For Plp columns, we may have gotten only the length of the first chunk
4698 if (!TryReadSqlValue(data
, md
, md
.metaType
.IsPlp
? (Int32
.MaxValue
) : (int)len
, stateObj
,
4699 SqlCommandColumnEncryptionSetting
.Disabled
/*Column Encryption Disabled for Bulk Copy*/,
4703 buffer
[map
[i
]] = data
.SqlValue
;
4704 if (stateObj
._longlen
!= 0) {
4705 throw new SqlTruncateException(Res
.GetString(Res
.SqlMisc_TruncationMaxDataMessage
));
4715 /// Determines if a column value should be transparently decrypted (based on SqlCommand and Connection String settings).
4717 /// <returns>true if the value should be transparently decrypted, false otherwise</returns>
4718 internal static bool ShouldHonorTceForRead (SqlCommandColumnEncryptionSetting columnEncryptionSetting
,
4719 SqlInternalConnectionTds connection
) {
4721 // Command leve setting trumps all
4722 switch (columnEncryptionSetting
) {
4723 case SqlCommandColumnEncryptionSetting
.Disabled
:
4725 case SqlCommandColumnEncryptionSetting
.Enabled
:
4727 case SqlCommandColumnEncryptionSetting
.ResultSetOnly
:
4730 // Check connection level setting!
4731 Debug
.Assert(SqlCommandColumnEncryptionSetting
.UseConnectionSetting
== columnEncryptionSetting
,
4732 "Unexpected value for command level override");
4733 return (connection
!= null && connection
.ConnectionOptions
!= null &&
4734 connection
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
);
4738 internal static object GetNullSqlValue(
4741 SqlCommandColumnEncryptionSetting columnEncryptionSetting
,
4742 SqlInternalConnectionTds connection
) {
4743 SqlDbType type
= md
.type
;
4745 if (type
== SqlDbType
.VarBinary
&& // if its a varbinary
4746 md
.isEncrypted
&&// and encrypted
4747 ShouldHonorTceForRead(columnEncryptionSetting
, connection
)){
4748 type
= md
.baseTI
.type
; // the use the actual (plaintext) type
4752 case SqlDbType
.Real
:
4753 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Single
);
4756 case SqlDbType
.Float
:
4757 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Double
);
4761 case SqlDbType
.Binary
:
4762 case SqlDbType
.VarBinary
:
4763 case SqlDbType
.Image
:
4764 nullVal
.SqlBinary
= SqlBinary
.Null
;
4767 case SqlDbType
.UniqueIdentifier
:
4768 nullVal
.SqlGuid
= SqlGuid
.Null
;
4772 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Boolean
);
4775 case SqlDbType
.TinyInt
:
4776 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Byte
);
4779 case SqlDbType
.SmallInt
:
4780 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Int16
);
4784 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Int32
);
4787 case SqlDbType
.BigInt
:
4788 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Int64
);
4791 case SqlDbType
.Char
:
4792 case SqlDbType
.VarChar
:
4793 case SqlDbType
.NChar
:
4794 case SqlDbType
.NVarChar
:
4795 case SqlDbType
.Text
:
4796 case SqlDbType
.NText
:
4797 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.String
);
4800 case SqlDbType
.Decimal
:
4801 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Decimal
);
4804 case SqlDbType
.DateTime
:
4805 case SqlDbType
.SmallDateTime
:
4806 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.DateTime
);
4809 case SqlDbType
.Money
:
4810 case SqlDbType
.SmallMoney
:
4811 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Money
);
4814 case SqlDbType
.Variant
:
4815 // DBNull.Value will have to work here
4816 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Empty
);
4820 nullVal
.SqlCachedBuffer
= SqlCachedBuffer
.Null
;
4823 case SqlDbType
.Date
:
4824 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Date
);
4827 case SqlDbType
.Time
:
4828 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Time
);
4831 case SqlDbType
.DateTime2
:
4832 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.DateTime2
);
4835 case SqlDbType
.DateTimeOffset
:
4836 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.DateTimeOffset
);
4839 case SqlDbType
.Timestamp
:
4840 // Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change
4841 // Dev10 Bug #752790 - don't assert when it does happen
4845 Debug
.Assert(false, "unknown null sqlType!" + md
.type
.ToString());
4852 internal bool TrySkipRow(_SqlMetaDataSet columns
, TdsParserStateObject stateObj
) {
4853 return TrySkipRow(columns
, 0, stateObj
);
4856 internal bool TrySkipRow(_SqlMetaDataSet columns
, int startCol
, TdsParserStateObject stateObj
) {
4857 for (int i
= startCol
; i
< columns
.Length
; i
++) {
4858 _SqlMetaData md
= columns
[i
];
4860 if (!TrySkipValue(md
, i
, stateObj
)) {
4868 /// 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
4870 internal bool TrySkipValue(SqlMetaDataPriv md
, int columnOrdinal
, TdsParserStateObject stateObj
) {
4871 if (stateObj
.IsNullCompressionBitSet(columnOrdinal
)) {
4875 if (md
.metaType
.IsPlp
) {
4877 if (!TrySkipPlpValue(UInt64
.MaxValue
, stateObj
, out ignored
)) {
4881 else if (md
.metaType
.IsLong
) {
4883 Debug
.Assert(!md
.metaType
.IsPlp
, "Plp types must be handled using SkipPlpValue");
4886 if (!stateObj
.TryReadByte(out textPtrLen
)) {
4890 if (0 != textPtrLen
) {
4891 if (!stateObj
.TrySkipBytes(textPtrLen
+ TdsEnums
.TEXT_TIME_STAMP_LEN
)) {
4896 if (!TryGetTokenLength(md
.tdsType
, stateObj
, out length
)) {
4899 if (!stateObj
.TrySkipBytes(length
)) {
4906 if (!TryGetTokenLength(md
.tdsType
, stateObj
, out length
)) {
4910 // if false, no value to skip - it's null
4911 if (!IsNull(md
.metaType
, (ulong)length
)) {
4912 if (!stateObj
.TrySkipBytes(length
)) {
4921 private bool IsNull(MetaType mt
, ulong length
) {
4922 // null bin and char types have a length of -1 to represent null
4924 return (TdsEnums
.SQL_PLP_NULL
== length
);
4927 // HOTFIX #50000415: for image/text, 0xFFFF is the length, not representing null
4928 if ((TdsEnums
.VARNULL
== length
) && !mt
.IsLong
) {
4932 // other types have a length of 0 to represent null
4933 // long and non-PLP types will always return false because these types are either char or binary
4934 // this is expected since for long and non-plp types isnull is checked based on textptr field and not the length
4935 return ((TdsEnums
.FIXEDNULL
== length
) && !mt
.IsCharType
&& !mt
.IsBinType
);
4938 private bool TryReadSqlStringValue(SqlBuffer
value, byte type
, int length
, Encoding encoding
, bool isPlp
, TdsParserStateObject stateObj
) {
4940 case TdsEnums
.SQLCHAR
:
4941 case TdsEnums
.SQLBIGCHAR
:
4942 case TdsEnums
.SQLVARCHAR
:
4943 case TdsEnums
.SQLBIGVARCHAR
:
4944 case TdsEnums
.SQLTEXT
:
4945 // If bigvarchar(max), we only read the first chunk here,
4946 // expecting the caller to read the rest
4947 if (encoding
== null) {
4948 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
4949 // 7.0 has no support for multiple code pages in data - single code page support only
4950 encoding
= _defaultEncoding
;
4953 if (!stateObj
.TryReadStringWithEncoding(length
, encoding
, isPlp
, out stringValue
)) {
4956 value.SetToString(stringValue
);
4959 case TdsEnums
.SQLNCHAR
:
4960 case TdsEnums
.SQLNVARCHAR
:
4961 case TdsEnums
.SQLNTEXT
:
4968 if (!TryReadPlpUnicodeChars(ref cc
, 0, length
>> 1, stateObj
, out length
)) {
4972 s
= new String(cc
, 0, length
);
4979 if (!stateObj
.TryReadString(length
>> 1, out s
)) {
4984 value.SetToString(s
);
4989 Debug
.Assert(false, "Unknown tds type for SqlString!" + type
.ToString(CultureInfo
.InvariantCulture
));
4997 /// Deserializes the unencrypted bytes into a value based on the target type info.
4999 internal bool DeserializeUnencryptedValue (SqlBuffer
value, byte[] unencryptedBytes
, SqlMetaDataPriv md
, TdsParserStateObject stateObj
, byte normalizationVersion
) {
5000 if (normalizationVersion
!= 0x01) {
5001 throw SQL
.UnsupportedNormalizationVersion(normalizationVersion
);
5004 byte tdsType
= md
.baseTI
.tdsType
;
5005 int length
= unencryptedBytes
.Length
;
5007 // For normalized types, the length and scale of the actual type might be different than the value's.
5008 int denormalizedLength
= md
.baseTI
.length
;
5009 byte denormalizedScale
= md
.baseTI
.scale
;
5011 Debug
.Assert (false == md
.baseTI
.isEncrypted
, "Double encryption detected");
5013 // We normalize to allow conversion across data types. All data types below are serialized into a BIGINT.
5014 case TdsEnums
.SQLBIT
:
5015 case TdsEnums
.SQLBITN
:
5016 case TdsEnums
.SQLINTN
:
5017 case TdsEnums
.SQLINT1
:
5018 case TdsEnums
.SQLINT2
:
5019 case TdsEnums
.SQLINT4
:
5020 case TdsEnums
.SQLINT8
:
5021 Debug
.Assert(length
== 8, "invalid length for SqlInt64 type!");
5025 if (unencryptedBytes
.Length
!= 8) {
5029 longValue
= BitConverter
.ToInt64(unencryptedBytes
, 0);
5031 if (tdsType
== TdsEnums
.SQLBIT
||
5032 tdsType
== TdsEnums
.SQLBITN
) {
5033 value.Boolean
= (longValue
!= 0);
5037 if (tdsType
== TdsEnums
.SQLINT1
|| denormalizedLength
== 1)
5038 value.Byte
= (byte)longValue
;
5039 else if (tdsType
== TdsEnums
.SQLINT2
|| denormalizedLength
== 2)
5040 value.Int16
= (Int16
)longValue
;
5041 else if (tdsType
== TdsEnums
.SQLINT4
|| denormalizedLength
== 4)
5042 value.Int32
= (Int32
)longValue
;
5044 value.Int64
= longValue
;
5048 case TdsEnums
.SQLFLTN
:
5050 goto case TdsEnums
.SQLFLT4
;
5053 goto case TdsEnums
.SQLFLT8
;
5056 case TdsEnums
.SQLFLT4
:
5057 Debug
.Assert(length
== 4, "invalid length for SqlSingle type!");
5059 if (unencryptedBytes
.Length
!= 4) {
5063 singleValue
= BitConverter
.ToSingle(unencryptedBytes
, 0);
5064 value.Single
= singleValue
;
5067 case TdsEnums
.SQLFLT8
:
5069 if (unencryptedBytes
.Length
!= 8) {
5073 doubleValue
= BitConverter
.ToDouble(unencryptedBytes
, 0);
5074 value.Double
= doubleValue
;
5077 // We normalize to allow conversion across data types. SMALLMONEY is serialized into a MONEY.
5078 case TdsEnums
.SQLMONEYN
:
5079 case TdsEnums
.SQLMONEY4
:
5080 case TdsEnums
.SQLMONEY
:
5085 if (unencryptedBytes
.Length
!= 8) {
5089 mid
= BitConverter
.ToInt32(unencryptedBytes
, 0);
5090 lo
= BitConverter
.ToUInt32(unencryptedBytes
, 4);
5092 long l
= (((long)mid
) << 0x20) + ((long)lo
);
5093 value.SetToMoney(l
);
5097 case TdsEnums
.SQLDATETIMN
:
5099 goto case TdsEnums
.SQLDATETIM4
;
5102 goto case TdsEnums
.SQLDATETIME
;
5105 case TdsEnums
.SQLDATETIM4
:
5106 ushort daypartShort
, timepartShort
;
5107 if (unencryptedBytes
.Length
!= 4) {
5111 daypartShort
= (UInt16
)((unencryptedBytes
[1] << 8) + unencryptedBytes
[0]);
5112 timepartShort
= (UInt16
)((unencryptedBytes
[3] << 8) + unencryptedBytes
[2]);
5113 value.SetToDateTime(daypartShort
, timepartShort
* SqlDateTime
.SQLTicksPerMinute
);
5116 case TdsEnums
.SQLDATETIME
:
5119 if (unencryptedBytes
.Length
!= 8) {
5123 daypart
= BitConverter
.ToInt32(unencryptedBytes
, 0);
5124 timepart
= BitConverter
.ToUInt32(unencryptedBytes
, 4);
5125 value.SetToDateTime(daypart
, (int)timepart
);
5128 case TdsEnums
.SQLUNIQUEID
:
5130 Debug
.Assert(length
== 16, "invalid length for SqlGuid type!");
5131 value.SqlGuid
= new SqlGuid(unencryptedBytes
, true); // doesn't copy the byte array
5135 case TdsEnums
.SQLBINARY
:
5136 case TdsEnums
.SQLBIGBINARY
:
5137 case TdsEnums
.SQLBIGVARBINARY
:
5138 case TdsEnums
.SQLVARBINARY
:
5139 case TdsEnums
.SQLIMAGE
:
5141 // Note: Better not come here with plp data!!
5142 Debug
.Assert(length
<= TdsEnums
.MAXSIZE
, "Plp data decryption attempted");
5144 // If this is a fixed length type, pad with zeros to get to the fixed length size.
5145 if (tdsType
== TdsEnums
.SQLBINARY
|| tdsType
== TdsEnums
.SQLBIGBINARY
) {
5146 byte[] bytes
= new byte[md
.baseTI
.length
];
5147 Buffer
.BlockCopy(unencryptedBytes
, 0, bytes
, 0, unencryptedBytes
.Length
);
5148 unencryptedBytes
= bytes
;
5151 value.SqlBinary
= new SqlBinary(unencryptedBytes
, true); // doesn't copy the byte array
5155 case TdsEnums
.SQLDECIMALN
:
5156 case TdsEnums
.SQLNUMERICN
:
5157 // Check the sign from the first byte.
5159 byteValue
= unencryptedBytes
[index
++];
5160 bool fPositive
= (1 == byteValue
);
5162 // Now read the 4 next integers which contain the actual value.
5163 length
= checked((int)length
-1);
5164 int[] bits
= new int[4];
5165 int decLength
= length
>>2;
5166 for (int i
= 0; i
< decLength
; i
++) {
5167 // up to 16 bytes of data following the sign byte
5168 bits
[i
] = BitConverter
.ToInt32(unencryptedBytes
, index
);
5171 value.SetToDecimal (md
.baseTI
.precision
, md
.baseTI
.scale
, fPositive
, bits
);
5174 case TdsEnums
.SQLCHAR
:
5175 case TdsEnums
.SQLBIGCHAR
:
5176 case TdsEnums
.SQLVARCHAR
:
5177 case TdsEnums
.SQLBIGVARCHAR
:
5178 case TdsEnums
.SQLTEXT
:
5180 System
.Text
.Encoding encoding
= md
.baseTI
.encoding
;
5182 if (null == encoding
) {
5183 encoding
= _defaultEncoding
;
5186 if (null == encoding
) {
5187 ThrowUnsupportedCollationEncountered(stateObj
);
5190 string strValue
= encoding
.GetString(unencryptedBytes
, 0, length
);
5192 // If this is a fixed length type, pad with spaces to get to the fixed length size.
5193 if (tdsType
== TdsEnums
.SQLCHAR
|| tdsType
== TdsEnums
.SQLBIGCHAR
) {
5194 strValue
= strValue
.PadRight(md
.baseTI
.length
);
5197 value.SetToString(strValue
);
5201 case TdsEnums
.SQLNCHAR
:
5202 case TdsEnums
.SQLNVARCHAR
:
5203 case TdsEnums
.SQLNTEXT
:
5205 string strValue
= System
.Text
.Encoding
.Unicode
.GetString(unencryptedBytes
, 0, length
);
5207 // If this is a fixed length type, pad with spaces to get to the fixed length size.
5208 if (tdsType
== TdsEnums
.SQLNCHAR
) {
5209 strValue
= strValue
.PadRight(md
.baseTI
.length
/ ADP
.CharSize
);
5212 value.SetToString(strValue
);
5216 case TdsEnums
.SQLDATE
:
5217 Debug
.Assert(length
== 3, "invalid length for date type!");
5218 value.SetToDate(unencryptedBytes
);
5221 case TdsEnums
.SQLTIME
:
5222 // We normalize to maximum precision to allow conversion across different precisions.
5223 Debug
.Assert(length
== 5, "invalid length for time type!");
5224 value.SetToTime(unencryptedBytes
, length
, TdsEnums
.MAX_TIME_SCALE
, denormalizedScale
);
5227 case TdsEnums
.SQLDATETIME2
:
5228 // We normalize to maximum precision to allow conversion across different precisions.
5229 Debug
.Assert(length
== 8, "invalid length for datetime2 type!");
5230 value.SetToDateTime2(unencryptedBytes
, length
, TdsEnums
.MAX_TIME_SCALE
, denormalizedScale
);
5233 case TdsEnums
.SQLDATETIMEOFFSET
:
5234 // We normalize to maximum precision to allow conversion across different precisions.
5235 Debug
.Assert(length
== 10, "invalid length for datetimeoffset type!");
5236 value.SetToDateTimeOffset(unencryptedBytes
, length
, TdsEnums
.MAX_TIME_SCALE
, denormalizedScale
);
5240 MetaType metaType
= md
.baseTI
.metaType
;
5242 // If we don't have a metatype already, construct one to get the proper type name.
5243 if (metaType
== null) {
5244 metaType
= MetaType
.GetSqlDataType(tdsType
, userType
:0, length
:length
);
5247 throw SQL
.UnsupportedDatatypeEncryption(metaType
.TypeName
);
5253 internal bool TryReadSqlValue(SqlBuffer
value,
5256 TdsParserStateObject stateObj
,
5257 SqlCommandColumnEncryptionSetting columnEncryptionOverride
,
5258 string columnName
) {
5259 bool isPlp
= md
.metaType
.IsPlp
;
5260 byte tdsType
= md
.tdsType
;
5262 Debug
.Assert(isPlp
|| !IsNull(md
.metaType
, (ulong)length
), "null value should not get here!");
5264 // We must read the column value completely, no matter what length is passed in
5265 length
= Int32
.MaxValue
;
5268 //DEVNOTE: When modifying the following routines (for deserialization) please pay attention to
5269 // deserialization code in DecryptWithKey () method and modify it accordingly.
5271 case TdsEnums
.SQLDECIMALN
:
5272 case TdsEnums
.SQLNUMERICN
:
5273 if (!TryReadSqlDecimal(value, length
, md
.precision
, md
.scale
, stateObj
)) {
5278 case TdsEnums
.SQLUDT
:
5279 case TdsEnums
.SQLBINARY
:
5280 case TdsEnums
.SQLBIGBINARY
:
5281 case TdsEnums
.SQLBIGVARBINARY
:
5282 case TdsEnums
.SQLVARBINARY
:
5283 case TdsEnums
.SQLIMAGE
:
5286 // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
5288 // If we are given -1 for length, then we read the entire value,
5289 // otherwise only the requested amount, usually first chunk.
5291 if (!stateObj
.TryReadPlpBytes(ref b
, 0, length
, out ignored
)) {
5296 //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
5297 b
= new byte[length
];
5298 if (!stateObj
.TryReadByteArray(b
, 0, length
)) {
5304 && ((columnEncryptionOverride
== SqlCommandColumnEncryptionSetting
.Enabled
5305 || columnEncryptionOverride
== SqlCommandColumnEncryptionSetting
.ResultSetOnly
)
5306 || (columnEncryptionOverride
== SqlCommandColumnEncryptionSetting
.UseConnectionSetting
5307 && _connHandler
!= null && _connHandler
.ConnectionOptions
!= null
5308 && _connHandler
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
))) {
5310 // CipherInfo is present, decrypt and read
5311 byte[] unencryptedBytes
= SqlSecurityUtility
.DecryptWithKey(b
, md
.cipherMD
, _connHandler
.ConnectionOptions
.DataSource
);
5313 if (unencryptedBytes
!= null) {
5314 DeserializeUnencryptedValue(value, unencryptedBytes
, md
, stateObj
, md
.NormalizationRuleVersion
);
5317 catch (Exception e
) {
5318 throw SQL
.ColumnDecryptionFailed(columnName
, null, e
);
5322 value.SqlBinary
= new SqlBinary(b
, true); // doesn't copy the byte array
5326 case TdsEnums
.SQLCHAR
:
5327 case TdsEnums
.SQLBIGCHAR
:
5328 case TdsEnums
.SQLVARCHAR
:
5329 case TdsEnums
.SQLBIGVARCHAR
:
5330 case TdsEnums
.SQLTEXT
:
5331 case TdsEnums
.SQLNCHAR
:
5332 case TdsEnums
.SQLNVARCHAR
:
5333 case TdsEnums
.SQLNTEXT
:
5334 if (!TryReadSqlStringValue(value, tdsType
, length
, md
.encoding
, isPlp
, stateObj
)) {
5339 case TdsEnums
.SQLXMLTYPE
:
5340 // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader.
5341 SqlCachedBuffer sqlBuf
;
5342 if (!SqlCachedBuffer
.TryCreate(md
, this, stateObj
, out sqlBuf
)) {
5346 value.SqlCachedBuffer
= sqlBuf
;
5349 case TdsEnums
.SQLDATE
:
5350 case TdsEnums
.SQLTIME
:
5351 case TdsEnums
.SQLDATETIME2
:
5352 case TdsEnums
.SQLDATETIMEOFFSET
:
5353 if (!TryReadSqlDateTime(value, tdsType
, length
, md
.scale
, stateObj
)) {
5359 Debug
.Assert(!isPlp
, "ReadSqlValue calling ReadSqlValueInternal with plp data");
5360 if (!TryReadSqlValueInternal(value, tdsType
, length
, stateObj
)) {
5366 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));
5370 private bool TryReadSqlDateTime(SqlBuffer
value, byte tdsType
, int length
, byte scale
, TdsParserStateObject stateObj
) {
5371 byte[] datetimeBuffer
= new byte[length
];
5373 if (!stateObj
.TryReadByteArray(datetimeBuffer
, 0, length
)) {
5378 case TdsEnums
.SQLDATE
:
5379 Debug
.Assert(length
== 3, "invalid length for date type!");
5380 value.SetToDate(datetimeBuffer
);
5383 case TdsEnums
.SQLTIME
:
5384 Debug
.Assert(3 <= length
&& length
<= 5, "invalid length for time type!");
5385 value.SetToTime(datetimeBuffer
, length
, scale
, scale
);
5388 case TdsEnums
.SQLDATETIME2
:
5389 Debug
.Assert(6 <= length
&& length
<= 8, "invalid length for datetime2 type!");
5390 value.SetToDateTime2(datetimeBuffer
, length
, scale
, scale
);
5393 case TdsEnums
.SQLDATETIMEOFFSET
:
5394 Debug
.Assert(8 <= length
&& length
<= 10, "invalid length for datetimeoffset type!");
5395 value.SetToDateTimeOffset(datetimeBuffer
, length
, scale
, scale
);
5399 Debug
.Assert(false, "ReadSqlDateTime is called with the wrong tdsType");
5406 internal bool TryReadSqlValueInternal(SqlBuffer
value, byte tdsType
, int length
, TdsParserStateObject stateObj
) {
5408 case TdsEnums
.SQLBIT
:
5409 case TdsEnums
.SQLBITN
:
5410 Debug
.Assert(length
== 1, "invalid length for SqlBoolean type!");
5412 if (!stateObj
.TryReadByte(out byteValue
)) {
5415 value.Boolean
= (byteValue
!= 0);
5418 case TdsEnums
.SQLINTN
:
5420 goto case TdsEnums
.SQLINT1
;
5422 else if (length
== 2) {
5423 goto case TdsEnums
.SQLINT2
;
5425 else if (length
== 4) {
5426 goto case TdsEnums
.SQLINT4
;
5429 goto case TdsEnums
.SQLINT8
;
5432 case TdsEnums
.SQLINT1
:
5433 Debug
.Assert(length
== 1, "invalid length for SqlByte type!");
5434 if (!stateObj
.TryReadByte(out byteValue
)) {
5437 value.Byte
= byteValue
;
5440 case TdsEnums
.SQLINT2
:
5441 Debug
.Assert(length
== 2, "invalid length for SqlInt16 type!");
5443 if (!stateObj
.TryReadInt16(out shortValue
)) {
5446 value.Int16
= shortValue
;
5449 case TdsEnums
.SQLINT4
:
5450 Debug
.Assert(length
== 4, "invalid length for SqlInt32 type!");
5452 if (!stateObj
.TryReadInt32(out intValue
)) {
5455 value.Int32
= intValue
;
5458 case TdsEnums
.SQLINT8
:
5459 Debug
.Assert(length
== 8, "invalid length for SqlInt64 type!");
5461 if (!stateObj
.TryReadInt64(out longValue
)) {
5464 value.Int64
= longValue
;
5467 case TdsEnums
.SQLFLTN
:
5469 goto case TdsEnums
.SQLFLT4
;
5472 goto case TdsEnums
.SQLFLT8
;
5475 case TdsEnums
.SQLFLT4
:
5476 Debug
.Assert(length
== 4, "invalid length for SqlSingle type!");
5478 if (!stateObj
.TryReadSingle(out singleValue
)) {
5481 value.Single
= singleValue
;
5484 case TdsEnums
.SQLFLT8
:
5485 Debug
.Assert(length
== 8, "invalid length for SqlDouble type!");
5487 if (!stateObj
.TryReadDouble(out doubleValue
)) {
5490 value.Double
= doubleValue
;
5493 case TdsEnums
.SQLMONEYN
:
5495 goto case TdsEnums
.SQLMONEY4
;
5498 goto case TdsEnums
.SQLMONEY
;
5501 case TdsEnums
.SQLMONEY
:
5506 if (!stateObj
.TryReadInt32(out mid
)) {
5509 if (!stateObj
.TryReadUInt32(out lo
)) {
5513 long l
= (((long)mid
) << 0x20) + ((long)lo
);
5515 value.SetToMoney(l
);
5519 case TdsEnums
.SQLMONEY4
:
5520 if (!stateObj
.TryReadInt32(out intValue
)) {
5523 value.SetToMoney(intValue
);
5526 case TdsEnums
.SQLDATETIMN
:
5528 goto case TdsEnums
.SQLDATETIM4
;
5531 goto case TdsEnums
.SQLDATETIME
;
5534 case TdsEnums
.SQLDATETIM4
:
5535 ushort daypartShort
, timepartShort
;
5536 if (!stateObj
.TryReadUInt16(out daypartShort
)) {
5539 if (!stateObj
.TryReadUInt16(out timepartShort
)) {
5542 value.SetToDateTime(daypartShort
, timepartShort
* SqlDateTime
.SQLTicksPerMinute
);
5545 case TdsEnums
.SQLDATETIME
:
5548 if (!stateObj
.TryReadInt32(out daypart
)) {
5551 if (!stateObj
.TryReadUInt32(out timepart
)) {
5554 value.SetToDateTime(daypart
, (int)timepart
);
5557 case TdsEnums
.SQLUNIQUEID
:
5559 Debug
.Assert(length
== 16, "invalid length for SqlGuid type!");
5561 byte[] b
= new byte[length
];
5563 if (!stateObj
.TryReadByteArray(b
, 0, length
)) {
5566 value.SqlGuid
= new SqlGuid(b
, true); // doesn't copy the byte array
5570 case TdsEnums
.SQLBINARY
:
5571 case TdsEnums
.SQLBIGBINARY
:
5572 case TdsEnums
.SQLBIGVARBINARY
:
5573 case TdsEnums
.SQLVARBINARY
:
5574 case TdsEnums
.SQLIMAGE
:
5576 // Note: Better not come here with plp data!!
5577 Debug
.Assert(length
<= TdsEnums
.MAXSIZE
);
5578 byte[] b
= new byte[length
];
5579 if (!stateObj
.TryReadByteArray(b
, 0, length
)) {
5582 value.SqlBinary
= new SqlBinary(b
, true); // doesn't copy the byte array
5587 case TdsEnums
.SQLVARIANT
:
5588 if (!TryReadSqlVariant(value, length
, stateObj
)) {
5594 Debug
.Assert(false, "Unknown SqlType!" + tdsType
.ToString(CultureInfo
.InvariantCulture
));
5602 // Read in a SQLVariant
5604 // SQLVariant looks like:
5609 // BYTE[] Properties
5612 internal bool TryReadSqlVariant(SqlBuffer
value, int lenTotal
, TdsParserStateObject stateObj
) {
5613 Debug
.Assert(_isShiloh
== true, "Shouldn't be dealing with sql_variaint in pre-SQL2000 server!");
5614 // get the SQLVariant type
5616 if (!stateObj
.TryReadByte(out type
)) {
5619 ushort lenMax
= 0; // maximum lenData of value inside variant
5623 if (!stateObj
.TryReadByte(out cbPropsActual
)) {
5626 MetaType mt
= MetaType
.GetSqlDataType(type
, 0 /*no user datatype*/, 0 /* no lenData, non-nullable type */);
5627 byte cbPropsExpected
= mt
.PropBytes
;
5629 int lenConsumed
= TdsEnums
.SQLVARIANT_SIZE
+ cbPropsActual
; // type, count of propBytes, and actual propBytes
5630 int lenData
= lenTotal
- lenConsumed
; // length of actual data
5632 // read known properties and skip unknown properties
5633 Debug
.Assert(cbPropsActual
>= cbPropsExpected
, "cbPropsActual is less that cbPropsExpected!");
5636 // now read the value
5639 case TdsEnums
.SQLBIT
:
5640 case TdsEnums
.SQLINT1
:
5641 case TdsEnums
.SQLINT2
:
5642 case TdsEnums
.SQLINT4
:
5643 case TdsEnums
.SQLINT8
:
5644 case TdsEnums
.SQLFLT4
:
5645 case TdsEnums
.SQLFLT8
:
5646 case TdsEnums
.SQLMONEY
:
5647 case TdsEnums
.SQLMONEY4
:
5648 case TdsEnums
.SQLDATETIME
:
5649 case TdsEnums
.SQLDATETIM4
:
5650 case TdsEnums
.SQLUNIQUEID
:
5651 if (!TryReadSqlValueInternal(value, type
, lenData
, stateObj
)) {
5656 case TdsEnums
.SQLDECIMALN
:
5657 case TdsEnums
.SQLNUMERICN
:
5659 Debug
.Assert(cbPropsExpected
== 2, "SqlVariant: invalid PropBytes for decimal/numeric type!");
5662 if (!stateObj
.TryReadByte(out precision
)) {
5666 if (!stateObj
.TryReadByte(out scale
)) {
5670 // skip over unknown properties
5671 if (cbPropsActual
> cbPropsExpected
) {
5672 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5677 if (!TryReadSqlDecimal(value, TdsEnums
.MAX_NUMERIC_LEN
, precision
, scale
, stateObj
)) {
5683 case TdsEnums
.SQLBIGBINARY
:
5684 case TdsEnums
.SQLBIGVARBINARY
:
5685 //Debug.Assert(TdsEnums.VARNULL == lenData, "SqlVariant: data length for Binary indicates null?");
5686 Debug
.Assert(cbPropsExpected
== 2, "SqlVariant: invalid PropBytes for binary type!");
5688 if (!stateObj
.TryReadUInt16(out lenMax
)) {
5691 Debug
.Assert(lenMax
!= TdsEnums
.SQL_USHORTVARMAXLEN
, "bigvarbinary(max) in a sqlvariant");
5693 // skip over unknown properties
5694 if (cbPropsActual
> cbPropsExpected
) {
5695 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5700 goto case TdsEnums
.SQLBIT
;
5702 case TdsEnums
.SQLBIGCHAR
:
5703 case TdsEnums
.SQLBIGVARCHAR
:
5704 case TdsEnums
.SQLNCHAR
:
5705 case TdsEnums
.SQLNVARCHAR
:
5707 Debug
.Assert(cbPropsExpected
== 7, "SqlVariant: invalid PropBytes for character type!");
5709 SqlCollation collation
;
5710 if (!TryProcessCollation(stateObj
, out collation
)) {
5714 if (!stateObj
.TryReadUInt16(out lenMax
)) {
5717 Debug
.Assert(lenMax
!= TdsEnums
.SQL_USHORTVARMAXLEN
, "bigvarchar(max) or nvarchar(max) in a sqlvariant");
5719 // skip over unknown properties
5720 if (cbPropsActual
> cbPropsExpected
) {
5721 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5726 Encoding encoding
= Encoding
.GetEncoding(GetCodePage(collation
, stateObj
));
5727 if (!TryReadSqlStringValue(value, type
, lenData
, encoding
, false, stateObj
)) {
5732 case TdsEnums
.SQLDATE
:
5733 if (!TryReadSqlDateTime(value, type
, lenData
, 0, stateObj
)) {
5738 case TdsEnums
.SQLTIME
:
5739 case TdsEnums
.SQLDATETIME2
:
5740 case TdsEnums
.SQLDATETIMEOFFSET
:
5742 Debug
.Assert(cbPropsExpected
== 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!");
5745 if (!stateObj
.TryReadByte(out scale
)) {
5749 // skip over unknown properties
5750 if (cbPropsActual
> cbPropsExpected
) {
5751 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5756 if (!TryReadSqlDateTime(value, type
, lenData
, scale
, stateObj
)) {
5763 Debug
.Assert(false, "Unknown tds type in SqlVariant!" + type
.ToString(CultureInfo
.InvariantCulture
));
5771 // Translates a com+ object -> SqlVariant
5772 // when the type is ambiguous, we always convert to the bigger type
5773 // note that we also write out the maxlen and actuallen members (4 bytes each)
5774 // in addition to the SQLVariant structure
5776 internal Task
WriteSqlVariantValue(object value, int length
, int offset
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
5777 Debug
.Assert(_isShiloh
== true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
5779 // handle null values
5780 if (ADP
.IsNull(value)) {
5781 WriteInt(TdsEnums
.FIXEDNULL
, stateObj
); //maxlen
5782 WriteInt(TdsEnums
.FIXEDNULL
, stateObj
); //actuallen
5786 MetaType mt
= MetaType
.GetMetaTypeFromValue(value);
5788 // Special case data type correction for SqlMoney inside a SqlVariant.
5789 if ((TdsEnums
.SQLNUMERICN
== mt
.TDSType
) && (8 == length
)) {
5790 // The caller will coerce all SqlTypes to native CLR types, which means SqlMoney will
5791 // coerce to decimal/SQLNUMERICN (via SqlMoney.Value call). In the case where the original
5792 // value was SqlMoney the caller will also pass in the metadata length for the SqlMoney type
5793 // which is 8 bytes. To honor the intent of the caller here we coerce this special case
5794 // input back to SqlMoney from decimal/SQLNUMERICN.
5795 mt
= MetaType
.GetMetaTypeFromValue(new SqlMoney((decimal)value));
5798 if (mt
.IsAnsiType
) {
5799 length
= GetEncodingCharLength((string)value, length
, 0, _defaultEncoding
);
5802 // max and actual len are equal to
5803 // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
5804 WriteInt(TdsEnums
.SQLVARIANT_SIZE
+ mt
.PropBytes
+ length
, stateObj
); // maxLen
5805 WriteInt(TdsEnums
.SQLVARIANT_SIZE
+ mt
.PropBytes
+ length
, stateObj
); // actualLen
5807 // write the SQLVariant header (type and cbPropBytes)
5808 stateObj
.WriteByte(mt
.TDSType
);
5809 stateObj
.WriteByte(mt
.PropBytes
);
5811 // now write the actual PropBytes and data
5812 switch (mt
.TDSType
) {
5813 case TdsEnums
.SQLFLT4
:
5814 WriteFloat((Single
)value, stateObj
);
5817 case TdsEnums
.SQLFLT8
:
5818 WriteDouble((Double
)value, stateObj
);
5821 case TdsEnums
.SQLINT8
:
5822 WriteLong((Int64
)value, stateObj
);
5825 case TdsEnums
.SQLINT4
:
5826 WriteInt((Int32
)value, stateObj
);
5829 case TdsEnums
.SQLINT2
:
5830 WriteShort((Int16
)value, stateObj
);
5833 case TdsEnums
.SQLINT1
:
5834 stateObj
.WriteByte((byte)value);
5837 case TdsEnums
.SQLBIT
:
5838 if ((bool)value == true)
5839 stateObj
.WriteByte(1);
5841 stateObj
.WriteByte(0);
5845 case TdsEnums
.SQLBIGVARBINARY
:
5847 byte[] b
= (byte[])value;
5849 WriteShort(length
, stateObj
); // propbytes: varlen
5850 return stateObj
.WriteByteArray(b
, length
, offset
, canAccumulate
);
5853 case TdsEnums
.SQLBIGVARCHAR
:
5855 string s
= (string)value;
5857 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
5858 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
5859 WriteShort(length
, stateObj
); // propbyte: varlen
5860 return WriteEncodingChar(s
, _defaultEncoding
, stateObj
, canAccumulate
);
5863 case TdsEnums
.SQLUNIQUEID
:
5865 System
.Guid guid
= (System
.Guid
)value;
5866 byte[] b
= guid
.ToByteArray();
5868 Debug
.Assert((length
== b
.Length
) && (length
== 16), "Invalid length for guid type in com+ object");
5869 stateObj
.WriteByteArray(b
, length
, 0);
5873 case TdsEnums
.SQLNVARCHAR
:
5875 string s
= (string)value;
5877 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
5878 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
5879 WriteShort(length
, stateObj
); // propbyte: varlen
5881 // string takes cchar, not cbyte so convert
5883 return WriteString(s
, length
, offset
, stateObj
, canAccumulate
);
5886 case TdsEnums
.SQLDATETIME
:
5888 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, 8);
5890 WriteInt(dt
.days
, stateObj
);
5891 WriteInt(dt
.time
, stateObj
);
5895 case TdsEnums
.SQLMONEY
:
5897 WriteCurrency((Decimal
)value, 8, stateObj
);
5901 case TdsEnums
.SQLNUMERICN
:
5903 stateObj
.WriteByte(mt
.Precision
); //propbytes: precision
5904 stateObj
.WriteByte((byte)((Decimal
.GetBits((Decimal
)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
5905 WriteDecimal((Decimal
)value, stateObj
);
5909 case TdsEnums
.SQLTIME
:
5910 stateObj
.WriteByte(mt
.Scale
); //propbytes: scale
5911 WriteTime((TimeSpan
)value, mt
.Scale
, length
, stateObj
);
5914 case TdsEnums
.SQLDATETIMEOFFSET
:
5915 stateObj
.WriteByte(mt
.Scale
); //propbytes: scale
5916 WriteDateTimeOffset((DateTimeOffset
)value, mt
.Scale
, length
, stateObj
);
5920 Debug
.Assert(false, "unknown tds type for sqlvariant!");
5923 // return point for accumulated writes, note: non-accumulated writes returned from their case statements
5927 // todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider
5928 // combining these tow methods.
5931 // Translates a com+ object -> SqlVariant
5932 // when the type is ambiguous, we always convert to the bigger type
5933 // note that we also write out the maxlen and actuallen members (4 bytes each)
5934 // in addition to the SQLVariant structure
5936 // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value.
5937 // Therefore the sql_variant value must not include the MaxLength. This is the major difference
5938 // between this method and WriteSqlVariantValue above.
5940 internal Task
WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
5941 Debug
.Assert(_isShiloh
== true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
5943 // handle null values
5944 if ((null == value) || (DBNull
.Value
== value)) {
5945 WriteInt(TdsEnums
.FIXEDNULL
, stateObj
);
5949 MetaType metatype
= MetaType
.GetMetaTypeFromValue(value);
5952 if (metatype
.IsAnsiType
) {
5953 length
= GetEncodingCharLength((string)value, length
, 0, _defaultEncoding
);
5956 switch (metatype
.TDSType
) {
5957 case TdsEnums
.SQLFLT4
:
5958 WriteSqlVariantHeader(6, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5959 WriteFloat((Single
)value, stateObj
);
5962 case TdsEnums
.SQLFLT8
:
5963 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5964 WriteDouble((Double
)value, stateObj
);
5967 case TdsEnums
.SQLINT8
:
5968 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5969 WriteLong((Int64
)value, stateObj
);
5972 case TdsEnums
.SQLINT4
:
5973 WriteSqlVariantHeader(6, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5974 WriteInt((Int32
)value, stateObj
);
5977 case TdsEnums
.SQLINT2
:
5978 WriteSqlVariantHeader(4, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5979 WriteShort((Int16
)value, stateObj
);
5982 case TdsEnums
.SQLINT1
:
5983 WriteSqlVariantHeader(3, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5984 stateObj
.WriteByte((byte)value);
5987 case TdsEnums
.SQLBIT
:
5988 WriteSqlVariantHeader(3, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5989 if ((bool)value == true)
5990 stateObj
.WriteByte(1);
5992 stateObj
.WriteByte(0);
5996 case TdsEnums
.SQLBIGVARBINARY
:
5998 byte[] b
= (byte[])value;
6001 WriteSqlVariantHeader(4 + length
, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6002 WriteShort(length
, stateObj
); // propbytes: varlen
6003 return stateObj
.WriteByteArray(b
, length
, 0, canAccumulate
);
6006 case TdsEnums
.SQLBIGVARCHAR
:
6008 string s
= (string)value;
6011 WriteSqlVariantHeader(9 + length
, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6012 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
6013 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
6014 WriteShort(length
, stateObj
);
6015 return WriteEncodingChar(s
, _defaultEncoding
, stateObj
, canAccumulate
);
6018 case TdsEnums
.SQLUNIQUEID
:
6020 System
.Guid guid
= (System
.Guid
)value;
6021 byte[] b
= guid
.ToByteArray();
6024 Debug
.Assert(length
== 16, "Invalid length for guid type in com+ object");
6025 WriteSqlVariantHeader(18, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6026 stateObj
.WriteByteArray(b
, length
, 0);
6030 case TdsEnums
.SQLNVARCHAR
:
6032 string s
= (string)value;
6034 length
= s
.Length
* 2;
6035 WriteSqlVariantHeader(9 + length
, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6036 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
6037 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
6038 WriteShort(length
, stateObj
); // propbyte: varlen
6040 // string takes cchar, not cbyte so convert
6042 return WriteString(s
, length
, 0, stateObj
, canAccumulate
);
6045 case TdsEnums
.SQLDATETIME
:
6047 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, 8);
6049 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6050 WriteInt(dt
.days
, stateObj
);
6051 WriteInt(dt
.time
, stateObj
);
6055 case TdsEnums
.SQLMONEY
:
6057 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6058 WriteCurrency((Decimal
)value, 8, stateObj
);
6062 case TdsEnums
.SQLNUMERICN
:
6064 WriteSqlVariantHeader(21, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6065 stateObj
.WriteByte(metatype
.Precision
); //propbytes: precision
6066 stateObj
.WriteByte((byte)((Decimal
.GetBits((Decimal
)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
6067 WriteDecimal((Decimal
)value, stateObj
);
6071 case TdsEnums
.SQLTIME
:
6072 WriteSqlVariantHeader(8, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6073 stateObj
.WriteByte(metatype
.Scale
); //propbytes: scale
6074 WriteTime((TimeSpan
)value, metatype
.Scale
, 5, stateObj
);
6077 case TdsEnums
.SQLDATETIMEOFFSET
:
6078 WriteSqlVariantHeader(13, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6079 stateObj
.WriteByte(metatype
.Scale
); //propbytes: scale
6080 WriteDateTimeOffset((DateTimeOffset
)value, metatype
.Scale
, 10, stateObj
);
6084 Debug
.Assert(false, "unknown tds type for sqlvariant!");
6087 // return point for accumualated writes, note: non-accumulated writes returned from their case statements
6091 internal void WriteSqlVariantHeader(int length
, byte tdstype
, byte propbytes
, TdsParserStateObject stateObj
) {
6092 WriteInt(length
, stateObj
);
6093 stateObj
.WriteByte(tdstype
);
6094 stateObj
.WriteByte(propbytes
);
6097 internal void WriteSqlVariantDateTime2(DateTime
value, TdsParserStateObject stateObj
)
6099 MSS
.SmiMetaData dateTime2MetaData
= MSS
.SmiMetaData
.DefaultDateTime2
;
6100 // NOTE: 3 bytes added here to support additional header information for variant - internal type, scale prop, scale
6101 WriteSqlVariantHeader((int)(dateTime2MetaData
.MaxLength
+ 3), TdsEnums
.SQLDATETIME2
, 1 /* one scale prop */, stateObj
);
6102 stateObj
.WriteByte(dateTime2MetaData
.Scale
); //scale property
6103 WriteDateTime2(value, dateTime2MetaData
.Scale
, (int)(dateTime2MetaData
.MaxLength
), stateObj
);
6106 internal void WriteSqlVariantDate(DateTime
value, TdsParserStateObject stateObj
)
6108 MSS
.SmiMetaData dateMetaData
= MSS
.SmiMetaData
.DefaultDate
;
6109 // NOTE: 2 bytes added here to support additional header information for variant - internal type, scale prop (ignoring scale here)
6110 WriteSqlVariantHeader((int)(dateMetaData
.MaxLength
+ 2), TdsEnums
.SQLDATE
, 0 /* one scale prop */, stateObj
);
6111 WriteDate(value, stateObj
);
6114 private byte[] SerializeSqlMoney(SqlMoney
value, int length
, TdsParserStateObject stateObj
) {
6115 return SerializeCurrency(value.Value
, length
, stateObj
);
6118 private void WriteSqlMoney(SqlMoney
value, int length
, TdsParserStateObject stateObj
) {
6120 int[] bits
= Decimal
.GetBits(value.Value
);
6122 // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6123 bool isNeg
= (0 != (bits
[3] & unchecked((int)0x80000000)));
6124 long l
= ((long)(uint)bits
[1]) << 0x20 | (uint)bits
[0];
6130 Decimal decimalValue
= value.Value
;
6132 // validate the value can be represented as a small money
6133 if (decimalValue
< TdsEnums
.SQL_SMALL_MONEY_MIN
|| decimalValue
> TdsEnums
.SQL_SMALL_MONEY_MAX
) {
6134 throw SQL
.MoneyOverflow(decimalValue
.ToString(CultureInfo
.InvariantCulture
));
6137 WriteInt((int)l
, stateObj
);
6140 WriteInt((int)(l
>> 0x20), stateObj
);
6141 WriteInt((int)l
, stateObj
);
6145 private byte[] SerializeCurrency(Decimal
value, int length
, TdsParserStateObject stateObj
) {
6146 SqlMoney m
= new SqlMoney(value);
6147 int[] bits
= Decimal
.GetBits(m
.Value
);
6149 // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6150 bool isNeg
= (0 != (bits
[3] & unchecked((int)0x80000000)));
6151 long l
= ((long)(uint)bits
[1]) << 0x20 | (uint)bits
[0];
6157 // validate the value can be represented as a small money
6158 if (value < TdsEnums
.SQL_SMALL_MONEY_MIN
|| value > TdsEnums
.SQL_SMALL_MONEY_MAX
) {
6159 throw SQL
.MoneyOverflow(value.ToString(CultureInfo
.InvariantCulture
));
6162 // We normalize to allow conversion across data types. SMALLMONEY is serialized into a MONEY.
6166 Debug
.Assert (8 == length
, "invalid length in SerializeCurrency");
6167 if (null == stateObj
._bLongBytes
) {
6168 stateObj
._bLongBytes
= new byte[8];
6171 byte[] bytes
= stateObj
._bLongBytes
;
6174 byte[] bytesPart
= SerializeInt((int)(l
>> 0x20), stateObj
);
6175 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6178 bytesPart
= SerializeInt((int)l
, stateObj
);
6179 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6184 private void WriteCurrency(Decimal
value, int length
, TdsParserStateObject stateObj
) {
6185 SqlMoney m
= new SqlMoney(value);
6186 int[] bits
= Decimal
.GetBits(m
.Value
);
6188 // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6189 bool isNeg
= (0 != (bits
[3] & unchecked((int)0x80000000)));
6190 long l
= ((long)(uint)bits
[1]) << 0x20 | (uint)bits
[0];
6196 // validate the value can be represented as a small money
6197 if (value < TdsEnums
.SQL_SMALL_MONEY_MIN
|| value > TdsEnums
.SQL_SMALL_MONEY_MAX
) {
6198 throw SQL
.MoneyOverflow(value.ToString(CultureInfo
.InvariantCulture
));
6201 WriteInt((int)l
, stateObj
);
6204 WriteInt((int)(l
>> 0x20), stateObj
);
6205 WriteInt((int)l
, stateObj
);
6209 private byte[] SerializeDate(DateTime
value) {
6210 long days
= value.Subtract(DateTime
.MinValue
).Days
;
6211 return SerializePartialLong(days
, 3);
6214 private void WriteDate(DateTime
value, TdsParserStateObject stateObj
) {
6215 long days
= value.Subtract(DateTime
.MinValue
).Days
;
6216 WritePartialLong(days
, 3, stateObj
);
6219 private byte[] SerializeTime(TimeSpan
value, byte scale
, int length
) {
6220 if (0 > value.Ticks
|| value.Ticks
>= TimeSpan
.TicksPerDay
) {
6221 throw SQL
.TimeOverflow(value.ToString());
6224 long time
= value.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
];
6226 // We normalize to maximum precision to allow conversion across different precisions.
6227 time
= time
* TdsEnums
.TICKS_FROM_SCALE
[scale
];
6228 length
= TdsEnums
.MAX_TIME_LENGTH
;
6230 return SerializePartialLong(time
, length
);
6233 private void WriteTime(TimeSpan
value, byte scale
, int length
, TdsParserStateObject stateObj
) {
6234 if (0 > value.Ticks
|| value.Ticks
>= TimeSpan
.TicksPerDay
) {
6235 throw SQL
.TimeOverflow(value.ToString());
6237 long time
= value.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
];
6238 WritePartialLong(time
, length
, stateObj
);
6241 private byte[] SerializeDateTime2(DateTime
value, byte scale
, int length
) {
6242 long time
= value.TimeOfDay
.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
6244 // We normalize to maximum precision to allow conversion across different precisions.
6245 time
= time
* TdsEnums
.TICKS_FROM_SCALE
[scale
];
6246 length
= TdsEnums
.MAX_DATETIME2_LENGTH
;
6248 byte[] bytes
= new byte[length
];
6252 bytesPart
= SerializePartialLong(time
, length
- 3);
6253 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, length
- 3);
6254 current
+= length
- 3;
6256 bytesPart
= SerializeDate(value);
6257 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 3);
6262 private void WriteDateTime2(DateTime
value, byte scale
, int length
, TdsParserStateObject stateObj
) {
6263 long time
= value.TimeOfDay
.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
6264 WritePartialLong(time
, length
- 3, stateObj
);
6265 WriteDate(value, stateObj
);
6268 private byte[] SerializeDateTimeOffset(DateTimeOffset
value, byte scale
, int length
) {
6272 bytesPart
= SerializeDateTime2(value.UtcDateTime
, scale
, length
- 2);
6274 // We need to allocate the array after we have received the length of the serialized value
6275 // since it might be higher due to normalization.
6276 length
= bytesPart
.Length
+ 2;
6277 byte[] bytes
= new byte[length
];
6279 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, length
- 2);
6280 current
+= length
- 2;
6282 Int16 offset
= (Int16
)value.Offset
.TotalMinutes
;
6283 bytes
[current
++] = (byte)(offset
& 0xff);
6284 bytes
[current
++] = (byte)((offset
>> 8) & 0xff);
6289 private void WriteDateTimeOffset(DateTimeOffset
value, byte scale
, int length
, TdsParserStateObject stateObj
) {
6290 WriteDateTime2(value.UtcDateTime
, scale
, length
- 2, stateObj
);
6291 Int16 offset
= (Int16
)value.Offset
.TotalMinutes
;
6292 stateObj
.WriteByte((byte)(offset
& 0xff));
6293 stateObj
.WriteByte((byte)((offset
>> 8) & 0xff));
6296 private bool TryReadSqlDecimal(SqlBuffer
value, int length
, byte precision
, byte scale
, TdsParserStateObject stateObj
) {
6298 if (!stateObj
.TryReadByte(out byteValue
)) {
6301 bool fPositive
= (1 == byteValue
);
6303 length
= checked((int)length
-1);
6306 if (!TryReadDecimalBits(length
, stateObj
, out bits
)) {
6310 value.SetToDecimal(precision
, scale
, fPositive
, bits
);
6314 // @devnote: length should be size of decimal without the sign
6315 // @devnote: sign should have already been read off the wire
6316 private bool TryReadDecimalBits(int length
, TdsParserStateObject stateObj
, out int[] bits
) {
6317 bits
= stateObj
._decimalBits
; // used alloc'd array if we have one already
6323 for (i
= 0; i
< bits
.Length
; i
++)
6327 Debug
.Assert((length
> 0) &&
6328 (length
<= TdsEnums
.MAX_NUMERIC_LEN
- 1) &&
6329 (length
% 4 == 0), "decimal should have 4, 8, 12, or 16 bytes of data");
6331 int decLength
= length
>> 2;
6333 for (i
= 0; i
< decLength
; i
++) {
6334 // up to 16 bytes of data following the sign byte
6335 if (!stateObj
.TryReadInt32(out bits
[i
])) {
6343 static internal SqlDecimal
AdjustSqlDecimalScale(SqlDecimal d
, int newScale
) {
6344 if (d
.Scale
!= newScale
) {
6345 return SqlDecimal
.AdjustScale(d
, newScale
- d
.Scale
, false /* Don't round, truncate. MDAC 69229 */);
6351 static internal decimal AdjustDecimalScale(decimal value, int newScale
) {
6352 int oldScale
= (Decimal
.GetBits(value)[3] & 0x00ff0000) >> 0x10;
6354 if (newScale
!= oldScale
) {
6355 SqlDecimal num
= new SqlDecimal(value);
6357 num
= SqlDecimal
.AdjustScale(num
, newScale
- oldScale
, false /* Don't round, truncate. MDAC 69229 */);
6364 internal byte[] SerializeSqlDecimal(SqlDecimal d
, TdsParserStateObject stateObj
) {
6365 if (null == stateObj
._bDecimalBytes
) {
6366 stateObj
._bDecimalBytes
= new byte[17];
6369 byte[] bytes
= stateObj
._bDecimalBytes
;
6374 bytes
[current
++] = 1;
6376 bytes
[current
++] = 0;
6378 byte[] bytesPart
= SerializeUnsignedInt(d
.m_data1
, stateObj
);
6379 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6381 bytesPart
= SerializeUnsignedInt(d
.m_data2
, stateObj
);
6382 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6384 bytesPart
= SerializeUnsignedInt(d
.m_data3
, stateObj
);
6385 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6387 bytesPart
= SerializeUnsignedInt(d
.m_data4
, stateObj
);
6388 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6393 internal void WriteSqlDecimal(SqlDecimal d
, TdsParserStateObject stateObj
) {
6396 stateObj
.WriteByte(1);
6398 stateObj
.WriteByte(0);
6400 WriteUnsignedInt(d
.m_data1
, stateObj
);
6401 WriteUnsignedInt(d
.m_data2
, stateObj
);
6402 WriteUnsignedInt(d
.m_data3
, stateObj
);
6403 WriteUnsignedInt(d
.m_data4
, stateObj
);
6406 private byte[] SerializeDecimal(decimal value, TdsParserStateObject stateObj
) {
6407 int[] decimalBits
= Decimal
.GetBits(value);
6408 if (null == stateObj
._bDecimalBytes
) {
6409 stateObj
._bDecimalBytes
= new byte[17];
6412 byte[] bytes
= stateObj
._bDecimalBytes
;
6416 Returns a binary representation of a Decimal. The return value is an integer
6417 array with four elements. Elements 0, 1, and 2 contain the low, middle, and
6418 high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
6419 the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
6420 unused; bits 16-23 contain a value between 0 and 28, indicating the power of
6421 10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
6422 30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
6423 meaning positive and 1 meaning negative.
6425 SQLDECIMAL/SQLNUMERIC has a byte stream of:
6427 BYTE sign; // 1 if positive, 0 if negative
6431 For TDS 7.0 and above, there are always 17 bytes of data
6434 // write the sign (note that COM and SQL are opposite)
6435 if (0x80000000 == (decimalBits
[3] & 0x80000000))
6436 bytes
[current
++] = 0;
6438 bytes
[current
++] = 1;
6440 byte[] bytesPart
= SerializeInt(decimalBits
[0], stateObj
);
6441 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6443 bytesPart
= SerializeInt(decimalBits
[1], stateObj
);
6444 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6446 bytesPart
= SerializeInt(decimalBits
[2], stateObj
);
6447 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6449 bytesPart
= SerializeInt(0, stateObj
);
6450 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6455 private void WriteDecimal(decimal value, TdsParserStateObject stateObj
) {
6456 stateObj
._decimalBits
= Decimal
.GetBits(value);
6457 Debug
.Assert(null != stateObj
._decimalBits
, "decimalBits should be filled in at TdsExecuteRPC time");
6460 Returns a binary representation of a Decimal. The return value is an integer
6461 array with four elements. Elements 0, 1, and 2 contain the low, middle, and
6462 high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
6463 the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
6464 unused; bits 16-23 contain a value between 0 and 28, indicating the power of
6465 10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
6466 30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
6467 meaning positive and 1 meaning negative.
6469 SQLDECIMAL/SQLNUMERIC has a byte stream of:
6471 BYTE sign; // 1 if positive, 0 if negative
6475 For TDS 7.0 and above, there are always 17 bytes of data
6478 // write the sign (note that COM and SQL are opposite)
6479 if (0x80000000 == (stateObj
._decimalBits
[3] & 0x80000000))
6480 stateObj
.WriteByte(0);
6482 stateObj
.WriteByte(1);
6484 WriteInt(stateObj
._decimalBits
[0], stateObj
);
6485 WriteInt(stateObj
._decimalBits
[1], stateObj
);
6486 WriteInt(stateObj
._decimalBits
[2], stateObj
);
6487 WriteInt(0, stateObj
);
6490 private void WriteIdentifier(string s
, TdsParserStateObject stateObj
) {
6492 stateObj
.WriteByte(checked((byte)s
.Length
));
6493 WriteString(s
, stateObj
);
6496 stateObj
.WriteByte((byte)0);
6500 private void WriteIdentifierWithShortLength(string s
, TdsParserStateObject stateObj
) {
6502 WriteShort(checked((short)s
.Length
), stateObj
);
6503 WriteString(s
, stateObj
);
6506 WriteShort(0, stateObj
);
6510 private Task
WriteString(string s
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6511 return WriteString(s
, s
.Length
, 0, stateObj
, canAccumulate
);
6514 internal byte[] SerializeCharArray(char[] carr
, int length
, int offset
) {
6515 int cBytes
= ADP
.CharSize
* length
;
6516 byte[] bytes
= new byte[cBytes
];
6518 CopyCharsToBytes(carr
, offset
, bytes
, 0, length
);
6522 internal Task
WriteCharArray(char[] carr
, int length
, int offset
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6523 int cBytes
= ADP
.CharSize
* length
;
6525 // Perf shortcut: If it fits, write directly to the outBuff
6526 if(cBytes
< (stateObj
._outBuff
.Length
- stateObj
._outBytesUsed
)) {
6527 CopyCharsToBytes(carr
, offset
, stateObj
._outBuff
, stateObj
._outBytesUsed
, length
);
6528 stateObj
._outBytesUsed
+= cBytes
;
6532 if (stateObj
._bTmp
== null || stateObj
._bTmp
.Length
< cBytes
) {
6533 stateObj
._bTmp
= new byte[cBytes
];
6536 CopyCharsToBytes(carr
, offset
, stateObj
._bTmp
, 0, length
);
6537 return stateObj
.WriteByteArray(stateObj
._bTmp
, cBytes
, 0, canAccumulate
);
6541 internal byte[] SerializeString(string s
, int length
, int offset
) {
6542 int cBytes
= ADP
.CharSize
* length
;
6543 byte[] bytes
= new byte[cBytes
];
6545 CopyStringToBytes(s
, offset
, bytes
, 0, length
);
6549 internal Task
WriteString(string s
, int length
, int offset
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6550 int cBytes
= ADP
.CharSize
* length
;
6552 // Perf shortcut: If it fits, write directly to the outBuff
6553 if(cBytes
< (stateObj
._outBuff
.Length
- stateObj
._outBytesUsed
)) {
6554 CopyStringToBytes(s
, offset
, stateObj
._outBuff
, stateObj
._outBytesUsed
, length
);
6555 stateObj
._outBytesUsed
+= cBytes
;
6559 if (stateObj
._bTmp
== null || stateObj
._bTmp
.Length
< cBytes
) {
6560 stateObj
._bTmp
= new byte[cBytes
];
6563 CopyStringToBytes(s
, offset
, stateObj
._bTmp
, 0, length
);
6564 return stateObj
.WriteByteArray(stateObj
._bTmp
, cBytes
, 0, canAccumulate
);
6569 private unsafe static void CopyCharsToBytes(char[] source
, int sourceOffset
, byte[] dest
, int destOffset
, int charLength
) {
6570 // DEVNOTE: BE EXTREMELY CAREFULL in this method. Since it pins the buffers and copies the memory
6571 // directly, it bypasses all of the CLR's usual array-bounds checking. For maintainability, the checks
6572 // here should NOT be removed just because some other code will check them ahead of time.
6573 if (charLength
< 0) {
6574 throw ADP
.InvalidDataLength(charLength
);
6577 if (checked(sourceOffset
+ charLength
) > source
.Length
|| sourceOffset
< 0) {
6578 throw ADP
.IndexOutOfRange(sourceOffset
);
6581 // charLength >= 0 & checked conversion implies byteLength >= 0
6582 int byteLength
= checked(charLength
* ADP
.CharSize
);
6584 if (checked(destOffset
+ byteLength
) > dest
.Length
|| destOffset
< 0) {
6585 throw ADP
.IndexOutOfRange(destOffset
);
6588 fixed (char* sourcePtr
= source
) {
6589 char* srcPtr
= sourcePtr
; // Can't increment the target of a Fixed statement
6590 srcPtr
+= sourceOffset
; // char* increments by sizeof(char)
6591 fixed (byte* destinationPtr
= dest
) {
6592 byte* destPtr
= destinationPtr
;
6593 destPtr
+= destOffset
;
6594 NativeOledbWrapper
.MemoryCopy((IntPtr
)destPtr
, (IntPtr
)srcPtr
, byteLength
);
6599 private unsafe static void CopyStringToBytes(string source
, int sourceOffset
, byte[] dest
, int destOffset
, int charLength
) {
6600 // DEVNOTE: BE EXTREMELY CAREFULL in this method. Since it pins the buffers and copies the memory
6601 // directly, it bypasses all of the CLR's usual array-bounds checking. For maintainability, the checks
6602 // here should NOT be removed just because some other code will check them ahead of time.
6603 if (charLength
< 0) {
6604 throw ADP
.InvalidDataLength(charLength
);
6607 if (checked(sourceOffset
+ charLength
) > source
.Length
|| sourceOffset
< 0) {
6608 throw ADP
.IndexOutOfRange(sourceOffset
);
6611 // charLength >= 0 & checked conversion implies byteLength >= 0
6612 int byteLength
= checked(charLength
* ADP
.CharSize
);
6614 if (checked(destOffset
+ byteLength
) > dest
.Length
|| destOffset
< 0) {
6615 throw ADP
.IndexOutOfRange(destOffset
);
6618 fixed (char* sourcePtr
= source
) {
6619 char* srcPtr
= sourcePtr
; // Can't increment the target of a Fixed statement
6620 srcPtr
+= sourceOffset
; // char* increments by sizeof(char)
6621 fixed (byte* destinationPtr
= dest
) {
6622 byte* destPtr
= destinationPtr
;
6623 destPtr
+= destOffset
;
6624 NativeOledbWrapper
.MemoryCopy((IntPtr
)destPtr
, (IntPtr
)srcPtr
, byteLength
);
6629 private Task
WriteEncodingChar(string s
, Encoding encoding
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6630 return WriteEncodingChar(s
, s
.Length
, 0, encoding
, stateObj
, canAccumulate
);
6633 private byte[] SerializeEncodingChar(string s
, int numChars
, int offset
, Encoding encoding
) {
6635 byte[] byteData
= null;
6637 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6638 // 7.0 has no support for multiple code pages in data - single code page support only
6639 if (encoding
== null)
6640 encoding
= _defaultEncoding
;
6642 charData
= s
.ToCharArray(offset
, numChars
);
6644 byteData
= new byte[encoding
.GetByteCount(charData
, 0, charData
.Length
)];
6645 encoding
.GetBytes(charData
, 0, charData
.Length
, byteData
, 0);
6650 private Task
WriteEncodingChar(string s
, int numChars
, int offset
, Encoding encoding
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6654 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6655 // 7.0 has no support for multiple code pages in data - single code page support only
6656 if (encoding
== null)
6657 encoding
= _defaultEncoding
;
6659 charData
= s
.ToCharArray(offset
, numChars
);
6661 // Optimization: if the entire string fits in the current buffer, then copy it directly
6662 int bytesLeft
= stateObj
._outBuff
.Length
- stateObj
._outBytesUsed
;
6663 if ((numChars
<= bytesLeft
) && (encoding
.GetMaxByteCount(charData
.Length
) <= bytesLeft
)) {
6664 int bytesWritten
= encoding
.GetBytes(charData
, 0, charData
.Length
, stateObj
._outBuff
, stateObj
._outBytesUsed
);
6665 stateObj
._outBytesUsed
+= bytesWritten
;
6669 byteData
= encoding
.GetBytes(charData
, 0, numChars
);
6670 Debug
.Assert(byteData
!= null, "no data from encoding");
6671 return stateObj
.WriteByteArray(byteData
, byteData
.Length
, 0, canAccumulate
);
6675 internal int GetEncodingCharLength(string value, int numChars
, int charOffset
, Encoding encoding
) {
6678 if (value == null || value == ADP
.StrEmpty
) {
6682 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6683 // 7.0 has no support for multiple code pages in data - single code page support only
6684 if (encoding
== null) {
6685 if (null == _defaultEncoding
) {
6686 ThrowUnsupportedCollationEncountered(null);
6689 encoding
= _defaultEncoding
;
6692 char[] charData
= value.ToCharArray(charOffset
, numChars
);
6694 return encoding
.GetByteCount(charData
, 0, numChars
);
6698 // Returns the data stream length of the data identified by tds type or SqlMetaData returns
6699 // Returns either the total size or the size of the first chunk for partially length prefixed types.
6701 internal bool TryGetDataLength(SqlMetaDataPriv colmeta
, TdsParserStateObject stateObj
, out ulong length
) {
6702 // Handle Yukon specific tokens
6703 if (_isYukon
&& colmeta
.metaType
.IsPlp
) {
6704 Debug
.Assert(colmeta
.tdsType
== TdsEnums
.SQLXMLTYPE
||
6705 colmeta
.tdsType
== TdsEnums
.SQLBIGVARCHAR
||
6706 colmeta
.tdsType
== TdsEnums
.SQLBIGVARBINARY
||
6707 colmeta
.tdsType
== TdsEnums
.SQLNVARCHAR
||
6708 // Large UDTs is WinFS-only
6709 colmeta
.tdsType
== TdsEnums
.SQLUDT
,
6710 "GetDataLength:Invalid streaming datatype");
6711 return stateObj
.TryReadPlpLength(true, out length
);
6715 if (!TryGetTokenLength(colmeta
.tdsType
, stateObj
, out intLength
)) {
6719 length
= (ulong)intLength
;
6725 // returns the token length of the token or tds type
6726 // Returns -1 for partially length prefixed (plp) types for metadata info.
6727 // DOES NOT handle plp data streams correctly!!!
6728 // Plp data streams length information should be obtained from GetDataLength
6730 internal bool TryGetTokenLength(byte token
, TdsParserStateObject stateObj
, out int tokenLength
) {
6731 Debug
.Assert(token
!= 0, "0 length token!");
6733 switch (token
) { // rules about SQLLenMask no longer apply to new tokens (as of 7.4)
6734 case TdsEnums
.SQLFEATUREEXTACK
:
6737 case TdsEnums
.SQLSESSIONSTATE
:
6738 case TdsEnums
.SQLFEDAUTHINFO
:
6739 return stateObj
.TryReadInt32(out tokenLength
);
6742 if (_isYukon
) { // Handle Yukon specific exceptions
6743 if (token
== TdsEnums
.SQLUDT
) { // special case for UDTs
6744 tokenLength
= -1; // Should we return -1 or not call GetTokenLength for UDTs?
6747 else if (token
== TdsEnums
.SQLRETURNVALUE
) {
6748 tokenLength
= -1; // In Yukon, the RETURNVALUE token stream no longer has length
6751 else if (token
== TdsEnums
.SQLXMLTYPE
) {
6753 if (!stateObj
.TryReadUInt16(out value)) {
6757 tokenLength
= (int)value;
6758 Debug
.Assert(tokenLength
== TdsEnums
.SQL_USHORTVARMAXLEN
, "Invalid token stream for xml datatype");
6763 switch (token
& TdsEnums
.SQLLenMask
) {
6764 case TdsEnums
.SQLFixedLen
:
6765 tokenLength
= ((0x01 << ((token
& 0x0c) >> 2))) & 0xff;
6767 case TdsEnums
.SQLZeroLen
:
6770 case TdsEnums
.SQLVarLen
:
6771 case TdsEnums
.SQLVarCnt
:
6772 if (0 != (token
& 0x80)) {
6774 if (!stateObj
.TryReadUInt16(out value)) {
6778 tokenLength
= value;
6781 else if (0 == (token
& 0x0c)) {
6783 if (!stateObj
.TryReadInt32(out tokenLength
)) {
6790 if (!stateObj
.TryReadByte(out value)) {
6794 tokenLength
= value;
6798 Debug
.Assert(false, "Unknown token length!");
6804 private void ProcessAttention(TdsParserStateObject stateObj
) {
6805 if (_state
== TdsParserState
.Closed
|| _state
== TdsParserState
.Broken
){
6808 Debug
.Assert(stateObj
._attentionSent
, "invalid attempt to ProcessAttention, attentionSent == false!");
6810 // Attention processing scenarios:
6811 // 1) EOM packet with header ST_AACK bit plus DONE with status DONE_ATTN
6812 // 2) Packet without ST_AACK header bit but has DONE with status DONE_ATTN
6813 // 3) Secondary timeout occurs while reading, break connection
6815 // Since errors can occur and we need to cancel prior to throwing those errors, we
6816 // cache away error state and then process TDS for the attention. We restore those
6817 // errors after processing.
6818 stateObj
.StoreErrorAndWarningForAttention();
6821 // Call run loop to process looking for attention ack.
6822 Run(RunBehavior
.Attention
, null, null, null, stateObj
);
6824 catch (Exception e
) {
6826 if (!ADP
.IsCatchableExceptionType(e
)) {
6830 // If an exception occurs - break the connection.
6831 // Attention error will not be thrown in this case by Run(), but other failures may.
6832 ADP
.TraceExceptionWithoutRethrow(e
);
6833 _state
= TdsParserState
.Broken
;
6834 _connHandler
.BreakConnection();
6839 stateObj
.RestoreErrorAndWarningAfterAttention();
6841 Debug
.Assert(!stateObj
._attentionSent
, "Invalid attentionSent state at end of ProcessAttention");
6844 static private int StateValueLength(int dataLen
) {
6845 return dataLen
< 0xFF ? (dataLen
+ 1) : (dataLen
+ 5);
6848 internal int WriteSessionRecoveryFeatureRequest(SessionData reconnectData
, bool write
/* if false just calculates the length */) {
6851 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_SRECOVERY
);
6853 if (reconnectData
== null) {
6855 WriteInt(0, _physicalStateObj
);
6860 Debug
.Assert(reconnectData
._unrecoverableStatesCount
== 0, "Unrecoverable state count should be 0");
6861 int initialLength
= 0; // sizeof(DWORD) - length itself
6862 initialLength
+= 1 + 2 * TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._initialDatabase
);
6863 initialLength
+= 1 + 2 * TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._initialLanguage
);
6864 initialLength
+= (reconnectData
._initialCollation
== null) ? 1 : 6;
6865 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6866 if (reconnectData
._initialState
[i
] != null) {
6867 initialLength
+= 1 /* StateId*/ + StateValueLength(reconnectData
._initialState
[i
].Length
);
6870 int currentLength
= 0; // sizeof(DWORD) - length itself
6871 currentLength
+= 1 + 2 * (reconnectData
._initialDatabase
== reconnectData
._database
? 0 : TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._database
));
6872 currentLength
+= 1 + 2 * (reconnectData
._initialLanguage
== reconnectData
._language
? 0 : TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._language
));
6873 currentLength
+= (reconnectData
._collation
!= null && !SqlCollation
.AreSame(reconnectData
._collation
, reconnectData
._initialCollation
)) ? 6 : 1;
6874 bool[] writeState
= new bool[SessionData
._maxNumberOfSessionStates
];
6875 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6876 if (reconnectData
._delta
[i
] != null) {
6877 Debug
.Assert(reconnectData
._delta
[i
]._recoverable
, "State should be recoverable");
6878 writeState
[i
] = true;
6879 if (reconnectData
._initialState
[i
] != null && reconnectData
._initialState
[i
].Length
== reconnectData
._delta
[i
]._dataLength
) {
6880 writeState
[i
] = false;
6881 for (int j
= 0; j
< reconnectData
._delta
[i
]._dataLength
; j
++) {
6882 if (reconnectData
._initialState
[i
][j
] != reconnectData
._delta
[i
]._data
[j
]) {
6883 writeState
[i
] = true;
6888 if (writeState
[i
]) {
6889 currentLength
+= 1 /* StateId*/ + StateValueLength(reconnectData
._delta
[i
]._dataLength
);
6894 WriteInt(8 + initialLength
+ currentLength
, _physicalStateObj
); // length of data w/o total length (initil+current+2*sizeof(DWORD))
6895 WriteInt(initialLength
, _physicalStateObj
);
6896 WriteIdentifier(reconnectData
._initialDatabase
, _physicalStateObj
);
6897 WriteCollation(reconnectData
._initialCollation
, _physicalStateObj
);
6898 WriteIdentifier(reconnectData
._initialLanguage
, _physicalStateObj
);
6899 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6900 if (reconnectData
._initialState
[i
] != null) {
6901 _physicalStateObj
.WriteByte((byte)i
);
6902 if (reconnectData
._initialState
[i
].Length
< 0xFF) {
6903 _physicalStateObj
.WriteByte((byte)reconnectData
._initialState
[i
].Length
);
6906 _physicalStateObj
.WriteByte(0xFF);
6907 WriteInt(reconnectData
._initialState
[i
].Length
, _physicalStateObj
);
6909 _physicalStateObj
.WriteByteArray(reconnectData
._initialState
[i
], reconnectData
._initialState
[i
].Length
, 0);
6912 WriteInt(currentLength
, _physicalStateObj
);
6913 WriteIdentifier(reconnectData
._database
!= reconnectData
._initialDatabase
? reconnectData
._database
: null, _physicalStateObj
);
6914 WriteCollation(SqlCollation
.AreSame(reconnectData
._initialCollation
, reconnectData
._collation
) ? null : reconnectData
._collation
, _physicalStateObj
);
6915 WriteIdentifier(reconnectData
._language
!= reconnectData
._initialLanguage
? reconnectData
._language
: null, _physicalStateObj
);
6916 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6917 if (writeState
[i
]) {
6918 _physicalStateObj
.WriteByte((byte)i
);
6919 if (reconnectData
._delta
[i
]._dataLength
< 0xFF) {
6920 _physicalStateObj
.WriteByte((byte)reconnectData
._delta
[i
]._dataLength
);
6923 _physicalStateObj
.WriteByte(0xFF);
6924 WriteInt(reconnectData
._delta
[i
]._dataLength
, _physicalStateObj
);
6926 _physicalStateObj
.WriteByteArray(reconnectData
._delta
[i
]._data
, reconnectData
._delta
[i
]._dataLength
, 0);
6930 len
+= initialLength
+ currentLength
+ 12 /* length fields (initial, current, total) */;
6935 internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionData fedAuthFeatureData
,
6936 bool write
/* if false just calculates the length */) {
6937 Debug
.Assert(fedAuthFeatureData
.libraryType
== TdsEnums
.FedAuthLibrary
.ADAL
|| fedAuthFeatureData
.libraryType
== TdsEnums
.FedAuthLibrary
.SecurityToken
,
6938 "only fed auth library type ADAL and Security Token are supported in writing feature request");
6943 // set dataLen and totalLen
6944 switch (fedAuthFeatureData
.libraryType
) {
6945 case TdsEnums
.FedAuthLibrary
.ADAL
:
6946 dataLen
= 2; // length of feature data = 1 byte for library and echo + 1 byte for workflow
6948 case TdsEnums
.FedAuthLibrary
.SecurityToken
:
6949 Debug
.Assert(fedAuthFeatureData
.accessToken
!= null, "AccessToken should not be null.");
6950 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
6953 Debug
.Assert(false, "Unrecognized library type for fedauth feature extension request");
6957 totalLen
= dataLen
+ 5; // length of feature id (1 byte), data length field (4 bytes), and feature data (dataLen)
6961 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_FEDAUTH
);
6964 byte options
= 0x00;
6966 // set upper 7 bits of options to indicate fed auth library type
6967 switch (fedAuthFeatureData
.libraryType
) {
6968 case TdsEnums
.FedAuthLibrary
.ADAL
:
6969 Debug
.Assert(_connHandler
._federatedAuthenticationInfoRequested
== true, "_federatedAuthenticationInfoRequested field should be true");
6970 options
|= TdsEnums
.FEDAUTHLIB_ADAL
<< 1;
6972 case TdsEnums
.FedAuthLibrary
.SecurityToken
:
6973 Debug
.Assert(_connHandler
._federatedAuthenticationRequested
== true, "_federatedAuthenticationRequested field should be true");
6974 options
|= TdsEnums
.FEDAUTHLIB_SECURITYTOKEN
<< 1;
6977 Debug
.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request");
6981 options
|= (byte)(fedAuthFeatureData
.fedAuthRequiredPreLoginResponse
== true ? 0x01 : 0x00);
6983 // write dataLen and options
6984 WriteInt(dataLen
, _physicalStateObj
);
6985 _physicalStateObj
.WriteByte(options
);
6987 // write workflow for FedAuthLibrary.ADAL
6988 // write accessToken for FedAuthLibrary.SecurityToken
6989 switch (fedAuthFeatureData
.libraryType
) {
6990 case TdsEnums
.FedAuthLibrary
.ADAL
:
6991 byte workflow
= 0x00;
6992 switch (fedAuthFeatureData
.authentication
) {
6993 case SqlAuthenticationMethod
.ActiveDirectoryPassword
:
6994 workflow
= TdsEnums
.ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD
;
6996 case SqlAuthenticationMethod
.ActiveDirectoryIntegrated
:
6997 workflow
= TdsEnums
.ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED
;
7000 Debug
.Assert(false, "Unrecognized Authentication type for fedauth ADAL request");
7004 _physicalStateObj
.WriteByte(workflow
);
7006 case TdsEnums
.FedAuthLibrary
.SecurityToken
:
7007 WriteInt(fedAuthFeatureData
.accessToken
.Length
, _physicalStateObj
);
7008 _physicalStateObj
.WriteByteArray(fedAuthFeatureData
.accessToken
, fedAuthFeatureData
.accessToken
.Length
, 0);
7011 Debug
.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request");
7018 internal int WriteTceFeatureRequest (bool write
/* if false just calculates the length */) {
7019 int len
= 6; // (1byte = featureID, 4bytes = featureData length, 1 bytes = Version
7022 // Write Feature ID, legth of the version# field and TCE Version#
7023 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_TCE
);
7024 WriteInt (1, _physicalStateObj
);
7025 _physicalStateObj
.WriteByte(TdsEnums
.MAX_SUPPORTED_TCE_VERSION
);
7028 return len
; // size of data written
7031 internal int WriteGlobalTransactionsFeatureRequest(bool write
/* if false just calculates the length */) {
7032 int len
= 5; // 1byte = featureID, 4bytes = featureData length
7036 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_GLOBALTRANSACTIONS
);
7037 WriteInt(0, _physicalStateObj
); // we don't send any data
7043 internal void TdsLogin(SqlLogin rec
,
7044 TdsEnums
.FeatureExtension requestedFeatures
,
7045 SessionData recoverySessionData
,
7046 FederatedAuthenticationFeatureExtensionData
? fedAuthFeatureExtensionData
) {
7047 _physicalStateObj
.SetTimeoutSeconds(rec
.timeout
);
7049 Debug
.Assert(recoverySessionData
== null || (requestedFeatures
& TdsEnums
.FeatureExtension
.SessionRecovery
) != 0, "Recovery session data without session recovery feature request");
7050 Debug
.Assert(TdsEnums
.MAXLEN_HOSTNAME
>=rec
.hostName
.Length
, "_workstationId.Length exceeds the max length for this value");
7052 Debug
.Assert(!(rec
.useSSPI
&& _connHandler
._fedAuthRequired
), "Cannot use SSPI when server has responded 0x01 for FedAuthRequired PreLogin Option.");
7053 Debug
.Assert(!rec
.useSSPI
|| (requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) == 0, "Cannot use both SSPI and FedAuth");
7054 Debug
.Assert(fedAuthFeatureExtensionData
== null || (requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) != 0, "fedAuthFeatureExtensionData provided without fed auth feature request");
7055 Debug
.Assert(fedAuthFeatureExtensionData
!= null || (requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) == 0, "Fed Auth feature requested without specifying fedAuthFeatureExtensionData.");
7057 Debug
.Assert(rec
.userName
== null || (rec
.userName
!= null && TdsEnums
.MAXLEN_USERNAME
>= rec
.userName
.Length
), "_userID.Length exceeds the max length for this value");
7058 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");
7060 Debug
.Assert(rec
.password
== null || (rec
.password
!= null && TdsEnums
.MAXLEN_PASSWORD
>=rec
.password
.Length
), "_password.Length exceeds the max length for this value");
7061 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");
7063 Debug
.Assert(rec
.credential
!= null || rec
.userName
!= null || rec
.password
!= null, "cannot mix the new secure password system and the connection string based password");
7064 Debug
.Assert(rec
.newSecurePassword
!= null || rec
.newPassword
!= null, "cannot have both new secure change password and string based change password");
7065 Debug
.Assert(TdsEnums
.MAXLEN_APPNAME
>=rec
.applicationName
.Length
, "_applicationName.Length exceeds the max length for this value");
7066 Debug
.Assert(TdsEnums
.MAXLEN_SERVERNAME
>=rec
.serverName
.Length
, "_dataSource.Length exceeds the max length for this value");
7067 Debug
.Assert(TdsEnums
.MAXLEN_LANGUAGE
>=rec
.language
.Length
, "_currentLanguage .Length exceeds the max length for this value");
7068 Debug
.Assert(TdsEnums
.MAXLEN_DATABASE
>=rec
.database
.Length
, "_initialCatalog.Length exceeds the max length for this value");
7069 Debug
.Assert(TdsEnums
.MAXLEN_ATTACHDBFILE
>=rec
.attachDBFilename
.Length
, "_attachDBFileName.Length exceeds the max length for this value");
7071 Debug
.Assert(_connHandler
!= null, "SqlConnectionInternalTds handler can not be null at this point.");
7072 _connHandler
.TimeoutErrorInternal
.EndPhase(SqlConnectionTimeoutErrorPhase
.LoginBegin
);
7073 _connHandler
.TimeoutErrorInternal
.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase
.ProcessConnectionAuth
);
7075 // get the password up front to use in sspi logic below
7076 byte[] encryptedPassword
= null;
7077 byte[] encryptedChangePassword
= null;
7078 int encryptedPasswordLengthInBytes
;
7079 int encryptedChangePasswordLengthInBytes
;
7080 bool useFeatureExt
= (requestedFeatures
!= TdsEnums
.FeatureExtension
.None
);
7084 if (rec
.credential
!= null) {
7085 userName
= rec
.credential
.UserId
;
7086 encryptedPasswordLengthInBytes
= rec
.credential
.Password
.Length
* 2;
7089 userName
= rec
.userName
;
7090 encryptedPassword
= TdsParserStaticMethods
.EncryptPassword(rec
.password
);
7091 encryptedPasswordLengthInBytes
= encryptedPassword
.Length
; // password in clear text is already encrypted and its length is in byte
7094 if (rec
.newSecurePassword
!= null) {
7095 encryptedChangePasswordLengthInBytes
= rec
.newSecurePassword
.Length
* 2;
7098 encryptedChangePassword
= TdsParserStaticMethods
.EncryptPassword(rec
.newPassword
);
7099 encryptedChangePasswordLengthInBytes
= encryptedChangePassword
.Length
;
7103 // set the message type
7104 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_LOGIN7
;
7107 int length
= TdsEnums
.YUKON_LOG_REC_FIXED_LEN
;
7109 string clientInterfaceName
= TdsEnums
.SQL_PROVIDER_NAME
;
7110 Debug
.Assert(TdsEnums
.MAXLEN_CLIENTINTERFACE
>= clientInterfaceName
.Length
, "cchCltIntName can specify at most 128 unicode characters. See Tds spec");
7112 // add up variable-len portions (multiply by 2 for byte len of char strings)
7115 length
+= (rec
.hostName
.Length
+ rec
.applicationName
.Length
+
7116 rec
.serverName
.Length
+ clientInterfaceName
.Length
+
7117 rec
.language
.Length
+ rec
.database
.Length
+
7118 rec
.attachDBFilename
.Length
) * 2;
7119 if (useFeatureExt
) {
7124 // allocate memory for SSPI variables
7125 byte[] outSSPIBuff
= null;
7126 UInt32 outSSPILength
= 0;
7128 // only add lengths of password and username if not using SSPI or requesting federated authentication info
7129 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7131 length
+= (userName
.Length
* 2) + encryptedPasswordLengthInBytes
7132 + encryptedChangePasswordLengthInBytes
;
7137 // now allocate proper length of buffer, and set length
7138 outSSPIBuff
= new byte[s_maxSSPILength
];
7139 outSSPILength
= s_maxSSPILength
;
7141 // Call helper function for SSPI data and actual length.
7142 // Since we don't have SSPI data from the server, send null for the
7143 // byte[] buffer and 0 for the int length.
7144 Debug
.Assert(SniContext
.Snix_Login
==_physicalStateObj
.SniContext
, String
.Format((IFormatProvider
)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj
.SniContext
));
7145 _physicalStateObj
.SniContext
= SniContext
.Snix_LoginSspi
;
7146 SSPIData(null, 0, outSSPIBuff
, ref outSSPILength
);
7147 if (outSSPILength
> Int32
.MaxValue
) {
7148 throw SQL
.InvalidSSPIPacketSize(); // SqlBu 332503
7150 _physicalStateObj
.SniContext
=SniContext
.Snix_Login
;
7153 length
+= (Int32
)outSSPILength
;
7158 int feOffset
= length
;
7160 if (useFeatureExt
) {
7162 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.SessionRecovery
) != 0) {
7163 length
+= WriteSessionRecoveryFeatureRequest(recoverySessionData
, false);
7165 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) != 0) {
7166 Debug
.Assert(fedAuthFeatureExtensionData
!= null, "fedAuthFeatureExtensionData should not null.");
7167 length
+= WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData
.Value
, write
:false);
7169 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.Tce
) != 0) {
7170 length
+= WriteTceFeatureRequest (false);
7172 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.GlobalTransactions
) != 0) {
7173 length
+= WriteGlobalTransactionsFeatureRequest(false);
7175 length
++; // for terminator
7180 WriteInt(length
, _physicalStateObj
);
7181 if (recoverySessionData
== null) {
7182 WriteInt((TdsEnums
.DENALI_MAJOR
<< 24) | (TdsEnums
.DENALI_INCREMENT
<< 16) | TdsEnums
.DENALI_MINOR
, _physicalStateObj
);
7185 WriteUnsignedInt(recoverySessionData
._tdsVersion
, _physicalStateObj
);
7187 WriteInt(rec
.packetSize
, _physicalStateObj
);
7188 WriteInt(TdsEnums
.CLIENT_PROG_VER
, _physicalStateObj
);
7189 WriteInt(TdsParserStaticMethods
.GetCurrentProcessIdForTdsLoginOnly(), _physicalStateObj
); //MDAC 84718
7190 WriteInt(0, _physicalStateObj
); // connectionID is unused
7192 // Log7Flags (DWORD)
7196 Current snapshot from TDS spec with the offsets added:
7197 0) fByteOrder:1, // byte order of numeric data types on client
7198 1) fCharSet:1, // character set on client
7199 2) fFloat:2, // Type of floating point on client
7200 4) fDumpLoad:1, // Dump/Load and BCP enable
7201 5) fUseDb:1, // USE notification
7202 6) fDatabase:1, // Initial database fatal flag
7203 7) fSetLang:1, // SET LANGUAGE notification
7204 8) fLanguage:1, // Initial language fatal flag
7205 9) fODBC:1, // Set if client is ODBC driver
7206 10) fTranBoundary:1, // Transaction boundary notification
7207 11) fDelegatedSec:1, // Security with delegation is available
7208 12) fUserType:3, // Type of user
7209 15) fIntegratedSecurity:1, // Set if client is using integrated security
7210 16) fSQLType:4, // Type of SQL sent from client
7211 20) fOLEDB:1, // Set if client is OLEDB driver
7212 21) fSpare1:3, // first bit used for read-only intent, rest unused
7213 24) fResetPassword:1, // set if client wants to reset password
7214 25) fNoNBCAndSparse:1, // set if client does not support NBC and Sparse column
7215 26) fUserInstance:1, // This connection wants to connect to a SQL "user instance"
7216 27) fUnknownCollationHandling:1, // This connection can handle unknown collation correctly.
7217 28) fExtension:1 // Extensions are used
7222 log7Flags
|= TdsEnums
.USE_DB_ON
<< 5;
7223 log7Flags
|= TdsEnums
.INIT_DB_FATAL
<< 6;
7224 log7Flags
|= TdsEnums
.SET_LANG_ON
<< 7;
7227 log7Flags
|= TdsEnums
.INIT_LANG_FATAL
<< 8;
7228 log7Flags
|= TdsEnums
.ODBC_ON
<< 9;
7229 if (rec
.useReplication
) {
7230 log7Flags
|= TdsEnums
.REPL_ON
<< 12;
7233 log7Flags
|= TdsEnums
.SSPI_ON
<< 15;
7237 if (rec
.readOnlyIntent
) {
7238 log7Flags
|= TdsEnums
.READONLY_INTENT_ON
<< 21; // read-only intent flag is a first bit of fSpare1
7242 if (!ADP
.IsEmpty(rec
.newPassword
) || (rec
.newSecurePassword
!= null && rec
.newSecurePassword
.Length
!= 0)) {
7243 log7Flags
|= 1 << 24;
7245 if (rec
.userInstance
) {
7246 log7Flags
|= 1 << 26;
7249 if (useFeatureExt
) {
7250 log7Flags
|= 1 << 28;
7253 WriteInt(log7Flags
, _physicalStateObj
);
7254 if (Bid
.AdvancedOn
) {
7255 Bid
.Trace("<sc.TdsParser.TdsLogin|ADV> %d#, TDS Login7 flags = %d:\n", ObjectID
, log7Flags
);
7258 WriteInt(0, _physicalStateObj
); // ClientTimeZone is not used
7259 WriteInt(0, _physicalStateObj
); // LCID is unused by server
7261 // Start writing offset and length of variable length portions
7262 int offset
= TdsEnums
.YUKON_LOG_REC_FIXED_LEN
;
7264 // write offset/length pairs
7266 // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record
7267 WriteShort(offset
, _physicalStateObj
); // host name offset
7268 WriteShort(rec
.hostName
.Length
, _physicalStateObj
);
7269 offset
+= rec
.hostName
.Length
* 2;
7271 // Only send user/password over if not fSSPI or fed auth ADAL... If both user/password and SSPI are in login
7272 // rec, only SSPI is used. Confirmed same bahavior as in luxor.
7273 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7274 WriteShort(offset
, _physicalStateObj
); // userName offset
7275 WriteShort(userName
.Length
, _physicalStateObj
);
7276 offset
+= userName
.Length
* 2;
7278 // the encrypted password is a byte array - so length computations different than strings
7279 WriteShort(offset
, _physicalStateObj
); // password offset
7280 WriteShort(encryptedPasswordLengthInBytes
/ 2, _physicalStateObj
);
7281 offset
+= encryptedPasswordLengthInBytes
;
7284 // case where user/password data is not used, send over zeros
7285 WriteShort(0, _physicalStateObj
); // userName offset
7286 WriteShort(0, _physicalStateObj
);
7287 WriteShort(0, _physicalStateObj
); // password offset
7288 WriteShort(0, _physicalStateObj
);
7291 WriteShort(offset
, _physicalStateObj
); // app name offset
7292 WriteShort(rec
.applicationName
.Length
, _physicalStateObj
);
7293 offset
+= rec
.applicationName
.Length
* 2;
7295 WriteShort(offset
, _physicalStateObj
); // server name offset
7296 WriteShort(rec
.serverName
.Length
, _physicalStateObj
);
7297 offset
+= rec
.serverName
.Length
* 2;
7299 WriteShort(offset
, _physicalStateObj
);
7300 if (useFeatureExt
) {
7301 WriteShort(4, _physicalStateObj
); // length of ibFeatgureExtLong (which is a DWORD)
7305 WriteShort(0, _physicalStateObj
); // unused (was remote password ?)
7308 WriteShort(offset
, _physicalStateObj
); // client interface name offset
7309 WriteShort(clientInterfaceName
.Length
, _physicalStateObj
);
7310 offset
+= clientInterfaceName
.Length
* 2;
7312 WriteShort(offset
, _physicalStateObj
); // language name offset
7313 WriteShort(rec
.language
.Length
, _physicalStateObj
);
7314 offset
+= rec
.language
.Length
* 2;
7316 WriteShort(offset
, _physicalStateObj
); // database name offset
7317 WriteShort(rec
.database
.Length
, _physicalStateObj
);
7318 offset
+= rec
.database
.Length
* 2;
7322 if (null == s_nicAddress
)
7323 s_nicAddress
= TdsParserStaticMethods
.GetNetworkPhysicalAddressForTdsLoginOnly();
7325 _physicalStateObj
.WriteByteArray(s_nicAddress
, s_nicAddress
.Length
, 0);
7327 WriteShort(offset
, _physicalStateObj
); // ibSSPI offset
7329 WriteShort((int)outSSPILength
, _physicalStateObj
);
7330 offset
+= (int)outSSPILength
;
7333 WriteShort(0, _physicalStateObj
);
7336 WriteShort(offset
, _physicalStateObj
); // DB filename offset
7337 WriteShort(rec
.attachDBFilename
.Length
, _physicalStateObj
);
7338 offset
+= rec
.attachDBFilename
.Length
* 2;
7340 WriteShort(offset
, _physicalStateObj
); // reset password offset
7341 WriteShort(encryptedChangePasswordLengthInBytes
/ 2, _physicalStateObj
);
7343 WriteInt(0, _physicalStateObj
); // reserved for chSSPI
7345 // write variable length portion
7346 WriteString(rec
.hostName
, _physicalStateObj
);
7348 // if we are using SSPI or fed auth ADAL, do not send over username/password, since we will use SSPI instead
7349 // same behavior as Luxor
7350 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7351 WriteString(userName
, _physicalStateObj
);
7353 // Cache offset in packet for tracing.
7354 _physicalStateObj
._tracePasswordOffset
= _physicalStateObj
._outBytesUsed
;
7355 _physicalStateObj
._tracePasswordLength
= encryptedPasswordLengthInBytes
;
7357 if (rec
.credential
!= null) {
7358 _physicalStateObj
.WriteSecureString(rec
.credential
.Password
);
7361 _physicalStateObj
.WriteByteArray(encryptedPassword
, encryptedPasswordLengthInBytes
, 0);
7365 WriteString(rec
.applicationName
, _physicalStateObj
);
7366 WriteString(rec
.serverName
, _physicalStateObj
);
7368 // write ibFeatureExtLong
7369 if (useFeatureExt
) {
7370 WriteInt(feOffset
, _physicalStateObj
);
7373 WriteString(clientInterfaceName
, _physicalStateObj
);
7374 WriteString(rec
.language
, _physicalStateObj
);
7375 WriteString(rec
.database
, _physicalStateObj
);
7377 // send over SSPI data if we are using SSPI
7379 _physicalStateObj
.WriteByteArray(outSSPIBuff
, (int)outSSPILength
, 0);
7381 WriteString(rec
.attachDBFilename
, _physicalStateObj
);
7382 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7383 // Cache offset in packet for tracing.
7384 _physicalStateObj
._traceChangePasswordOffset
= _physicalStateObj
._outBytesUsed
;
7385 _physicalStateObj
._traceChangePasswordLength
= encryptedChangePasswordLengthInBytes
;
7386 if (rec
.newSecurePassword
!= null) {
7387 _physicalStateObj
.WriteSecureString(rec
.newSecurePassword
);
7390 _physicalStateObj
.WriteByteArray(encryptedChangePassword
, encryptedChangePasswordLengthInBytes
, 0);
7394 if (useFeatureExt
) {
7395 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.SessionRecovery
) != 0) {
7396 WriteSessionRecoveryFeatureRequest(recoverySessionData
, true);
7398 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) != 0) {
7399 Bid
.Trace("<sc.TdsParser.TdsLogin|SEC> Sending federated authentication feature request\n");
7400 Debug
.Assert(fedAuthFeatureExtensionData
!= null, "fedAuthFeatureExtensionData should not null.");
7401 WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData
.Value
, write
: true);
7403 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.Tce
) != 0) {
7404 WriteTceFeatureRequest (true);
7406 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.GlobalTransactions
) != 0) {
7407 WriteGlobalTransactionsFeatureRequest(true);
7409 _physicalStateObj
.WriteByte(0xFF); // terminator
7412 catch (Exception e
) {
7414 if (ADP
.IsCatchableExceptionType(e
)) {
7415 // be sure to wipe out our buffer if we started sending stuff
7416 _physicalStateObj
._outputPacketNumber
= 1; // end of message - reset to 1 - per ramas
7417 _physicalStateObj
.ResetBuffer();
7423 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7424 _physicalStateObj
.ResetSecurePasswordsInfomation(); // Password information is needed only from Login process; done with writing login packet and should clear information
7425 _physicalStateObj
._pendingData
= true;
7426 _physicalStateObj
._messageStatus
= 0;
7430 /// Send the access token to the server.
7432 /// <param name="fedAuthToken">Type encapuslating a Federated Authentication access token.</param>
7433 internal void SendFedAuthToken(SqlFedAuthToken fedAuthToken
) {
7434 Debug
.Assert(fedAuthToken
!= null, "fedAuthToken cannot be null");
7435 Debug
.Assert(fedAuthToken
.accessToken
!= null, "fedAuthToken.accessToken cannot be null");
7438 Bid
.Trace("<sc.TdsParser.SendFedAuthToken|SEC> Sending federated authentication token\n");
7440 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_FEDAUTH
;
7442 byte[] accessToken
= fedAuthToken
.accessToken
;
7444 // Send total length (length of token plus 4 bytes for the token length field)
7445 // If we were sending a nonce, this would include that length as well
7446 WriteUnsignedInt((uint)accessToken
.Length
+ sizeof(uint), _physicalStateObj
);
7448 // Send length of token
7449 WriteUnsignedInt((uint)accessToken
.Length
, _physicalStateObj
);
7451 // Send federated authentication access token
7452 _physicalStateObj
.WriteByteArray(accessToken
, accessToken
.Length
, 0);
7454 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7455 _physicalStateObj
._pendingData
= true;
7456 _physicalStateObj
._messageStatus
= 0;
7458 _connHandler
._federatedAuthenticationRequested
= true;
7461 private void SSPIData(byte[] receivedBuff
, UInt32 receivedLength
, byte[] sendBuff
, ref UInt32 sendLength
) {
7462 SNISSPIData(receivedBuff
, receivedLength
, sendBuff
, ref sendLength
);
7465 private void SNISSPIData(byte[] receivedBuff
, UInt32 receivedLength
, byte[] sendBuff
, ref UInt32 sendLength
)
7467 if (receivedBuff
== null)
7469 // we do not have SSPI data coming from server, so send over 0's for pointer and length
7472 // we need to respond to the server's message with SSPI data
7473 if(0 != SNINativeMethodWrapper
.SNISecGenClientContext(_physicalStateObj
.Handle
, receivedBuff
, receivedLength
, sendBuff
, ref sendLength
, _sniSpnBuffer
))
7475 SSPIError(SQLMessage
.SSPIGenerateError(), TdsEnums
.GEN_CLIENT_CONTEXT
);
7480 private void ProcessSSPI(int receivedLength
) {
7481 SniContext outerContext
=_physicalStateObj
.SniContext
;
7482 _physicalStateObj
.SniContext
= SniContext
.Snix_ProcessSspi
;
7483 // allocate received buffer based on length from SSPI message
7484 byte[] receivedBuff
= new byte[receivedLength
];
7486 // read SSPI data received from server
7487 Debug
.Assert(_physicalStateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
7488 bool result
= _physicalStateObj
.TryReadByteArray(receivedBuff
, 0, receivedLength
);
7489 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
7491 // allocate send buffer and initialize length
7492 byte[] sendBuff
= new byte[s_maxSSPILength
];
7493 UInt32 sendLength
= s_maxSSPILength
;
7495 // make call for SSPI data
7496 SSPIData(receivedBuff
, (UInt32
)receivedLength
, sendBuff
, ref sendLength
);
7498 // DO NOT SEND LENGTH - TDS DOC INCORRECT! JUST SEND SSPI DATA!
7499 _physicalStateObj
.WriteByteArray(sendBuff
, (int)sendLength
, 0);
7501 // set message type so server knows its a SSPI response
7502 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_SSPI
;
7505 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7506 _physicalStateObj
.SniContext
=outerContext
;
7509 private void SSPIError(string error
, string procedure
) {
7510 Debug
.Assert(!ADP
.IsEmpty(procedure
), "TdsParser.SSPIError called with an empty or null procedure string");
7511 Debug
.Assert(!ADP
.IsEmpty(error
), "TdsParser.SSPIError called with an empty or null error string");
7513 _physicalStateObj
.AddError(new SqlError(0, (byte)0x00, (byte)TdsEnums
.MIN_ERROR_CLASS
, _server
, error
, procedure
, 0));
7514 ThrowExceptionAndWarning(_physicalStateObj
);
7517 private void LoadSSPILibrary() {
7518 // Outer check so we don't acquire lock once once it's loaded.
7519 if (!s_fSSPILoaded
) {
7520 lock (s_tdsParserLock
) {
7521 // re-check inside lock
7522 if (!s_fSSPILoaded
) {
7523 // use local for ref param to defer setting s_maxSSPILength until we know the call succeeded.
7524 UInt32 maxLength
= 0;
7525 if (0 != SNINativeMethodWrapper
.SNISecInitPackage(ref maxLength
))
7526 SSPIError(SQLMessage
.SSPIInitializeError(), TdsEnums
.INIT_SSPI_PACKAGE
);
7528 s_maxSSPILength
= maxLength
;
7529 s_fSSPILoaded
= true;
7535 if (s_maxSSPILength
> Int32
.MaxValue
) {
7536 throw SQL
.InvalidSSPIPacketSize(); // SqlBu 332503
7540 private void LoadADALLibrary() {
7541 // Outer check so we don't acquire lock once once it's loaded.
7542 if (!s_fADALLoaded
) {
7543 lock (s_tdsParserLock
) {
7544 // re-check inside lock
7545 if (!s_fADALLoaded
) {
7546 int result
= ADALNativeWrapper
.ADALInitialize();
7549 s_fADALLoaded
= true;
7552 s_fADALLoaded
= false;
7554 SqlAuthenticationMethod authentication
= SqlAuthenticationMethod
.NotSpecified
;
7556 if (_connHandler
.ConnectionOptions
!= null)
7558 authentication
= _connHandler
.ConnectionOptions
.Authentication
;
7560 // Only the below connection string options should have ended up calling this function.
7561 Debug
.Assert(authentication
== SqlAuthenticationMethod
.ActiveDirectoryIntegrated
|| authentication
== SqlAuthenticationMethod
.ActiveDirectoryPassword
);
7564 _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));
7566 ThrowExceptionAndWarning(_physicalStateObj
);
7573 internal byte[] GetDTCAddress(int timeout
, TdsParserStateObject stateObj
) {
7574 // If this fails, the server will return a server error - Sameet Agarwal confirmed.
7575 // Success: DTCAddress returned. Failure: SqlError returned.
7577 byte[] dtcAddr
= null;
7579 using (SqlDataReader dtcReader
= TdsExecuteTransactionManagerRequest(
7581 TdsEnums
.TransactionManagerRequestType
.GetDTCAddress
,
7583 TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
,
7584 timeout
, null, stateObj
, true)) {
7586 Debug
.Assert(SniContext
.Snix_Read
==stateObj
.SniContext
, String
.Format((IFormatProvider
)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj
.SniContext
));
7587 if (null != dtcReader
&& dtcReader
.Read()) {
7588 Debug
.Assert(dtcReader
.GetName(0) == "TM Address", "TdsParser: GetDTCAddress did not return 'TM Address'");
7590 // DTCAddress is of variable size, and does not have a maximum. So we call GetBytes
7591 // to get the length of the dtcAddress, then allocate a byte array of that length,
7592 // then call GetBytes again on that byte[] with the length
7593 long dtcLength
= dtcReader
.GetBytes(0, 0, null, 0, 0);
7596 if (dtcLength
<= Int32
.MaxValue
) {
7597 int cb
= (int)dtcLength
;
7599 dtcAddr
= new byte[cb
];
7600 dtcReader
.GetBytes(0, 0, dtcAddr
, 0, cb
);
7604 Debug
.Assert(false, "unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes");
7605 // if we hit this case we'll just return a null address so that the user
7606 // will get a transcaction enlistment error in the upper layers
7614 // Propagate the dtc cookie to the server, enlisting the connection.
7615 internal void PropagateDistributedTransaction(byte[] buffer
, int timeout
, TdsParserStateObject stateObj
) {
7616 // if this fails, the server will return a server error - Sameet Agarwal confirmed
7617 // Success: server will return done token. Failure: SqlError returned.
7619 TdsExecuteTransactionManagerRequest(buffer
,
7620 TdsEnums
.TransactionManagerRequestType
.Propagate
, null,
7621 TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
, timeout
, null, stateObj
, true);
7624 internal SqlDataReader
TdsExecuteTransactionManagerRequest(
7626 TdsEnums
.TransactionManagerRequestType request
,
7627 string transactionName
,
7628 TdsEnums
.TransactionManagerIsolationLevel isoLevel
,
7630 SqlInternalTransaction transaction
,
7631 TdsParserStateObject stateObj
,
7632 bool isDelegateControlRequest
) {
7634 Debug
.Assert(this == stateObj
.Parser
, "different parsers");
7636 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
) {
7640 // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7641 // delegated transactions often happen while there is an open result
7642 // set, so we need to handle them by using a different MARS session,
7643 // otherwise we'll write on the physical state objects while someone
7644 // else is using it. When we don't have MARS enabled, we need to
7645 // lock the physical state object to syncronize it's use at least
7646 // until we increment the open results count. Once it's been
7647 // incremented the delegated transaction requests will fail, so they
7648 // won't stomp on anything.
7651 Debug
.Assert(!_connHandler
.ThreadHasParserLockForClose
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken");
7652 bool callerHasConnectionLock
= _connHandler
.ThreadHasParserLockForClose
; // If the thread already claims to have the parser lock, then we will let the caller handle releasing it
7653 if (!callerHasConnectionLock
) {
7654 _connHandler
._parserLock
.Wait(canReleaseFromAnyThread
:false);
7655 _connHandler
.ThreadHasParserLockForClose
= true;
7657 // Capture _asyncWrite (after taking lock) to restore it afterwards
7658 bool hadAsyncWrites
= _asyncWrite
;
7660 // Temprarily disable async writes
7661 _asyncWrite
= false;
7663 // This validation step MUST be done after locking the connection to guarantee we don't
7664 // accidentally execute after the transaction has completed on a different thread.
7665 if (!isDelegateControlRequest
) {
7666 _connHandler
.CheckEnlistedTransactionBinding();
7669 stateObj
._outputMessageType
= TdsEnums
.MT_TRANS
; // set message type
7670 stateObj
.SetTimeoutSeconds(timeout
);
7672 stateObj
.SniContext
= SniContext
.Snix_Execute
;
7675 const int marsHeaderSize
= 18; // 4 + 2 + 8 + 4
7676 const int totalHeaderLength
= 22; // 4 + 4 + 2 + 8 + 4
7677 Debug
.Assert(stateObj
._outBytesUsed
== stateObj
._outputHeaderLen
, "Output bytes written before total header length");
7678 // Write total header length
7679 WriteInt(totalHeaderLength
, stateObj
);
7680 // Write mars header length
7681 WriteInt(marsHeaderSize
, stateObj
);
7682 WriteMarsHeaderData(stateObj
, _currentTransaction
);
7685 WriteShort((short)request
, stateObj
); // write TransactionManager Request type
7687 bool returnReader
= false;
7690 case TdsEnums
.TransactionManagerRequestType
.GetDTCAddress
:
7691 WriteShort(0, stateObj
);
7693 returnReader
= true;
7695 case TdsEnums
.TransactionManagerRequestType
.Propagate
:
7696 if (null != buffer
) {
7697 WriteShort(buffer
.Length
, stateObj
);
7698 stateObj
.WriteByteArray(buffer
, buffer
.Length
, 0);
7701 WriteShort(0, stateObj
);
7704 case TdsEnums
.TransactionManagerRequestType
.Begin
:
7705 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
7706 Debug
.Assert(null != transaction
, "Should have specified an internalTransaction when doing a BeginTransaction request!");
7708 // Only assign the passed in transaction if it is not equal to the current transaction.
7709 // And, if it is not equal, the current actually should be null. Anything else
7710 // is a unexpected state. The concern here is mainly for the mixed use of
7711 // T-SQL and API transactions. See SQL BU DT 345300 for full details and repro.
7714 // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
7715 // passed in on BeginTransaction API call.
7716 // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
7717 // passed in but equivalent to _currentTransaction.
7719 // #1 will occur on standard BeginTransactionAPI call. #2 should only occur if
7720 // t-sql transaction started followed by a call to SqlConnection.BeginTransaction.
7721 // Any other state is unknown.
7722 if (_currentTransaction
!= transaction
) {
7723 Debug
.Assert(_currentTransaction
== null || true == _fResetConnection
, "We should not have a current Tx at this point");
7724 PendingTransaction
= transaction
;
7727 stateObj
.WriteByte((byte)isoLevel
);
7729 stateObj
.WriteByte((byte)(transactionName
.Length
* 2)); // Write number of bytes (unicode string).
7730 WriteString(transactionName
, stateObj
);
7732 case TdsEnums
.TransactionManagerRequestType
.Promote
:
7733 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
7734 // No payload - except current transaction in header
7735 // Promote returns a DTC cookie. However, the transaction cookie we use for the
7736 // connection does not change after a promote.
7738 case TdsEnums
.TransactionManagerRequestType
.Commit
:
7739 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");
7741 Debug
.Assert(transactionName
.Length
== 0, "Should not have a transaction name on Commit");
7742 stateObj
.WriteByte((byte)0); // No xact name
7744 stateObj
.WriteByte(0); // No flags
7746 Debug
.Assert(isoLevel
== TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
, "Should not have isolation level other than unspecified on Commit!");
7747 // WriteByte((byte) 0, stateObj); // IsolationLevel
7748 // WriteByte((byte) 0, stateObj); // No begin xact name
7750 case TdsEnums
.TransactionManagerRequestType
.Rollback
:
7751 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");
7753 stateObj
.WriteByte((byte)(transactionName
.Length
* 2)); // Write number of bytes (unicode string).
7754 WriteString(transactionName
, stateObj
);
7756 stateObj
.WriteByte(0); // No flags
7758 Debug
.Assert(isoLevel
== TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
, "Should not have isolation level other than unspecified on Commit!");
7759 // WriteByte((byte) 0, stateObj); // IsolationLevel
7760 // WriteByte((byte) 0, stateObj); // No begin xact name
7762 case TdsEnums
.TransactionManagerRequestType
.Save
:
7763 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");
7765 stateObj
.WriteByte((byte)(transactionName
.Length
* 2)); // Write number of bytes (unicode string).
7766 WriteString(transactionName
, stateObj
);
7769 Debug
.Assert(false, "Unexpected TransactionManagerRequest");
7773 Task writeTask
= stateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7774 Debug
.Assert(writeTask
== null, "Writes should not pend when writing sync");
7775 stateObj
._pendingData
= true;
7776 stateObj
._messageStatus
= 0;
7778 SqlDataReader dtcReader
= null;
7779 stateObj
.SniContext
= SniContext
.Snix_Read
;
7781 dtcReader
= new SqlDataReader(null, CommandBehavior
.Default
);
7782 Debug
.Assert(this == stateObj
.Parser
, "different parser");
7784 // Remove the current owner of stateObj - otherwise we will hit asserts
7785 stateObj
.Owner
= null;
7787 dtcReader
.Bind(stateObj
);
7789 // force consumption of metadata
7790 _SqlMetaDataSet metaData
= dtcReader
.MetaData
;
7793 Run(RunBehavior
.UntilDone
, null, null, null, stateObj
);
7796 // If the retained ID is no longer valid (because we are enlisting in null or a new transaction) then it should be cleared
7797 if (((request
== TdsEnums
.TransactionManagerRequestType
.Begin
) || (request
== TdsEnums
.TransactionManagerRequestType
.Propagate
)) && ((transaction
== null) || (transaction
.TransactionId
!= _retainedTransactionId
))) {
7798 _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
7803 catch (Exception e
) {
7805 if (!ADP
.IsCatchableExceptionType(e
)) {
7809 FailureCleanup(stateObj
, e
);
7814 // SQLHotfix 50000518
7815 // make sure we don't leave temporary fields set when leaving this function
7816 _pendingTransaction
= null;
7818 _asyncWrite
= hadAsyncWrites
;
7820 if (!callerHasConnectionLock
) {
7821 _connHandler
.ThreadHasParserLockForClose
= false;
7822 _connHandler
._parserLock
.Release();
7827 internal void FailureCleanup(TdsParserStateObject stateObj
, Exception e
) {
7828 int old_outputPacketNumber
= stateObj
._outputPacketNumber
;
7831 Bid
.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception caught on ExecuteXXX: '%ls' \n", e
.ToString());
7834 if (stateObj
.HasOpenResult
) { // SQL BU DT 383773 - need to decrement openResultCount if operation failed.
7835 stateObj
.DecrementOpenResultCount();
7838 // be sure to wipe out our buffer if we started sending stuff
7839 stateObj
.ResetBuffer();
7840 stateObj
._outputPacketNumber
= 1; // end of message - reset to 1 - per ramas
7842 if (old_outputPacketNumber
!= 1 && _state
== TdsParserState
.OpenLoggedIn
) {
7843 Debug
.Assert(_connHandler
._parserLock
.ThreadMayHaveLock(), "Should not be calling into FailureCleanup without first taking the parser lock");
7845 bool originalThreadHasParserLock
= _connHandler
.ThreadHasParserLockForClose
;
7847 // Dev11 Bug 385286 : ExecuteNonQueryAsync hangs when trying to write a parameter which generates ArgumentException and while handling that exception the server disconnects the connection
7848 // Need to set this to true such that if we have an error sending\processing the attention, we won't deadlock ourselves
7849 _connHandler
.ThreadHasParserLockForClose
= true;
7851 // If _outputPacketNumber prior to ResetBuffer was not equal to 1, a packet was already
7852 // sent to the server and so we need to send an attention and process the attention ack.
7853 stateObj
.SendAttention();
7854 ProcessAttention(stateObj
);
7857 // Reset the ThreadHasParserLock value incase our caller expects it to be set\not set
7858 _connHandler
.ThreadHasParserLockForClose
= originalThreadHasParserLock
;
7862 Bid
.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception rethrown. \n");
7865 internal Task
TdsExecuteSQLBatch(string text
, int timeout
, SqlNotificationRequest notificationRequest
, TdsParserStateObject stateObj
, bool sync
, bool callerHasConnectionLock
= false) {
7866 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
) {
7870 if (stateObj
.BcpLock
) {
7871 throw SQL
.ConnectionLockedForBcpEvent();
7874 // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7875 // delegated transactions often happen while there is an open result
7876 // set, so we need to handle them by using a different MARS session,
7877 // otherwise we'll write on the physical state objects while someone
7878 // else is using it. When we don't have MARS enabled, we need to
7879 // lock the physical state object to syncronize it's use at least
7880 // until we increment the open results count. Once it's been
7881 // incremented the delegated transaction requests will fail, so they
7882 // won't stomp on anything.
7884 // Only need to take the lock if neither the thread nor the caller claims to already have it
7885 bool needToTakeParserLock
= (!callerHasConnectionLock
) && (!_connHandler
.ThreadHasParserLockForClose
);
7886 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
7887 Debug
.Assert(needToTakeParserLock
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Thread or caller claims to have connection lock, but lock is not taken");
7889 bool releaseConnectionLock
= false;
7890 if (needToTakeParserLock
) {
7891 _connHandler
._parserLock
.Wait(canReleaseFromAnyThread
: !sync
);
7892 releaseConnectionLock
= true;
7895 // Switch the writing mode
7896 // NOTE: We are not turning off async writes when we complete since SqlBulkCopy uses this method and expects _asyncWrite to not change
7897 _asyncWrite
= !sync
;
7900 // Check that the connection is still alive
7901 if ((_state
== TdsParserState
.Closed
) || (_state
== TdsParserState
.Broken
)) {
7902 throw ADP
.ClosedConnectionError();
7905 // This validation step MUST be done after locking the connection to guarantee we don't
7906 // accidentally execute after the transaction has completed on a different thread.
7907 _connHandler
.CheckEnlistedTransactionBinding();
7909 stateObj
.SetTimeoutSeconds(timeout
);
7910 if ((!_fMARS
) && (_physicalStateObj
.HasOpenResult
))
7912 Bid
.Trace("<sc.TdsParser.TdsExecuteSQLBatch|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID
);
7914 stateObj
.SniContext
= SniContext
.Snix_Execute
;
7918 WriteRPCBatchHeaders(stateObj
, notificationRequest
);
7921 stateObj
._outputMessageType
= TdsEnums
.MT_SQL
;
7923 WriteString(text
, text
.Length
, 0, stateObj
);
7925 Task executeTask
= stateObj
.ExecuteFlush();
7926 if (executeTask
== null) {
7927 stateObj
.SniContext
= SniContext
.Snix_Read
;
7930 Debug
.Assert(!sync
, "Should not have gotten a Task when writing in sync mode");
7932 // Need to wait for flush - continuation will unlock the connection
7933 bool taskReleaseConnectionLock
= releaseConnectionLock
;
7934 releaseConnectionLock
= false;
7935 return executeTask
.ContinueWith(t
=> {
7936 Debug
.Assert(!t
.IsCanceled
, "Task should not be canceled");
7939 FailureCleanup(stateObj
, t
.Exception
.InnerException
);
7940 throw t
.Exception
.InnerException
;
7943 stateObj
.SniContext
= SniContext
.Snix_Read
;
7947 if (taskReleaseConnectionLock
) {
7948 _connHandler
._parserLock
.Release();
7951 }, TaskScheduler
.Default
);
7957 catch (Exception e
) {
7958 //Debug.Assert(_state == TdsParserState.Broken, "Caught exception in TdsExecuteSQLBatch but connection was not broken!");
7960 if (!ADP
.IsCatchableExceptionType(e
)) {
7964 FailureCleanup(stateObj
, e
);
7969 if (releaseConnectionLock
) {
7970 _connHandler
._parserLock
.Release();
7975 internal Task
TdsExecuteRPC(SqlCommand cmd
, _SqlRPC
[] rpcArray
, int timeout
, bool inSchema
, SqlNotificationRequest notificationRequest
, TdsParserStateObject stateObj
, bool isCommandProc
, bool sync
= true,
7976 TaskCompletionSource
<object> completion
= null, int startRpc
= 0, int startParam
= 0) {
7977 bool firstCall
= (completion
== null);
7978 bool releaseConnectionLock
= false;
7980 Debug
.Assert(cmd
!= null, @"cmd cannot be null inside TdsExecuteRPC");
7981 Debug
.Assert(!firstCall
|| startRpc
== 0, "startRpc is not 0 on first call");
7982 Debug
.Assert(!firstCall
|| startParam
== 0, "startParam is not 0 on first call");
7983 Debug
.Assert(!firstCall
|| !_connHandler
.ThreadHasParserLockForClose
, "Thread should not already have connection lock");
7984 Debug
.Assert(firstCall
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Connection lock not taken after the first call");
7986 _SqlRPC rpcext
= null;
7989 // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7990 // delegated transactions often happen while there is an open result
7991 // set, so we need to handle them by using a different MARS session,
7992 // otherwise we'll write on the physical state objects while someone
7993 // else is using it. When we don't have MARS enabled, we need to
7994 // lock the physical state object to syncronize it's use at least
7995 // until we increment the open results count. Once it's been
7996 // incremented the delegated transaction requests will fail, so they
7997 // won't stomp on anything.
8001 _connHandler
._parserLock
.Wait(canReleaseFromAnyThread
:!sync
);
8002 releaseConnectionLock
= true;
8005 // Ensure that connection is alive
8006 if ((TdsParserState
.Broken
== State
) || (TdsParserState
.Closed
== State
)) {
8007 throw ADP
.ClosedConnectionError();
8010 // This validation step MUST be done after locking the connection to guarantee we don't
8011 // accidentally execute after the transaction has completed on a different thread.
8013 _asyncWrite
= !sync
;
8015 _connHandler
.CheckEnlistedTransactionBinding();
8017 stateObj
.SetTimeoutSeconds(timeout
);
8018 if ((!_fMARS
) && (_physicalStateObj
.HasOpenResult
))
8020 Bid
.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID
);
8022 stateObj
.SniContext
= SniContext
.Snix_Execute
;
8026 WriteRPCBatchHeaders(stateObj
, notificationRequest
);
8029 stateObj
._outputMessageType
= TdsEnums
.MT_RPC
;
8032 for (int ii
= startRpc
; ii
< rpcArray
.Length
; ii
++) {
8033 rpcext
= rpcArray
[ii
];
8035 if (startParam
== 0 || ii
> startRpc
) {
8036 if (rpcext
.ProcID
!= 0 && _isShiloh
) {
8037 // Perf optimization for Shiloh and later,
8038 Debug
.Assert(rpcext
.ProcID
< 255, "rpcExec:ProcID can't be larger than 255");
8039 WriteShort(0xffff, stateObj
);
8040 WriteShort((short)(rpcext
.ProcID
), stateObj
);
8043 Debug
.Assert(!ADP
.IsEmpty(rpcext
.rpcName
), "must have an RPC name");
8044 tempLen
= rpcext
.rpcName
.Length
;
8045 WriteShort(tempLen
, stateObj
);
8046 WriteString(rpcext
.rpcName
, tempLen
, 0, stateObj
);
8050 WriteShort((short)rpcext
.options
, stateObj
);
8053 // Stream out parameters
8054 SqlParameter
[] parameters
= rpcext
.parameters
;
8056 for (int i
= (ii
== startRpc
) ? startParam
: 0; i
< parameters
.Length
; i
++) {
8057 // Debug.WriteLine("i: " + i.ToString(CultureInfo.InvariantCulture));
8058 // parameters can be unnamed
8059 SqlParameter param
= parameters
[i
];
8060 // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters.
8062 break; // End of parameters for this execute
8064 // Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
8065 if (param
.ForceColumnEncryption
&&
8066 !(cmd
.ColumnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.Enabled
||
8067 (cmd
.ColumnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.UseConnectionSetting
&& cmd
.Connection
.IsColumnEncryptionSettingEnabled
))) {
8068 throw SQL
.ParamInvalidForceColumnEncryptionSetting(param
.ParameterName
, rpcext
.GetCommandTextOrRpcName());
8071 // Check if the applications wants to force column encryption to avoid sending sensitive data to server
8072 if (param
.ForceColumnEncryption
&& param
.CipherMetadata
== null
8073 && (param
.Direction
== ParameterDirection
.Input
|| param
.Direction
== ParameterDirection
.InputOutput
)) {
8074 // Application wants a parameter to be encrypted before sending it to server, however server doesnt think this parameter needs encryption.
8075 throw SQL
.ParamUnExpectedEncryptionMetadata(param
.ParameterName
, rpcext
.GetCommandTextOrRpcName());
8078 // Validate parameters are not variable length without size and with null value. MDAC 66522
8079 param
.Validate(i
, isCommandProc
);
8081 // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
8082 MetaType mt
= param
.InternalMetaType
;
8084 if (mt
.IsNewKatmaiType
) {
8085 WriteSmiParameter(param
, i
, 0 != (rpcext
.paramoptions
[i
] & TdsEnums
.RPC_PARAM_DEFAULT
), stateObj
);
8089 if ((!_isShiloh
&& !mt
.Is70Supported
) ||
8090 (!_isYukon
&& !mt
.Is80Supported
) ||
8091 (!_isKatmai
&& !mt
.Is90Supported
)) {
8092 throw ADP
.VersionDoesNotSupportDataType(mt
.TypeName
);
8094 object value = null;
8096 bool isSqlVal
= false;
8097 bool isDataFeed
= false;
8098 // if we have an output param, set the value to null so we do not send it across to the server
8099 if (param
.Direction
== ParameterDirection
.Output
) {
8100 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.
8102 param
.ParamaterIsSqlType
= isSqlVal
;
8105 value = param
.GetCoercedValue();
8106 isNull
= param
.IsNull
;
8108 isSqlVal
= param
.CoercedValueIsSqlType
;
8109 isDataFeed
= param
.CoercedValueIsDataFeed
;
8113 WriteParameterName(param
.ParameterNameFixed
, stateObj
);
8115 // Write parameter status
8116 stateObj
.WriteByte(rpcext
.paramoptions
[i
]);
8118 // MaxLen field is only written out for non-fixed length data types
8119 // use the greater of the two sizes for maxLen
8121 int size
= mt
.IsSizeInCharacters
? param
.GetParameterSize() * 2 : param
.GetParameterSize();
8123 //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
8124 if (mt
.TDSType
!= TdsEnums
.SQLUDT
)
8125 // getting the actualSize is expensive, cache here and use below
8126 actualSize
= param
.GetActualSize();
8128 actualSize
= 0; //get this later
8133 // scale and precision are only relevant for numeric and decimal types
8134 // adjust the actual value scale and precision to match the user specified
8135 if (mt
.SqlDbType
== SqlDbType
.Decimal
) {
8136 precision
= param
.GetActualPrecision();
8137 scale
= param
.GetActualScale();
8139 if (precision
> TdsEnums
.MAX_NUMERIC_PRECISION
) {
8140 throw SQL
.PrecisionValueOutOfRange(precision
);
8143 // bug 49512, make sure the value matches the scale the user enters
8146 value = AdjustSqlDecimalScale((SqlDecimal
)value, scale
);
8148 // If Precision is specified, verify value precision vs param precision
8149 if (precision
!= 0) {
8150 if (precision
< ((SqlDecimal
)value).Precision
) {
8151 throw ADP
.ParameterValueOutOfRange((SqlDecimal
)value);
8156 value = AdjustDecimalScale((Decimal
)value, scale
);
8158 SqlDecimal sqlValue
= new SqlDecimal((Decimal
)value);
8160 // If Precision is specified, verify value precision vs param precision
8161 if (precision
!= 0) {
8162 if (precision
< sqlValue
.Precision
) {
8163 throw ADP
.ParameterValueOutOfRange((Decimal
)value);
8170 bool isParameterEncrypted
= 0 != (rpcext
.paramoptions
[i
] & TdsEnums
.RPC_PARAM_ENCRYPTED
);
8172 // Additional information we need to send over wire to the server when writing encrypted parameters.
8173 SqlColumnEncryptionInputParameterInfo encryptedParameterInfoToWrite
= null;
8175 // If the parameter is encrypted, we need to encrypt the value.
8176 if (isParameterEncrypted
) {
8177 Debug
.Assert(mt
.TDSType
!= TdsEnums
.SQLVARIANT
&&
8178 mt
.TDSType
!= TdsEnums
.SQLUDT
&&
8179 mt
.TDSType
!= TdsEnums
.SQLXMLTYPE
&&
8180 mt
.TDSType
!= TdsEnums
.SQLIMAGE
&&
8181 mt
.TDSType
!= TdsEnums
.SQLTEXT
&&
8182 mt
.TDSType
!= TdsEnums
.SQLNTEXT
, "Type unsupported for encryption");
8184 byte[] serializedValue
= null;
8185 byte[] encryptedValue
= null;
8190 serializedValue
= SerializeUnencryptedSqlValue(value, mt
, actualSize
, param
.Offset
, param
.NormalizationRuleVersion
, stateObj
);
8193 // for codePageEncoded types, WriteValue simply expects the number of characters
8194 // For plp types, we also need the encoded byte size
8195 serializedValue
= SerializeUnencryptedValue(value, mt
, param
.GetActualScale(), actualSize
, param
.Offset
, isDataFeed
, param
.NormalizationRuleVersion
, stateObj
);
8198 Debug
.Assert(serializedValue
!= null, "serializedValue should not be null in TdsExecuteRPC.");
8199 encryptedValue
= SqlSecurityUtility
.EncryptWithKey(serializedValue
, param
.CipherMetadata
, _connHandler
.ConnectionOptions
.DataSource
);
8201 catch (Exception e
) {
8202 throw SQL
.ParamEncryptionFailed(param
.ParameterName
, null, e
);
8205 Debug
.Assert(encryptedValue
!= null && encryptedValue
.Length
> 0,
8206 "encryptedValue should not be null or empty in TdsExecuteRPC.");
8209 encryptedValue
= null;
8212 // Change the datatype to varbinary(max).
8213 // Since we don't know the size of the encrypted parameter on the server side, always set to (max).
8215 mt
= MetaType
.MetaMaxVarBinary
;
8217 actualSize
= (encryptedValue
== null) ? 0 : encryptedValue
.Length
;
8219 encryptedParameterInfoToWrite
= new SqlColumnEncryptionInputParameterInfo(param
.GetMetadataForTypeInfo(),
8220 param
.CipherMetadata
);
8222 // Set the value to the encrypted value and mark isSqlVal as false for VARBINARY encrypted value.
8223 value = encryptedValue
;
8227 Debug
.Assert(isParameterEncrypted
== (encryptedParameterInfoToWrite
!= null),
8228 "encryptedParameterInfoToWrite can be not null if and only if isParameterEncrypted is true.");
8230 Debug
.Assert(!isSqlVal
|| !isParameterEncrypted
, "isParameterEncrypted can be true only if isSqlVal is false.");
8233 // fixup the types by using the NullableType property of the MetaType class
8235 // following rules should be followed based on feedback from the M-SQL team
8236 // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
8237 // 2) always use nullable types (ex: instead of SQLINT use SQLINTN)
8238 // 3) DECIMALN should always be sent as NUMERICN
8240 stateObj
.WriteByte(mt
.NullableType
);
8242 // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
8243 if (mt
.TDSType
== TdsEnums
.SQLVARIANT
) {
8244 // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant
8245 // param.GetActualSize is not used
8246 WriteSqlVariantValue(isSqlVal
? MetaType
.GetComValueFromSqlVariant(value) : value, param
.GetActualSize(), param
.Offset
, stateObj
);
8250 int codePageByteSize
= 0;
8253 if (mt
.IsAnsiType
) {
8254 // Avoid the following code block if ANSI but unfilled LazyMat blob
8255 if ((!isNull
) && (!isDataFeed
)) {
8259 if (value is SqlString
) {
8260 s
= ((SqlString
)value).Value
;
8263 Debug
.Assert(value is SqlChars
, "Unknown value for Ansi datatype");
8264 s
= new String(((SqlChars
)value).Value
);
8271 codePageByteSize
= GetEncodingCharLength(s
, actualSize
, param
.Offset
, _defaultEncoding
);
8275 WriteShort(TdsEnums
.SQL_USHORTVARMAXLEN
, stateObj
);
8278 maxsize
= (size
> codePageByteSize
) ? size
: codePageByteSize
;
8280 // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
8287 WriteParameterVarLen(mt
, maxsize
, false/*IsNull*/, stateObj
);
8291 // If type timestamp - treat as fixed type and always send over timestamp length, which is 8.
8292 // For fixed types, we either send null or fixed length for type length. We want to match that
8293 // behavior for timestamps. However, in the case of null, we still must send 8 because if we
8294 // send null we will not receive a output val. You can send null for fixed types and still
8295 // receive a output value, but not for variable types. So, always send 8 for timestamp because
8296 // while the user sees it as a fixed type, we are actually representing it as a bigbinary which
8298 if (mt
.SqlDbType
== SqlDbType
.Timestamp
) {
8299 WriteParameterVarLen(mt
, TdsEnums
.TEXT_TIME_STAMP_LEN
, false, stateObj
);
8301 else if (mt
.SqlDbType
== SqlDbType
.Udt
) {
8302 byte[] udtVal
= null;
8303 Microsoft
.SqlServer
.Server
.Format format
= Microsoft
.SqlServer
.Server
.Format
.Native
;
8305 Debug
.Assert(_isYukon
, "Invalid DataType UDT for non-Yukon or later server!");
8308 udtVal
= _connHandler
.Connection
.GetBytes(value, out format
, out maxsize
);
8310 Debug
.Assert(null != udtVal
, "GetBytes returned null instance. Make sure that it always returns non-null value");
8311 size
= udtVal
.Length
;
8313 //it may be legitimate, but we dont support it yet
8314 if (size
< 0 || (size
>= UInt16
.MaxValue
&& maxsize
!= -1))
8315 throw new IndexOutOfRangeException();
8318 //if this is NULL value, write special null value
8319 byte[] lenBytes
= BitConverter
.GetBytes((Int64
)size
);
8321 if (ADP
.IsEmpty(param
.UdtTypeName
))
8322 throw SQL
.MustSetUdtTypeNameForUdtParams();
8324 // Split the input name. TypeName is returned as single 3 part name during DeriveParameters.
8325 // NOTE: ParseUdtTypeName throws if format is incorrect
8326 String
[] names
= SqlParameter
.ParseTypeName(param
.UdtTypeName
, true /* is UdtTypeName */);
8327 if (!ADP
.IsEmpty(names
[0]) && TdsEnums
.MAX_SERVERNAME
< names
[0].Length
) {
8328 throw ADP
.ArgumentOutOfRange("names");
8330 if (!ADP
.IsEmpty(names
[1]) && TdsEnums
.MAX_SERVERNAME
< names
[names
.Length
- 2].Length
) {
8331 throw ADP
.ArgumentOutOfRange("names");
8333 if (TdsEnums
.MAX_SERVERNAME
< names
[2].Length
) {
8334 throw ADP
.ArgumentOutOfRange("names");
8337 WriteUDTMetaData(value, names
[0], names
[1], names
[2], stateObj
);
8341 WriteUnsignedLong((ulong)udtVal
.Length
, stateObj
); // PLP length
8342 if (udtVal
.Length
> 0) { // Only write chunk length if its value is greater than 0
8343 WriteInt(udtVal
.Length
, stateObj
); // Chunk length
8344 stateObj
.WriteByteArray(udtVal
, udtVal
.Length
, 0); // Value
8346 WriteInt(0, stateObj
); // Terminator
8349 WriteUnsignedLong(TdsEnums
.SQL_PLP_NULL
, stateObj
); // PLP Null.
8351 continue; // End of UDT - continue to next parameter.
8354 else if (mt
.IsPlp
) {
8355 if (mt
.SqlDbType
!= SqlDbType
.Xml
)
8356 WriteShort(TdsEnums
.SQL_USHORTVARMAXLEN
, stateObj
);
8358 else if ((!mt
.IsVarTime
) && (mt
.SqlDbType
!= SqlDbType
.Date
)) { // Time, Date, DateTime2, DateTimeoffset do not have the size written out
8359 maxsize
= (size
> actualSize
) ? size
: actualSize
;
8360 if (maxsize
== 0 && IsYukonOrNewer
) {
8361 // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
8368 WriteParameterVarLen(mt
, maxsize
, false/*IsNull*/, stateObj
);
8372 // scale and precision are only relevant for numeric and decimal types
8373 if (mt
.SqlDbType
== SqlDbType
.Decimal
) {
8374 if (0 == precision
) {
8376 stateObj
.WriteByte(TdsEnums
.DEFAULT_NUMERIC_PRECISION
);
8378 stateObj
.WriteByte(TdsEnums
.SPHINX_DEFAULT_NUMERIC_PRECISION
);
8381 stateObj
.WriteByte(precision
);
8383 stateObj
.WriteByte(scale
);
8385 else if (mt
.IsVarTime
) {
8386 stateObj
.WriteByte(param
.GetActualScale());
8389 // write out collation or xml metadata
8391 if (_isYukon
&& (mt
.SqlDbType
== SqlDbType
.Xml
)) {
8392 if (((param
.XmlSchemaCollectionDatabase
!= null) && (param
.XmlSchemaCollectionDatabase
!= ADP
.StrEmpty
)) ||
8393 ((param
.XmlSchemaCollectionOwningSchema
!= null) && (param
.XmlSchemaCollectionOwningSchema
!= ADP
.StrEmpty
)) ||
8394 ((param
.XmlSchemaCollectionName
!= null) && (param
.XmlSchemaCollectionName
!= ADP
.StrEmpty
))) {
8395 stateObj
.WriteByte(1); //Schema present flag
8397 if ((param
.XmlSchemaCollectionDatabase
!= null) && (param
.XmlSchemaCollectionDatabase
!= ADP
.StrEmpty
)) {
8398 tempLen
= (param
.XmlSchemaCollectionDatabase
).Length
;
8399 stateObj
.WriteByte((byte)(tempLen
));
8400 WriteString(param
.XmlSchemaCollectionDatabase
, tempLen
, 0, stateObj
);
8403 stateObj
.WriteByte(0); // No dbname
8406 if ((param
.XmlSchemaCollectionOwningSchema
!= null) && (param
.XmlSchemaCollectionOwningSchema
!= ADP
.StrEmpty
)) {
8407 tempLen
= (param
.XmlSchemaCollectionOwningSchema
).Length
;
8408 stateObj
.WriteByte((byte)(tempLen
));
8409 WriteString(param
.XmlSchemaCollectionOwningSchema
, tempLen
, 0, stateObj
);
8412 stateObj
.WriteByte(0); // no xml schema name
8414 if ((param
.XmlSchemaCollectionName
!= null) && (param
.XmlSchemaCollectionName
!= ADP
.StrEmpty
)) {
8415 tempLen
= (param
.XmlSchemaCollectionName
).Length
;
8416 WriteShort((short)(tempLen
), stateObj
);
8417 WriteString(param
.XmlSchemaCollectionName
, tempLen
, 0, stateObj
);
8420 WriteShort(0, stateObj
); // No xml schema collection name
8425 stateObj
.WriteByte(0); // No schema
8428 else if (_isShiloh
&& mt
.IsCharType
) {
8429 // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
8430 SqlCollation outCollation
= (param
.Collation
!= null) ? param
.Collation
: _defaultCollation
;
8431 Debug
.Assert(_defaultCollation
!= null, "_defaultCollation is null!");
8433 WriteUnsignedInt(outCollation
.info
, stateObj
);
8434 stateObj
.WriteByte(outCollation
.sortId
);
8437 if (0 == codePageByteSize
)
8438 WriteParameterVarLen(mt
, actualSize
, isNull
, stateObj
, isDataFeed
);
8440 WriteParameterVarLen(mt
, codePageByteSize
, isNull
, stateObj
, isDataFeed
);
8442 Task writeParamTask
= null;
8443 // write the value now
8446 writeParamTask
= WriteSqlValue(value, mt
, actualSize
, codePageByteSize
, param
.Offset
, stateObj
);
8449 // for codePageEncoded types, WriteValue simply expects the number of characters
8450 // For plp types, we also need the encoded byte size
8451 writeParamTask
= WriteValue(value, mt
, isParameterEncrypted
? (byte)0 : param
.GetActualScale(), actualSize
, codePageByteSize
, isParameterEncrypted
? 0 : param
.Offset
, stateObj
, isParameterEncrypted
? 0 : param
.Size
, isDataFeed
);
8455 // Send encryption metadata for encrypted parameters.
8456 if (isParameterEncrypted
) {
8457 writeParamTask
= WriteEncryptionMetadata(writeParamTask
, encryptedParameterInfoToWrite
, stateObj
);
8461 if (writeParamTask
== null) {
8462 writeParamTask
= stateObj
.WaitForAccumulatedWrites();
8465 if (writeParamTask
!= null) {
8467 if (completion
== null) {
8468 completion
= new TaskCompletionSource
<object>();
8469 task
= completion
.Task
;
8472 AsyncHelper
.ContinueTask(writeParamTask
, completion
,
8473 () => TdsExecuteRPC(cmd
, rpcArray
, timeout
, inSchema
, notificationRequest
, stateObj
, isCommandProc
, sync
, completion
,
8474 startRpc: ii
, startParam
: i
+ 1),
8475 connectionToDoom: _connHandler
,
8476 onFailure: exc
=> TdsExecuteRPC_OnFailure(exc
, stateObj
));
8478 // Take care of releasing the locks
8479 if (releaseConnectionLock
) {
8480 task
.ContinueWith(_
=> {
8481 _connHandler
._parserLock
.Release();
8482 }, TaskScheduler
.Default
);
8483 releaseConnectionLock
= false;
8491 Debug
.Assert(writeParamTask
== null, "Should not have a task when executing sync");
8494 } // parameter for loop
8496 // If this is not the last RPC we are sending, add the batch flag
8497 if (ii
< (rpcArray
.Length
- 1)) {
8499 stateObj
.WriteByte(TdsEnums
.YUKON_RPCBATCHFLAG
);
8503 stateObj
.WriteByte(TdsEnums
.SHILOH_RPCBATCHFLAG
);
8508 Task execFlushTask
= stateObj
.ExecuteFlush();
8509 Debug
.Assert(!sync
|| execFlushTask
== null, "Should not get a task when executing sync");
8510 if (execFlushTask
!= null) {
8513 if (completion
== null) {
8514 completion
= new TaskCompletionSource
<object>();
8515 task
= completion
.Task
;
8518 bool taskReleaseConnectionLock
= releaseConnectionLock
;
8519 execFlushTask
.ContinueWith(tsk
=> ExecuteFlushTaskCallback(tsk
, stateObj
, completion
, taskReleaseConnectionLock
), TaskScheduler
.Default
);
8521 // ExecuteFlushTaskCallback will take care of the locks for us
8522 releaseConnectionLock
= false;
8527 catch (Exception e
) {
8529 if (!ADP
.IsCatchableExceptionType(e
)) {
8533 FailureCleanup(stateObj
, e
);
8537 FinalizeExecuteRPC(stateObj
);
8538 if (completion
!= null) {
8539 completion
.SetResult(null);
8543 catch (Exception e
) {
8544 FinalizeExecuteRPC(stateObj
);
8545 if (completion
!= null) {
8546 completion
.SetException(e
);
8554 Debug
.Assert(firstCall
|| !releaseConnectionLock
, "Shouldn't be releasing locks synchronously after the first call");
8555 if (releaseConnectionLock
) {
8556 _connHandler
._parserLock
.Release();
8561 private void FinalizeExecuteRPC(TdsParserStateObject stateObj
) {
8562 stateObj
.SniContext
= SniContext
.Snix_Read
;
8563 _asyncWrite
= false;
8566 private void TdsExecuteRPC_OnFailure(Exception exc
, TdsParserStateObject stateObj
) {
8567 RuntimeHelpers
.PrepareConstrainedRegions();
8570 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
8572 RuntimeHelpers
.PrepareConstrainedRegions();
8574 tdsReliabilitySection
.Start();
8578 FailureCleanup(stateObj
, exc
);
8582 tdsReliabilitySection
.Stop();
8586 catch (System
.OutOfMemoryException
) {
8587 _connHandler
.DoomThisConnection();
8590 catch (System
.StackOverflowException
) {
8591 _connHandler
.DoomThisConnection();
8594 catch (System
.Threading
.ThreadAbortException
) {
8595 _connHandler
.DoomThisConnection();
8600 private void ExecuteFlushTaskCallback(Task tsk
, TdsParserStateObject stateObj
, TaskCompletionSource
<object> completion
, bool releaseConnectionLock
) {
8602 FinalizeExecuteRPC(stateObj
);
8603 if (tsk
.Exception
!= null) {
8604 Exception exc
= tsk
.Exception
.InnerException
;
8605 RuntimeHelpers
.PrepareConstrainedRegions();
8608 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
8610 RuntimeHelpers
.PrepareConstrainedRegions();
8612 tdsReliabilitySection
.Start();
8616 FailureCleanup(stateObj
, tsk
.Exception
);
8620 tdsReliabilitySection
.Stop();
8624 catch (System
.OutOfMemoryException e
) {
8625 _connHandler
.DoomThisConnection();
8626 completion
.SetException(e
);
8629 catch (System
.StackOverflowException e
) {
8630 _connHandler
.DoomThisConnection();
8631 completion
.SetException(e
);
8634 catch (System
.Threading
.ThreadAbortException e
) {
8635 _connHandler
.DoomThisConnection();
8636 completion
.SetException(e
);
8639 catch (Exception e
) {
8642 completion
.SetException(exc
);
8645 completion
.SetResult(null);
8649 if (releaseConnectionLock
) {
8650 _connHandler
._parserLock
.Release();
8656 private void WriteParameterName(string parameterName
, TdsParserStateObject stateObj
) {
8659 if (!ADP
.IsEmpty(parameterName
)) {
8660 Debug
.Assert(parameterName
.Length
<= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
8661 int tempLen
= parameterName
.Length
& 0xff;
8662 stateObj
.WriteByte((byte)tempLen
);
8663 WriteString(parameterName
, tempLen
, 0, stateObj
);
8666 stateObj
.WriteByte(0);
8670 private static readonly IEnumerable
<MSS
.SqlDataRecord
> __tvpEmptyValue
= new List
<MSS
.SqlDataRecord
>().AsReadOnly();
8671 private void WriteSmiParameter(SqlParameter param
, int paramIndex
, bool sendDefault
, TdsParserStateObject stateObj
) {
8673 // Determine Metadata
8675 ParameterPeekAheadValue peekAhead
;
8676 MSS
.SmiParameterMetaData metaData
= param
.MetaDataForSmi(out peekAhead
);
8679 MetaType mt
= MetaType
.GetMetaTypeFromSqlDbType(metaData
.SqlDbType
, metaData
.IsMultiValued
);
8680 throw ADP
.VersionDoesNotSupportDataType(mt
.TypeName
);
8684 // Determine value to send
8687 MSS
.ExtendedClrTypeCode typeCode
;
8689 // if we have an output or default param, set the value to null so we do not send it across to the server
8691 // Value for TVP default is empty list, not NULL
8692 if (SqlDbType
.Structured
== metaData
.SqlDbType
&& metaData
.IsMultiValued
) {
8693 value = __tvpEmptyValue
;
8694 typeCode
= MSS
.ExtendedClrTypeCode
.IEnumerableOfSqlDataRecord
;
8697 // Need to send null value for default
8699 typeCode
= MSS
.ExtendedClrTypeCode
.DBNull
;
8702 else if (param
.Direction
== ParameterDirection
.Output
) {
8703 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.
8706 typeCode
= MSS
.ExtendedClrTypeCode
.DBNull
;
8707 param
.ParamaterIsSqlType
= isCLRType
;
8710 value = param
.GetCoercedValue();
8711 typeCode
= MSS
.MetaDataUtilsSmi
.DetermineExtendedTypeCodeForUseWithSqlDbType(
8712 metaData
.SqlDbType
, metaData
.IsMultiValued
, value, null, MSS
.SmiContextFactory
.KatmaiVersion
);
8715 if (Bid
.AdvancedOn
) {
8716 Bid
.Trace("<sc.TdsParser.WriteSmiParameter|ADV> %d#, Sending parameter '%ls', default flag=%d, metadata:\n", ObjectID
, param
.ParameterName
, sendDefault
?1:0);
8717 Bid
.PutStr(metaData
.TraceString(3));
8722 // Write parameter metadata
8724 WriteSmiParameterMetaData(metaData
, sendDefault
, stateObj
);
8727 // Now write the value
8729 TdsParameterSetter paramSetter
= new TdsParameterSetter(stateObj
, metaData
);
8730 MSS
.ValueUtilsSmi
.SetCompatibleValueV200(
8731 new MSS
.SmiEventSink_Default(), // TDS Errors/events dealt with at lower level for now, just need an object for processing
8733 0, // ordinal. TdsParameterSetter only handles one parameter at a time
8738 0 < param
.Size
? param
.Size
: -1,
8742 // Writes metadata portion of parameter stream from an SmiParameterMetaData object.
8743 private void WriteSmiParameterMetaData(MSS
.SmiParameterMetaData metaData
, bool sendDefault
, TdsParserStateObject stateObj
) {
8746 if (ParameterDirection
.Output
== metaData
.Direction
|| ParameterDirection
.InputOutput
== metaData
.Direction
) {
8747 status
|= TdsEnums
.RPC_PARAM_BYREF
;
8751 status
|= TdsEnums
.RPC_PARAM_DEFAULT
;
8754 // Write everything out
8755 WriteParameterName(metaData
.Name
, stateObj
);
8756 stateObj
.WriteByte(status
);
8757 WriteSmiTypeInfo(metaData
, stateObj
);
8760 // Write a TypeInfo stream
8761 // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR, SQLNVARCHAR, and SQLBIGVARBINARY
8762 private void WriteSmiTypeInfo(MSS
.SmiExtendedMetaData metaData
, TdsParserStateObject stateObj
) {
8763 switch(metaData
.SqlDbType
) {
8764 case SqlDbType
.BigInt
:
8765 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8766 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8768 case SqlDbType
.Binary
:
8769 stateObj
.WriteByte(TdsEnums
.SQLBIGBINARY
);
8770 WriteUnsignedShort(checked((ushort)metaData
.MaxLength
), stateObj
);
8773 stateObj
.WriteByte(TdsEnums
.SQLBITN
);
8774 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8776 case SqlDbType
.Char
:
8777 stateObj
.WriteByte(TdsEnums
.SQLBIGCHAR
);
8778 WriteUnsignedShort(checked((ushort)(metaData
.MaxLength
)), stateObj
);
8779 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8780 stateObj
.WriteByte(_defaultCollation
.sortId
);
8782 case SqlDbType
.DateTime
:
8783 stateObj
.WriteByte(TdsEnums
.SQLDATETIMN
);
8784 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8786 case SqlDbType
.Decimal
:
8787 stateObj
.WriteByte(TdsEnums
.SQLNUMERICN
);
8788 stateObj
.WriteByte(checked((byte)MetaType
.MetaDecimal
.FixedLength
)); // SmiMetaData's length and actual wire format's length are different
8789 stateObj
.WriteByte(0 == metaData
.Precision
? (byte)1 : metaData
.Precision
);
8790 stateObj
.WriteByte(metaData
.Scale
);
8792 case SqlDbType
.Float
:
8793 stateObj
.WriteByte(TdsEnums
.SQLFLTN
);
8794 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8796 case SqlDbType
.Image
:
8797 stateObj
.WriteByte(TdsEnums
.SQLBIGVARBINARY
);
8798 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8801 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8802 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8804 case SqlDbType
.Money
:
8805 stateObj
.WriteByte(TdsEnums
.SQLMONEYN
);
8806 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8808 case SqlDbType
.NChar
:
8809 stateObj
.WriteByte(TdsEnums
.SQLNCHAR
);
8810 WriteUnsignedShort(checked((ushort)(metaData
.MaxLength
*2)), stateObj
);
8811 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8812 stateObj
.WriteByte(_defaultCollation
.sortId
);
8814 case SqlDbType
.NText
:
8815 stateObj
.WriteByte(TdsEnums
.SQLNVARCHAR
);
8816 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8817 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8818 stateObj
.WriteByte(_defaultCollation
.sortId
);
8820 case SqlDbType
.NVarChar
:
8821 stateObj
.WriteByte(TdsEnums
.SQLNVARCHAR
);
8822 if (MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
== metaData
.MaxLength
) {
8823 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8826 WriteUnsignedShort(checked((ushort)(metaData
.MaxLength
*2)), stateObj
);
8828 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8829 stateObj
.WriteByte(_defaultCollation
.sortId
);
8831 case SqlDbType
.Real
:
8832 stateObj
.WriteByte(TdsEnums
.SQLFLTN
);
8833 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8835 case SqlDbType
.UniqueIdentifier
:
8836 stateObj
.WriteByte(TdsEnums
.SQLUNIQUEID
);
8837 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8839 case SqlDbType
.SmallDateTime
:
8840 stateObj
.WriteByte(TdsEnums
.SQLDATETIMN
);
8841 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8843 case SqlDbType
.SmallInt
:
8844 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8845 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8847 case SqlDbType
.SmallMoney
:
8848 stateObj
.WriteByte(TdsEnums
.SQLMONEYN
);
8849 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8851 case SqlDbType
.Text
:
8852 stateObj
.WriteByte(TdsEnums
.SQLBIGVARCHAR
);
8853 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8854 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8855 stateObj
.WriteByte(_defaultCollation
.sortId
);
8857 case SqlDbType
.Timestamp
:
8858 stateObj
.WriteByte(TdsEnums
.SQLBIGBINARY
);
8859 WriteShort(checked((int)metaData
.MaxLength
), stateObj
);
8861 case SqlDbType
.TinyInt
:
8862 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8863 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8865 case SqlDbType
.VarBinary
:
8866 stateObj
.WriteByte(TdsEnums
.SQLBIGVARBINARY
);
8867 WriteUnsignedShort(unchecked((ushort)metaData
.MaxLength
), stateObj
);
8869 case SqlDbType
.VarChar
:
8870 stateObj
.WriteByte(TdsEnums
.SQLBIGVARCHAR
);
8871 WriteUnsignedShort(unchecked((ushort)metaData
.MaxLength
), stateObj
);
8872 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8873 stateObj
.WriteByte(_defaultCollation
.sortId
);
8875 case SqlDbType
.Variant
:
8876 stateObj
.WriteByte(TdsEnums
.SQLVARIANT
);
8877 WriteInt(checked((int)metaData
.MaxLength
), stateObj
);
8880 stateObj
.WriteByte(TdsEnums
.SQLXMLTYPE
);
8881 // Is there a schema
8882 if (ADP
.IsEmpty(metaData
.TypeSpecificNamePart1
) && ADP
.IsEmpty(metaData
.TypeSpecificNamePart2
) &&
8883 ADP
.IsEmpty(metaData
.TypeSpecificNamePart3
)) {
8884 stateObj
.WriteByte(0); // schema not present
8887 stateObj
.WriteByte(1); // schema present
8888 WriteIdentifier(metaData
.TypeSpecificNamePart1
, stateObj
);
8889 WriteIdentifier(metaData
.TypeSpecificNamePart2
, stateObj
);
8890 WriteIdentifierWithShortLength(metaData
.TypeSpecificNamePart3
, stateObj
);
8894 stateObj
.WriteByte(TdsEnums
.SQLUDT
);
8895 WriteIdentifier(metaData
.TypeSpecificNamePart1
, stateObj
);
8896 WriteIdentifier(metaData
.TypeSpecificNamePart2
, stateObj
);
8897 WriteIdentifier(metaData
.TypeSpecificNamePart3
, stateObj
);
8899 case SqlDbType
.Structured
:
8900 if (metaData
.IsMultiValued
) {
8901 WriteTvpTypeInfo(metaData
, stateObj
);
8904 Debug
.Assert(false, "SUDTs not yet supported.");
8907 case SqlDbType
.Date
:
8908 stateObj
.WriteByte(TdsEnums
.SQLDATE
);
8910 case SqlDbType
.Time
:
8911 stateObj
.WriteByte(TdsEnums
.SQLTIME
);
8912 stateObj
.WriteByte(metaData
.Scale
);
8914 case SqlDbType
.DateTime2
:
8915 stateObj
.WriteByte(TdsEnums
.SQLDATETIME2
);
8916 stateObj
.WriteByte(metaData
.Scale
);
8918 case SqlDbType
.DateTimeOffset
:
8919 stateObj
.WriteByte(TdsEnums
.SQLDATETIMEOFFSET
);
8920 stateObj
.WriteByte(metaData
.Scale
);
8923 Debug
.Assert(false, "Unknown SqlDbType should have been caught earlier!");
8928 private void WriteTvpTypeInfo(MSS
.SmiExtendedMetaData metaData
, TdsParserStateObject stateObj
) {
8929 Debug
.Assert(SqlDbType
.Structured
== metaData
.SqlDbType
&& metaData
.IsMultiValued
,
8930 "Invalid metadata for TVPs. Type=" + metaData
.SqlDbType
);
8932 stateObj
.WriteByte((byte)TdsEnums
.SQLTABLE
);
8934 // 3-part name (DB, Schema, TypeName)
8935 WriteIdentifier(metaData
.TypeSpecificNamePart1
, stateObj
);
8936 WriteIdentifier(metaData
.TypeSpecificNamePart2
, stateObj
);
8937 WriteIdentifier(metaData
.TypeSpecificNamePart3
, stateObj
);
8940 if (0 == metaData
.FieldMetaData
.Count
) {
8941 WriteUnsignedShort((ushort)TdsEnums
.TVP_NOMETADATA_TOKEN
, stateObj
);
8945 WriteUnsignedShort(checked((ushort) metaData
.FieldMetaData
.Count
), stateObj
);
8947 // TvpColumnMetaData for each column (look for defaults in this loop
8948 MSS
.SmiDefaultFieldsProperty defaults
= (MSS
.SmiDefaultFieldsProperty
) metaData
.ExtendedProperties
[MSS
.SmiPropertySelector
.DefaultFields
];
8949 for(int i
=0; i
<metaData
.FieldMetaData
.Count
; i
++) {
8950 WriteTvpColumnMetaData(metaData
.FieldMetaData
[i
], defaults
[i
], stateObj
);
8953 // optional OrderUnique metadata
8954 WriteTvpOrderUnique(metaData
, stateObj
);
8958 // END of optional metadata
8959 stateObj
.WriteByte(TdsEnums
.TVP_END_TOKEN
);
8962 // Write a single TvpColumnMetaData stream to the server
8963 private void WriteTvpColumnMetaData(MSS
.SmiExtendedMetaData md
, bool isDefault
, TdsParserStateObject stateObj
) {
8965 if (SqlDbType
.Timestamp
== md
.SqlDbType
) {
8966 WriteUnsignedInt(TdsEnums
.SQLTIMESTAMP
, stateObj
);
8968 WriteUnsignedInt(0, stateObj
);
8972 ushort status
= TdsEnums
.Nullable
;
8974 status
|= TdsEnums
.TVP_DEFAULT_COLUMN
;
8976 WriteUnsignedShort(status
, stateObj
);
8979 WriteSmiTypeInfo(md
, stateObj
);
8982 // per spec, "ColName is never sent to server or client for TVP, it is required within a TVP to be zero length."
8983 WriteIdentifier(null, stateObj
);
8986 // temporary-results structure used only by WriteTvpOrderUnique
8987 // use class to avoid List<T>'s per-struct-instantiated memory costs.
8988 private class TdsOrderUnique
{
8989 internal short ColumnOrdinal
;
8990 internal byte Flags
;
8992 internal TdsOrderUnique(short ordinal
, byte flags
) {
8993 ColumnOrdinal
= ordinal
;
8998 private void WriteTvpOrderUnique(MSS
.SmiExtendedMetaData metaData
, TdsParserStateObject stateObj
) {
8999 // TVP_ORDER_UNIQUE token (uniqueness and sort order)
9001 // Merge order and unique keys into a single token stream
9003 MSS
.SmiOrderProperty orderProperty
= (MSS
.SmiOrderProperty
) metaData
.ExtendedProperties
[MSS
.SmiPropertySelector
.SortOrder
];
9004 MSS
.SmiUniqueKeyProperty uniqueKeyProperty
= (MSS
.SmiUniqueKeyProperty
) metaData
.ExtendedProperties
[MSS
.SmiPropertySelector
.UniqueKey
];
9007 List
<TdsOrderUnique
> columnList
= new List
<TdsOrderUnique
>(metaData
.FieldMetaData
.Count
);
9008 for(int i
=0; i
<metaData
.FieldMetaData
.Count
; i
++) {
9010 // Add appropriate SortOrder flag
9012 MSS
.SmiOrderProperty
.SmiColumnOrder columnOrder
= orderProperty
[i
];
9013 if (SortOrder
.Ascending
== columnOrder
.Order
) {
9014 flags
= TdsEnums
.TVP_ORDERASC_FLAG
;
9016 else if (SortOrder
.Descending
== columnOrder
.Order
) {
9017 flags
= TdsEnums
.TVP_ORDERDESC_FLAG
;
9020 // Add unique key flage if appropriate
9021 if (uniqueKeyProperty
[i
]) {
9022 flags
|= TdsEnums
.TVP_UNIQUE_FLAG
;
9025 // Remember this column if any flags were set
9027 columnList
.Add(new TdsOrderUnique(checked((short)(i
+1)), flags
));
9031 // Write flagged columns to wire...
9032 if (0 < columnList
.Count
) {
9033 stateObj
.WriteByte(TdsEnums
.TVP_ORDER_UNIQUE_TOKEN
);
9034 WriteShort(columnList
.Count
, stateObj
);
9035 foreach(TdsOrderUnique column
in columnList
) {
9036 WriteShort(column
.ColumnOrdinal
, stateObj
);
9037 stateObj
.WriteByte(column
.Flags
);
9042 internal Task
WriteBulkCopyDone(TdsParserStateObject stateObj
) {
9043 // Write DONE packet
9045 if (!(State
== TdsParserState
.OpenNotLoggedIn
|| State
== TdsParserState
.OpenLoggedIn
)) {
9046 throw ADP
.ClosedConnectionError();
9048 stateObj
.WriteByte(TdsEnums
.SQLDONE
);
9049 WriteShort(0, stateObj
);
9050 WriteShort(0, stateObj
);
9051 WriteInt(0, stateObj
);
9053 stateObj
._pendingData
= true;
9054 stateObj
._messageStatus
= 0;
9055 return stateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
9059 /// Loads the column encryptions keys into cache. This will read the master key info,
9060 /// decrypt the CEK and keep it ready for encryption.
9062 /// <returns></returns>
9063 internal void LoadColumnEncryptionKeys (_SqlMetaDataSet metadataCollection
, string serverName
) {
9064 if (_serverSupportsColumnEncryption
&& ShouldEncryptValuesForBulkCopy()) {
9065 for (int col
= 0; col
< metadataCollection
.Length
; col
++) {
9066 if (null != metadataCollection
[col
]) {
9067 _SqlMetaData md
= metadataCollection
[col
];
9068 if (md
.isEncrypted
) {
9069 SqlSecurityUtility
.DecryptSymmetricKey(md
.cipherMD
, serverName
);
9077 /// Writes a single entry of CEK Table into TDS Stream (for bulk copy).
9079 /// <returns></returns>
9080 internal void WriteEncryptionEntries (ref SqlTceCipherInfoTable cekTable
, TdsParserStateObject stateObj
) {
9081 for (int i
=0; i
< cekTable
.Size
; i
++) {
9083 WriteInt(cekTable
[i
].DatabaseId
, stateObj
);
9086 WriteInt(cekTable
[i
].CekId
, stateObj
);
9088 // Write Key Version
9089 WriteInt(cekTable
[i
].CekVersion
, stateObj
);
9091 // Write 8 bytes of key MD Version
9092 Debug
.Assert (8 == cekTable
[i
].CekMdVersion
.Length
);
9093 stateObj
.WriteByteArray (cekTable
[i
].CekMdVersion
, 8, 0);
9095 // We don't really need to send the keys
9096 stateObj
.WriteByte(0x00);
9101 /// Writes a CEK Table (as part of COLMETADATA token) for bulk copy.
9103 /// <returns></returns>
9104 internal void WriteCekTable (_SqlMetaDataSet metadataCollection
, TdsParserStateObject stateObj
) {
9105 if (!_serverSupportsColumnEncryption
) {
9109 // If no cek table is present, send a count of 0 for table size
9110 // Note- Cek table (with 0 entries) will be present if TCE
9111 // was enabled and server supports it!
9112 // OR if encryption was disabled in connection options
9113 if (!metadataCollection
.cekTable
.HasValue
||
9114 !ShouldEncryptValuesForBulkCopy()) {
9115 WriteShort(0x00, stateObj
);
9119 SqlTceCipherInfoTable cekTable
= metadataCollection
.cekTable
.Value
;
9120 ushort count
= (ushort)cekTable
.Size
;
9122 WriteShort(count
, stateObj
);
9124 WriteEncryptionEntries(ref cekTable
, stateObj
);
9128 /// Writes the UserType and TYPE_INFO values for CryptoMetadata (for bulk copy).
9130 /// <returns></returns>
9131 internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv
, TdsParserStateObject stateObj
) {
9132 // Write the UserType (4 byte value)
9133 WriteInt(0x0, stateObj
); //
9135 Debug
.Assert(SqlDbType
.Xml
!= mdPriv
.type
);
9136 Debug
.Assert(SqlDbType
.Udt
!= mdPriv
.type
);
9138 stateObj
.WriteByte(mdPriv
.tdsType
);
9140 switch (mdPriv
.type
) {
9141 case SqlDbType
.Decimal
:
9142 WriteTokenLength(mdPriv
.tdsType
, mdPriv
.length
, stateObj
);
9143 stateObj
.WriteByte(mdPriv
.precision
);
9144 stateObj
.WriteByte(mdPriv
.scale
);
9146 case SqlDbType
.Date
:
9147 // Nothing more to write!
9149 case SqlDbType
.Time
:
9150 case SqlDbType
.DateTime2
:
9151 case SqlDbType
.DateTimeOffset
:
9152 stateObj
.WriteByte(mdPriv
.scale
);
9155 WriteTokenLength(mdPriv
.tdsType
, mdPriv
.length
, stateObj
);
9156 if (mdPriv
.metaType
.IsCharType
&& _isShiloh
) {
9157 WriteUnsignedInt(mdPriv
.collation
.info
, stateObj
);
9158 stateObj
.WriteByte(mdPriv
.collation
.sortId
);
9165 /// Writes the crypto metadata (as part of COLMETADATA token) for encrypted columns.
9167 /// <returns></returns>
9168 internal void WriteCryptoMetadata(_SqlMetaData md
, TdsParserStateObject stateObj
) {
9169 if (!_serverSupportsColumnEncryption
|| // TCE Feature supported
9170 !md
.isEncrypted
|| // Column is not encrypted
9171 !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string
9175 // Write the ordinal
9176 WriteShort (md
.cipherMD
.CekTableOrdinal
, stateObj
);
9178 // Write UserType and TYPEINFO
9179 WriteTceUserTypeAndTypeInfo(md
.baseTI
, stateObj
);
9181 // Write Encryption Algo
9182 stateObj
.WriteByte(md
.cipherMD
.CipherAlgorithmId
);
9184 if (TdsEnums
.CustomCipherAlgorithmId
== md
.cipherMD
.CipherAlgorithmId
) {
9185 // Write the algorithm name
9186 Debug
.Assert (md
.cipherMD
.CipherAlgorithmName
.Length
< 256);
9187 stateObj
.WriteByte((byte)md
.cipherMD
.CipherAlgorithmName
.Length
);
9188 WriteString(md
.cipherMD
.CipherAlgorithmName
, stateObj
);
9191 // Write Encryption Algo Type
9192 stateObj
.WriteByte(md
.cipherMD
.EncryptionType
);
9194 // Write Normalization Version
9195 stateObj
.WriteByte(md
.cipherMD
.NormalizationRuleVersion
);
9198 internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection
, int count
, TdsParserStateObject stateObj
) {
9199 if (!(State
== TdsParserState
.OpenNotLoggedIn
|| State
== TdsParserState
.OpenLoggedIn
)) {
9200 throw ADP
.ClosedConnectionError();
9203 stateObj
.WriteByte(TdsEnums
.SQLCOLMETADATA
);
9204 WriteShort(count
, stateObj
);
9206 // Write CEK table - 0 count
9207 WriteCekTable(metadataCollection
, stateObj
);
9209 for (int i
= 0; i
< metadataCollection
.Length
; i
++) {
9210 if (metadataCollection
[i
] != null) {
9211 _SqlMetaData md
= metadataCollection
[i
];
9213 // read user type - 4 bytes Yukon, 2 backwards
9214 if (IsYukonOrNewer
) {
9215 WriteInt(0x0, stateObj
);
9218 WriteShort(0x0000, stateObj
);
9223 flags
= (UInt16
)(md
.updatability
<< 2);
9224 flags
|= (UInt16
)(md
.isNullable
? (UInt16
)TdsEnums
.Nullable
: (UInt16
)0);
9225 flags
|= (UInt16
)(md
.isIdentity
? (UInt16
)TdsEnums
.Identity
: (UInt16
)0);
9227 // Write the next byte of flags
9228 if (_serverSupportsColumnEncryption
) { // TCE Supported
9229 if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options
9230 flags
|= (UInt16
)(md
.isEncrypted
? (UInt16
)(TdsEnums
.IsEncrypted
<< 8) : (UInt16
)0);
9234 WriteShort(flags
, stateObj
);// write the flags
9237 // for xml WriteTokenLength results in a no-op
9238 // discuss this with blaine ...
9239 // (Microsoft) xml datatype does not have token length in its metadata. So it should be a noop.
9242 case SqlDbType
.Decimal
:
9243 stateObj
.WriteByte(md
.tdsType
);
9244 WriteTokenLength(md
.tdsType
, md
.length
, stateObj
);
9245 stateObj
.WriteByte(md
.precision
);
9246 stateObj
.WriteByte(md
.scale
);
9250 stateObj
.WriteByteArray(s_xmlMetadataSubstituteSequence
, s_xmlMetadataSubstituteSequence
.Length
, 0);
9253 stateObj
.WriteByte(TdsEnums
.SQLBIGVARBINARY
);
9254 WriteTokenLength(TdsEnums
.SQLBIGVARBINARY
, md
.length
, stateObj
);
9256 case SqlDbType
.Date
:
9257 stateObj
.WriteByte(md
.tdsType
);
9259 case SqlDbType
.Time
:
9260 case SqlDbType
.DateTime2
:
9261 case SqlDbType
.DateTimeOffset
:
9262 stateObj
.WriteByte(md
.tdsType
);
9263 stateObj
.WriteByte(md
.scale
);
9266 stateObj
.WriteByte(md
.tdsType
);
9267 WriteTokenLength(md
.tdsType
, md
.length
, stateObj
);
9268 if (md
.metaType
.IsCharType
&& _isShiloh
) {
9269 WriteUnsignedInt(md
.collation
.info
, stateObj
);
9270 stateObj
.WriteByte(md
.collation
.sortId
);
9275 if (md
.metaType
.IsLong
&& !md
.metaType
.IsPlp
) {
9276 WriteShort(md
.tableName
.Length
, stateObj
);
9277 WriteString(md
.tableName
, stateObj
);
9280 WriteCryptoMetadata(md
, stateObj
);
9282 stateObj
.WriteByte((byte)md
.column
.Length
);
9283 WriteString(md
.column
, stateObj
);
9289 /// Determines if a column value should be encrypted when using BulkCopy (based on connectionstring setting).
9291 /// <returns></returns>
9292 internal bool ShouldEncryptValuesForBulkCopy () {
9293 if (null != _connHandler
&&
9294 null != _connHandler
.ConnectionOptions
&&
9295 SqlConnectionColumnEncryptionSetting
.Enabled
== _connHandler
.ConnectionOptions
.ColumnEncryptionSetting
) {
9303 /// Encrypts a column value (for SqlBulkCopy)
9305 /// <returns></returns>
9306 internal object EncryptColumnValue (object value, SqlMetaDataPriv metadata
, string column
, TdsParserStateObject stateObj
, bool isDataFeed
, bool isSqlType
) {
9307 Debug
.Assert (_serverSupportsColumnEncryption
, "Server doesn't support encryption, yet we received encryption metadata");
9308 Debug
.Assert (ShouldEncryptValuesForBulkCopy(), "Encryption attempted when not requested");
9310 if (isDataFeed
) { // can't encrypt a stream column
9311 SQL
.StreamNotSupportOnEncryptedColumn(column
);
9314 int actualLengthInBytes
;
9315 switch(metadata
.baseTI
.metaType
.NullableType
) {
9316 case TdsEnums
.SQLBIGBINARY
:
9317 case TdsEnums
.SQLBIGVARBINARY
:
9318 case TdsEnums
.SQLIMAGE
:
9319 // For some datatypes, engine does truncation before storing the value. (For example, when
9320 // trying to insert a varbinary(7000) into a varbinary(3000) column). Since we encrypt the
9321 // column values, engine has no way to tell the size of the plaintext datatype. Therefore,
9322 // we truncate the values based on target column sizes here before encrypting them. This
9323 // truncation is only needed if we exceed the max column length or if the target column is
9324 // not a blob type (eg. varbinary(max)). The actual work of truncating the column happens
9325 // when we normalize and serialize the data buffers. The serialization routine expects us
9326 // to report the size of data to be copied out (for serialization). If we underreport the
9327 // size, truncation will happen for us!
9328 actualLengthInBytes
= (isSqlType
) ? ((SqlBinary
)value).Length
: ((byte[])value).Length
;
9329 if (metadata
.baseTI
.length
> 0 &&
9330 actualLengthInBytes
> metadata
.baseTI
.length
) { // see comments agove
9331 actualLengthInBytes
= metadata
.baseTI
.length
;
9335 case TdsEnums
.SQLUNIQUEID
:
9336 actualLengthInBytes
= GUID_SIZE
; // that's a constant for guid
9338 case TdsEnums
.SQLBIGCHAR
:
9339 case TdsEnums
.SQLBIGVARCHAR
:
9340 case TdsEnums
.SQLTEXT
:
9341 if (null == _defaultEncoding
)
9343 ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
9346 string stringValue
= (isSqlType
) ? ((SqlString
)value).Value
: (string)value;
9347 actualLengthInBytes
= _defaultEncoding
.GetByteCount(stringValue
);
9349 // If the string length is > max length, then use the max length (see comments above)
9350 if (metadata
.baseTI
.length
> 0 &&
9351 actualLengthInBytes
> metadata
.baseTI
.length
) {
9352 actualLengthInBytes
= metadata
.baseTI
.length
; // this ensure truncation!
9356 case TdsEnums
.SQLNCHAR
:
9357 case TdsEnums
.SQLNVARCHAR
:
9358 case TdsEnums
.SQLNTEXT
:
9359 actualLengthInBytes
= ((isSqlType
) ? ((SqlString
)value).Value
.Length
: ((string)value).Length
) * 2;
9361 if (metadata
.baseTI
.length
> 0 &&
9362 actualLengthInBytes
> metadata
.baseTI
.length
) { // see comments above
9363 actualLengthInBytes
= metadata
.baseTI
.length
;
9369 actualLengthInBytes
= metadata
.baseTI
.length
;
9373 byte[] serializedValue
;
9376 serializedValue
= SerializeUnencryptedSqlValue (value,
9377 metadata
.baseTI
.metaType
,
9378 actualLengthInBytes
,
9380 normalizationVersion: metadata
.cipherMD
.NormalizationRuleVersion
,
9381 stateObj: stateObj
);
9384 serializedValue
= SerializeUnencryptedValue (value,
9385 metadata
.baseTI
.metaType
,
9386 metadata
.baseTI
.scale
,
9387 actualLengthInBytes
,
9389 isDataFeed: isDataFeed
,
9390 normalizationVersion: metadata
.cipherMD
.NormalizationRuleVersion
,
9391 stateObj: stateObj
);
9394 Debug
.Assert(serializedValue
!= null, "serializedValue should not be null in TdsExecuteRPC.");
9395 return SqlSecurityUtility
.EncryptWithKey(
9398 _connHandler
.ConnectionOptions
.DataSource
);
9401 internal Task
WriteBulkCopyValue(object value, SqlMetaDataPriv metadata
, TdsParserStateObject stateObj
, bool isSqlType
, bool isDataFeed
, bool isNull
) {
9402 Debug
.Assert(!isSqlType
|| value is INullable
, "isSqlType is true, but value can not be type cast to an INullable");
9403 Debug
.Assert(!isDataFeed ^
value is DataFeed
, "Incorrect value for isDataFeed");
9405 Encoding saveEncoding
= _defaultEncoding
;
9406 SqlCollation saveCollation
= _defaultCollation
;
9407 int saveCodePage
= _defaultCodePage
;
9408 int saveLCID
= _defaultLCID
;
9409 Task resultTask
= null;
9410 Task internalWriteTask
= null;
9412 if (!(State
== TdsParserState
.OpenNotLoggedIn
|| State
== TdsParserState
.OpenLoggedIn
)) {
9413 throw ADP
.ClosedConnectionError();
9416 if (metadata
.encoding
!= null) {
9417 _defaultEncoding
= metadata
.encoding
;
9419 if (metadata
.collation
!= null) {
9420 _defaultCollation
= metadata
.collation
;
9421 _defaultLCID
= _defaultCollation
.LCID
;
9423 _defaultCodePage
= metadata
.codePage
;
9425 MetaType metatype
= metadata
.metaType
;
9427 int ccbStringBytes
= 0;
9430 // For UDT, remember we treat as binary even though it is a PLP
9431 if (metatype
.IsPlp
&& (metatype
.NullableType
!= TdsEnums
.SQLUDT
|| metatype
.IsLong
)) {
9432 WriteLong(unchecked((long)TdsEnums
.SQL_PLP_NULL
), stateObj
);
9434 else if (!metatype
.IsFixed
&& !metatype
.IsLong
&& !metatype
.IsVarTime
) {
9435 WriteShort(TdsEnums
.VARNULL
, stateObj
);
9438 stateObj
.WriteByte(TdsEnums
.FIXEDNULL
);
9444 switch (metatype
.NullableType
) {
9445 case TdsEnums
.SQLBIGBINARY
:
9446 case TdsEnums
.SQLBIGVARBINARY
:
9447 case TdsEnums
.SQLIMAGE
:
9448 case TdsEnums
.SQLUDT
:
9449 ccb
= (isSqlType
) ? ((SqlBinary
)value).Length
: ((byte[])value).Length
;
9451 case TdsEnums
.SQLUNIQUEID
:
9452 ccb
= GUID_SIZE
; // that's a constant for guid
9454 case TdsEnums
.SQLBIGCHAR
:
9455 case TdsEnums
.SQLBIGVARCHAR
:
9456 case TdsEnums
.SQLTEXT
:
9457 if (null == _defaultEncoding
) {
9458 ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
9461 string stringValue
= null;
9463 stringValue
= ((SqlString
)value).Value
;
9466 stringValue
= (string)value;
9469 ccb
= stringValue
.Length
;
9470 ccbStringBytes
= _defaultEncoding
.GetByteCount(stringValue
);
9472 case TdsEnums
.SQLNCHAR
:
9473 case TdsEnums
.SQLNVARCHAR
:
9474 case TdsEnums
.SQLNTEXT
:
9475 ccb
= ((isSqlType
) ? ((SqlString
)value).Value
.Length
: ((string)value).Length
) * 2;
9477 case TdsEnums
.SQLXMLTYPE
:
9478 // Value here could be string or XmlReader
9479 if (value is XmlReader
) {
9480 value = MetaType
.GetStringFromXml((XmlReader
)value);
9482 ccb
= ((isSqlType
) ? ((SqlString
)value).Value
.Length
: ((string)value).Length
) * 2;
9486 ccb
= metadata
.length
;
9491 Debug
.Assert(metatype
.IsLong
&&
9492 ((metatype
.SqlDbType
== SqlDbType
.VarBinary
&& value is StreamDataFeed
) ||
9493 ((metatype
.SqlDbType
== SqlDbType
.VarChar
|| metatype
.SqlDbType
== SqlDbType
.NVarChar
) && value is TextDataFeed
) ||
9494 (metatype
.SqlDbType
== SqlDbType
.Xml
&& value is XmlDataFeed
)),
9495 "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)");
9499 // Expected the text length in data stream for bulk copy of text, ntext, or image data.
9501 if (metatype
.IsLong
) {
9502 switch (metatype
.SqlDbType
) {
9503 case SqlDbType
.Text
:
9504 case SqlDbType
.NText
:
9505 case SqlDbType
.Image
:
9506 stateObj
.WriteByteArray(s_longDataHeader
, s_longDataHeader
.Length
, 0);
9507 WriteTokenLength(metadata
.tdsType
, ccbStringBytes
== 0 ? ccb
: ccbStringBytes
, stateObj
);
9510 case SqlDbType
.VarChar
:
9511 case SqlDbType
.NVarChar
:
9512 case SqlDbType
.VarBinary
:
9516 WriteUnsignedLong(TdsEnums
.SQL_PLP_UNKNOWNLEN
, stateObj
);
9521 WriteTokenLength(metadata
.tdsType
, ccbStringBytes
== 0 ? ccb
: ccbStringBytes
, stateObj
);
9525 internalWriteTask
= WriteSqlValue(value, metatype
, ccb
, ccbStringBytes
, 0, stateObj
);
9527 else if (metatype
.SqlDbType
!= SqlDbType
.Udt
|| metatype
.IsLong
) {
9528 internalWriteTask
= WriteValue(value, metatype
, metadata
.scale
, ccb
, ccbStringBytes
, 0, stateObj
, metadata
.length
, isDataFeed
);
9529 if ((internalWriteTask
== null) && (_asyncWrite
)) {
9530 internalWriteTask
= stateObj
.WaitForAccumulatedWrites();
9532 Debug
.Assert(_asyncWrite
|| stateObj
.WaitForAccumulatedWrites() == null, "Should not have accumulated writes when writing sync");
9535 WriteShort(ccb
, stateObj
);
9536 internalWriteTask
= stateObj
.WriteByteArray((byte[])value, ccb
, 0);
9540 //In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths.
9541 if (_asyncWrite
&& internalWriteTask
== null && SqlBulkCopy
.SetAlwaysTaskOnWrite
== true) {
9542 internalWriteTask
= Task
.FromResult
<object>(null);
9545 if (internalWriteTask
!= null) { //i.e. the write was async.
9546 resultTask
= WriteBulkCopyValueSetupContinuation(internalWriteTask
, saveEncoding
, saveCollation
, saveCodePage
, saveLCID
);
9550 if (internalWriteTask
== null) {
9551 _defaultEncoding
= saveEncoding
;
9552 _defaultCollation
= saveCollation
;
9553 _defaultCodePage
= saveCodePage
;
9554 _defaultLCID
= saveLCID
;
9560 // This is in its own method to avoid always allocating the lambda in WriteBulkCopyValue
9561 private Task
WriteBulkCopyValueSetupContinuation(Task internalWriteTask
, Encoding saveEncoding
, SqlCollation saveCollation
, int saveCodePage
, int saveLCID
) {
9562 return internalWriteTask
.ContinueWith
<Task
>(t
=> {
9563 _defaultEncoding
= saveEncoding
;
9564 _defaultCollation
= saveCollation
;
9565 _defaultCodePage
= saveCodePage
;
9566 _defaultLCID
= saveLCID
;
9568 }, TaskScheduler
.Default
).Unwrap();
9571 // Write mars header data, not including the mars header length
9572 private void WriteMarsHeaderData(TdsParserStateObject stateObj
, SqlInternalTransaction transaction
) {
9573 // Function to send over additional payload header data for Yukon and beyond only.
9574 Debug
.Assert(_isYukon
, "WriteMarsHeaderData called on a non-Yukon server");
9576 // These are not necessary - can have local started in distributed.
9577 // Debug.Assert(!(null != sqlTransaction && null != distributedTransaction), "Error to have local (api started) and distributed transaction at the same time!");
9578 // Debug.Assert(!(null != _userStartedLocalTransaction && null != distributedTransaction), "Error to have local (started outside of the api) and distributed transaction at the same time!");
9580 // We may need to update the mars header length if mars header is changed in the future
9582 WriteShort(TdsEnums
.HEADERTYPE_MARS
, stateObj
);
9584 if (null != transaction
&& SqlInternalTransaction
.NullTransactionId
!= transaction
.TransactionId
) {
9585 WriteLong(transaction
.TransactionId
, stateObj
);
9586 WriteInt(stateObj
.IncrementAndObtainOpenResultCount(transaction
), stateObj
);
9589 // If no transaction, send over retained transaction descriptor (empty if none retained)
9590 // and always 1 for result count.
9591 WriteLong(_retainedTransactionId
, stateObj
);
9592 WriteInt(stateObj
.IncrementAndObtainOpenResultCount(null), stateObj
);
9596 private int GetNotificationHeaderSize(SqlNotificationRequest notificationRequest
) {
9597 if (null != notificationRequest
) {
9598 string callbackId
= notificationRequest
.UserData
;
9599 string service
= notificationRequest
.Options
;
9600 int timeout
= notificationRequest
.Timeout
;
9602 if (null == callbackId
) {
9603 throw ADP
.ArgumentNull("CallbackId");
9605 else if (UInt16
.MaxValue
< callbackId
.Length
) {
9606 throw ADP
.ArgumentOutOfRange("CallbackId");
9609 if (null == service
) {
9610 throw ADP
.ArgumentNull("Service");
9612 else if (UInt16
.MaxValue
< service
.Length
) {
9613 throw ADP
.ArgumentOutOfRange("Service");
9617 throw ADP
.ArgumentOutOfRange("Timeout");
9620 // Header Length (uint) (included in size) (already written to output buffer)
9621 // Header Type (ushort)
9622 // NotifyID Length (ushort)
9623 // NotifyID UnicodeStream (unicode text)
9624 // SSBDeployment Length (ushort)
9625 // SSBDeployment UnicodeStream (unicode text)
9626 // Timeout (uint) -- optional
9627 // WEBDATA 102263: Don't send timeout value if it is 0
9629 int headerLength
= 4 + 2 + 2 + (callbackId
.Length
* 2) + 2 + (service
.Length
* 2);
9632 return headerLength
;
9639 // Write query notificaiton header data, not including the notificaiton header length
9640 private void WriteQueryNotificationHeaderData(SqlNotificationRequest notificationRequest
, TdsParserStateObject stateObj
) {
9641 Debug
.Assert(_isYukon
, "WriteQueryNotificationHeaderData called on a non-Yukon server");
9643 // We may need to update the notification header length if the header is changed in the future
9645 Debug
.Assert (null != notificationRequest
, "notificaitonRequest is null");
9647 string callbackId
= notificationRequest
.UserData
;
9648 string service
= notificationRequest
.Options
;
9649 int timeout
= notificationRequest
.Timeout
;
9651 // we did verification in GetNotificationHeaderSize, so just assert here.
9652 Debug
.Assert(null != callbackId
, "CallbackId is null");
9653 Debug
.Assert(UInt16
.MaxValue
>= callbackId
.Length
, "CallbackId length is out of range");
9654 Debug
.Assert(null != service
, "Service is null");
9655 Debug
.Assert(UInt16
.MaxValue
>= service
.Length
, "Service length is out of range");
9656 Debug
.Assert(-1 <= timeout
, "Timeout");
9659 Bid
.NotificationsTrace("<sc.TdsParser.WriteQueryNotificationHeader|DEP> NotificationRequest: userData: '%ls', options: '%ls', timeout: '%d'\n", notificationRequest
.UserData
, notificationRequest
.Options
, notificationRequest
.Timeout
);
9661 WriteShort(TdsEnums
.HEADERTYPE_QNOTIFICATION
, stateObj
); // Query notifications Type
9663 WriteShort(callbackId
.Length
* 2, stateObj
); // Length in bytes
9664 WriteString(callbackId
, stateObj
);
9666 WriteShort(service
.Length
* 2, stateObj
); // Length in bytes
9667 WriteString(service
, stateObj
);
9669 WriteInt(timeout
, stateObj
);
9672 // Write the trace header data, not including the trace header length
9673 private void WriteTraceHeaderData(TdsParserStateObject stateObj
) {
9674 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");
9676 // We may need to update the trace header length if trace header is changed in the future
9678 ActivityCorrelator
.ActivityId actId
= ActivityCorrelator
.Current
;
9680 WriteShort(TdsEnums
.HEADERTYPE_TRACE
, stateObj
); // Trace Header Type
9682 stateObj
.WriteByteArray(actId
.Id
.ToByteArray(), GUID_SIZE
, 0); // Id (Guid)
9683 WriteUnsignedInt(actId
.Sequence
, stateObj
); // sequence number
9685 Bid
.Trace("<sc.TdsParser.WriteTraceHeaderData|INFO> ActivityID %ls\n", actId
.ToString());
9688 private void WriteRPCBatchHeaders(TdsParserStateObject stateObj
, SqlNotificationRequest notificationRequest
) {
9689 Debug
.Assert(_isYukon
, "WriteRPCBatchHeaders can only be called on Yukon or higher version servers");
9692 TotalLength - DWORD - including all headers and lengths, including itself
9695 HeaderLength - DWORD - including all header length fields, including itself
9701 int notificationHeaderSize
= GetNotificationHeaderSize(notificationRequest
);
9703 const int marsHeaderSize
= 18; // 4 + 2 + 8 + 4
9705 // Header Length (DWORD)
9706 // Header Type (ushort)
9708 // Trace Data Sequence Number (uint)
9709 const int traceHeaderSize
= 26; // 4 + 2 + GUID_SIZE + sizeof(UInt32);
9711 // TotalLength - DWORD - including all headers and lengths, including itself
9712 int totalHeaderLength
= this.IncludeTraceHeader
? (4 + marsHeaderSize
+ notificationHeaderSize
+ traceHeaderSize
) : (4 + marsHeaderSize
+ notificationHeaderSize
);
9713 Debug
.Assert(stateObj
._outBytesUsed
== stateObj
._outputHeaderLen
, "Output bytes written before total header length");
9714 // Write total header length
9715 WriteInt(totalHeaderLength
, stateObj
);
9717 // Write Mars header length
9718 WriteInt(marsHeaderSize
, stateObj
);
9719 // Write Mars header data
9720 WriteMarsHeaderData(stateObj
, CurrentTransaction
);
9722 if (0 != notificationHeaderSize
) {
9723 // Write Notification header length
9724 WriteInt(notificationHeaderSize
, stateObj
);
9725 // Write notificaiton header data
9726 WriteQueryNotificationHeaderData(notificationRequest
, stateObj
);
9729 if (IncludeTraceHeader
) {
9731 // Write trace header length
9732 WriteInt(traceHeaderSize
, stateObj
);
9733 // Write trace header data
9734 WriteTraceHeaderData(stateObj
);
9740 // Reverse function of GetTokenLength
9742 private void WriteTokenLength(byte token
, int length
, TdsParserStateObject stateObj
) {
9743 int tokenLength
= 0;
9745 Debug
.Assert(token
!= 0, "0 length token!");
9747 // For Plp fields, this should only be used when writing to metadata header.
9748 // For actual data length, WriteDataLength should be used.
9749 // For Xml fields, there is no token length field. For MAX fields it is 0xffff.
9750 if (_isYukon
) { // Handle Yukon specific exceptions
9751 if (TdsEnums
.SQLUDT
== token
) {
9754 else if (token
== TdsEnums
.SQLXMLTYPE
) {
9759 if (tokenLength
== 0) {
9760 switch (token
& TdsEnums
.SQLLenMask
) {
9761 case TdsEnums
.SQLFixedLen
:
9762 Debug
.Assert(length
== 0x01 << ((token
& 0x0c) >> 2), "length does not match encoded length in token");
9766 case TdsEnums
.SQLZeroLen
:
9770 case TdsEnums
.SQLVarLen
:
9771 case TdsEnums
.SQLVarCnt
:
9772 if (0 != (token
& 0x80))
9774 else if (0 == (token
& 0x0c))
9784 Debug
.Assert(false, "Unknown token length!");
9788 switch (tokenLength
) {
9790 stateObj
.WriteByte((byte)length
);
9794 WriteShort(length
, stateObj
);
9798 WriteInt(length
, stateObj
);
9802 // In the metadata case we write 0xffff for partial length prefixed types.
9803 // For actual data length preceding data, WriteDataLength should be used.
9804 WriteShort(TdsEnums
.SQL_USHORTVARMAXLEN
, stateObj
);
9810 // Returns true if BOM byte mark is needed for an XML value
9811 private bool IsBOMNeeded(MetaType type
, object value) {
9812 if (type
.NullableType
== TdsEnums
.SQLXMLTYPE
) {
9813 Type currentType
= value.GetType();
9815 if(currentType
== typeof(SqlString
)) {
9816 if (!((SqlString
)value).IsNull
&& ((((SqlString
)value).Value
).Length
> 0)) {
9817 if ((((SqlString
)value).Value
[0] & 0xff) != 0xff)
9821 else if ((currentType
== typeof(String
)) && (((String
)value).Length
> 0)) {
9822 if ((value != null) && (((String
)value)[0] & 0xff) != 0xff)
9825 else if (currentType
== typeof(SqlXml
)) {
9826 if (!((SqlXml
)value).IsNull
)
9829 else if (currentType
== typeof(XmlDataFeed
)) {
9830 return true; // Values will eventually converted to unicode string here
9836 private Task
GetTerminationTask(Task unterminatedWriteTask
, object value, MetaType type
, int actualLength
, TdsParserStateObject stateObj
, bool isDataFeed
) {
9837 if (type
.IsPlp
&& ((actualLength
> 0) || isDataFeed
)) {
9838 if (unterminatedWriteTask
== null) {
9839 WriteInt(0, stateObj
);
9843 return AsyncHelper
.CreateContinuationTask
<int, TdsParserStateObject
>(unterminatedWriteTask
,
9844 WriteInt
, 0, stateObj
,
9845 connectionToDoom: _connHandler
);
9849 return unterminatedWriteTask
;
9854 private Task
WriteSqlValue(object value, MetaType type
, int actualLength
, int codePageByteSize
, int offset
, TdsParserStateObject stateObj
) {
9855 return GetTerminationTask(
9856 WriteUnterminatedSqlValue(value, type
, actualLength
, codePageByteSize
, offset
, stateObj
),
9857 value, type
, actualLength
, stateObj
, false);
9860 // For MAX types, this method can only write everything in one big chunk. If multiple
9861 // chunk writes needed, please use WritePlpBytes/WritePlpChars
9862 private Task
WriteUnterminatedSqlValue(object value, MetaType type
, int actualLength
, int codePageByteSize
, int offset
, TdsParserStateObject stateObj
) {
9863 Debug
.Assert(((type
.NullableType
== TdsEnums
.SQLXMLTYPE
) ||
9864 (value is INullable
&& !((INullable
)value).IsNull
)),
9865 "unexpected null SqlType!");
9867 // parameters are always sent over as BIG or N types
9868 switch (type
.NullableType
) {
9869 case TdsEnums
.SQLFLTN
:
9870 if (type
.FixedLength
== 4)
9871 WriteFloat(((SqlSingle
)value).Value
, stateObj
);
9873 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
9874 WriteDouble(((SqlDouble
)value).Value
, stateObj
);
9879 case TdsEnums
.SQLBIGBINARY
:
9880 case TdsEnums
.SQLBIGVARBINARY
:
9881 case TdsEnums
.SQLIMAGE
:
9884 WriteInt(actualLength
, stateObj
); // chunk length
9887 if (value is SqlBinary
) {
9888 return stateObj
.WriteByteArray(((SqlBinary
)value).Value
, actualLength
, offset
, canAccumulate
:false);
9891 Debug
.Assert(value is SqlBytes
);
9892 return stateObj
.WriteByteArray(((SqlBytes
)value).Value
, actualLength
, offset
, canAccumulate
:false);
9896 case TdsEnums
.SQLUNIQUEID
:
9898 byte[] b
= ((SqlGuid
)value).ToByteArray();
9900 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
9901 stateObj
.WriteByteArray(b
, actualLength
, 0);
9905 case TdsEnums
.SQLBITN
:
9907 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
9908 if (((SqlBoolean
)value).Value
== true)
9909 stateObj
.WriteByte(1);
9911 stateObj
.WriteByte(0);
9916 case TdsEnums
.SQLINTN
:
9917 if (type
.FixedLength
== 1)
9918 stateObj
.WriteByte(((SqlByte
)value).Value
);
9920 if (type
.FixedLength
== 2)
9921 WriteShort(((SqlInt16
)value).Value
, stateObj
);
9923 if (type
.FixedLength
== 4)
9924 WriteInt(((SqlInt32
)value).Value
, stateObj
);
9926 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
9927 WriteLong(((SqlInt64
)value).Value
, stateObj
);
9932 case TdsEnums
.SQLBIGCHAR
:
9933 case TdsEnums
.SQLBIGVARCHAR
:
9934 case TdsEnums
.SQLTEXT
:
9936 WriteInt(codePageByteSize
, stateObj
); // chunk length
9938 if (value is SqlChars
) {
9939 String sch
= new String(((SqlChars
)value).Value
);
9941 return WriteEncodingChar(sch
, actualLength
, offset
, _defaultEncoding
, stateObj
, canAccumulate
:false);
9944 Debug
.Assert(value is SqlString
);
9945 return WriteEncodingChar(((SqlString
)value).Value
, actualLength
, offset
, _defaultEncoding
, stateObj
, canAccumulate
:false);
9949 case TdsEnums
.SQLNCHAR
:
9950 case TdsEnums
.SQLNVARCHAR
:
9951 case TdsEnums
.SQLNTEXT
:
9952 case TdsEnums
.SQLXMLTYPE
:
9955 if(IsBOMNeeded(type
, value)) {
9956 WriteInt(actualLength
+2, stateObj
); // chunk length
9957 WriteShort(TdsEnums
.XMLUNICODEBOM
, stateObj
);
9959 WriteInt(actualLength
, stateObj
); // chunk length
9963 // convert to cchars instead of cbytes
9964 // Xml type is already converted to string through GetCoercedValue
9965 if (actualLength
!= 0)
9968 if (value is SqlChars
) {
9969 return WriteCharArray(((SqlChars
)value).Value
, actualLength
, offset
, stateObj
, canAccumulate
:false);
9972 Debug
.Assert(value is SqlString
);
9973 return WriteString(((SqlString
)value).Value
, actualLength
, offset
, stateObj
, canAccumulate
:false);
9976 case TdsEnums
.SQLNUMERICN
:
9977 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
9978 WriteSqlDecimal((SqlDecimal
)value, stateObj
);
9981 case TdsEnums
.SQLDATETIMN
:
9982 SqlDateTime dt
= (SqlDateTime
)value;
9984 if (type
.FixedLength
== 4) {
9985 if (0 > dt
.DayTicks
|| dt
.DayTicks
> UInt16
.MaxValue
)
9986 throw SQL
.SmallDateTimeOverflow(dt
.ToString());
9988 WriteShort(dt
.DayTicks
, stateObj
);
9989 WriteShort(dt
.TimeTicks
/ SqlDateTime
.SQLTicksPerMinute
, stateObj
);
9992 WriteInt(dt
.DayTicks
, stateObj
);
9993 WriteInt(dt
.TimeTicks
, stateObj
);
9998 case TdsEnums
.SQLMONEYN
:
10000 WriteSqlMoney((SqlMoney
)value, type
.FixedLength
, stateObj
);
10004 case TdsEnums
.SQLUDT
:
10005 Debug
.Assert(false, "Called WriteSqlValue on UDT param.Should have already been handled");
10006 throw SQL
.UDTUnexpectedResult(value.GetType().AssemblyQualifiedName
);
10009 Debug
.Assert(false, "Unknown TdsType!" + type
.NullableType
.ToString("x2", (IFormatProvider
)null));
10012 // return point for accumualated writes, note: non-accumulated writes returned from their case statements
10016 private class TdsOutputStream
: Stream
{
10018 TdsParserStateObject _stateObj
;
10019 byte[] _preambleToStrip
;
10021 public TdsOutputStream(TdsParser parser
, TdsParserStateObject stateObj
, byte[] preambleToStrip
) {
10023 _stateObj
= stateObj
;
10024 _preambleToStrip
= preambleToStrip
;
10027 public override bool CanRead
{
10028 get { return false; }
10031 public override bool CanSeek
{
10032 get { return false; }
10035 public override bool CanWrite
{
10036 get { return true; }
10039 public override void Flush() {
10043 public override long Length
{
10044 get { throw new NotSupportedException(); }
10047 public override long Position
{
10049 throw new NotSupportedException();
10052 throw new NotSupportedException();
10056 public override int Read(byte[] buffer
, int offset
, int count
) {
10057 throw new NotSupportedException();
10060 public override long Seek(long offset
, SeekOrigin origin
) {
10061 throw new NotSupportedException();
10064 public override void SetLength(long value) {
10065 throw new NotSupportedException();
10068 private void StripPreamble(byte[] buffer
, ref int offset
, ref int count
) {
10069 if (_preambleToStrip
!= null && count
>= _preambleToStrip
.Length
) {
10071 for (int idx
= 0; idx
< _preambleToStrip
.Length
; idx
++) {
10072 if (_preambleToStrip
[idx
] != buffer
[idx
]) {
10073 _preambleToStrip
= null;
10078 offset
+= _preambleToStrip
.Length
;
10079 count
-= _preambleToStrip
.Length
;
10081 _preambleToStrip
= null;
10084 public override void Write(byte[] buffer
, int offset
, int count
) {
10085 Debug
.Assert(!_parser
._asyncWrite
);
10086 ValidateWriteParameters(buffer
, offset
, count
);
10088 StripPreamble(buffer
, ref offset
, ref count
);
10091 _parser
.WriteInt(count
, _stateObj
); // write length of chunk
10092 _stateObj
.WriteByteArray(buffer
, count
, offset
);
10096 public override Task
WriteAsync(byte[] buffer
, int offset
, int count
, CancellationToken cancellationToken
) {
10097 Debug
.Assert(_parser
._asyncWrite
);
10098 ValidateWriteParameters(buffer
, offset
, count
);
10100 StripPreamble(buffer
, ref offset
, ref count
);
10102 RuntimeHelpers
.PrepareConstrainedRegions();
10105 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
10107 RuntimeHelpers
.PrepareConstrainedRegions();
10109 tdsReliabilitySection
.Start();
10115 _parser
.WriteInt(count
, _stateObj
); // write length of chunk
10116 task
= _stateObj
.WriteByteArray(buffer
, count
, offset
, canAccumulate
: false);
10118 if (task
== null) {
10119 return CompletedTask
;
10127 tdsReliabilitySection
.Stop();
10131 catch (System
.OutOfMemoryException
) {
10132 _parser
._connHandler
.DoomThisConnection();
10135 catch (System
.StackOverflowException
) {
10136 _parser
._connHandler
.DoomThisConnection();
10139 catch (System
.Threading
.ThreadAbortException
) {
10140 _parser
._connHandler
.DoomThisConnection();
10145 internal static void ValidateWriteParameters(byte[] buffer
, int offset
, int count
) {
10146 if (buffer
== null) {
10147 throw ADP
.ArgumentNull(ADP
.ParameterBuffer
);
10150 throw ADP
.ArgumentOutOfRange(ADP
.ParameterOffset
);
10153 throw ADP
.ArgumentOutOfRange(ADP
.ParameterCount
);
10156 if (checked(offset
+ count
) > buffer
.Length
) {
10157 throw ExceptionBuilder
.InvalidOffsetLength();
10160 catch (OverflowException
) {
10161 // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
10162 throw ExceptionBuilder
.InvalidOffsetLength();
10168 private class ConstrainedTextWriter
: TextWriter
{
10173 public ConstrainedTextWriter(TextWriter next
, int size
) {
10179 _size
= int.MaxValue
;
10183 public bool IsComplete
{
10185 return _size
> 0 && _written
>= _size
;
10189 public override Encoding Encoding
{
10190 get { return _next.Encoding; }
10193 public override void Flush() {
10197 public override Task
FlushAsync() {
10198 return _next
.FlushAsync();
10201 public override void Write(char value) {
10202 if (_written
< _size
) {
10203 _next
.Write(value);
10206 Debug
.Assert(_size
< 0 || _written
<= _size
, string.Format("Length of data written exceeds specified length. Written: {0}, specified: {1}", _written
, _size
));
10209 public override void Write(char[] buffer
, int index
, int count
) {
10211 ValidateWriteParameters(buffer
, index
, count
);
10213 Debug
.Assert(_size
>= _written
);
10214 count
= Math
.Min(_size
- _written
, count
);
10216 _next
.Write(buffer
, index
, count
);
10221 public override Task
WriteAsync(char value) {
10223 if (_written
< _size
) {
10225 return _next
.WriteAsync(value);
10228 return CompletedTask
;
10231 public override Task
WriteAsync(char[] buffer
, int index
, int count
) {
10233 ValidateWriteParameters(buffer
, index
, count
);
10235 Debug
.Assert(_size
>= _written
);
10236 count
= Math
.Min(_size
- _written
, count
);
10239 return _next
.WriteAsync(buffer
, index
, count
);
10242 return CompletedTask
;
10245 public override Task
WriteAsync(string value) {
10246 return WriteAsync(value.ToCharArray());
10249 internal static void ValidateWriteParameters(char[] buffer
, int offset
, int count
) {
10250 if (buffer
== null) {
10251 throw ADP
.ArgumentNull(ADP
.ParameterBuffer
);
10254 throw ADP
.ArgumentOutOfRange(ADP
.ParameterOffset
);
10257 throw ADP
.ArgumentOutOfRange(ADP
.ParameterCount
);
10260 if (checked(offset
+ count
) > buffer
.Length
) {
10261 throw ExceptionBuilder
.InvalidOffsetLength();
10264 catch (OverflowException
) {
10265 // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
10266 throw ExceptionBuilder
.InvalidOffsetLength();
10272 private async Task
WriteXmlFeed(XmlDataFeed feed
, TdsParserStateObject stateObj
, bool needBom
, Encoding encoding
, int size
) {
10273 byte[] preambleToSkip
= null;
10275 preambleToSkip
= encoding
.GetPreamble();
10277 ConstrainedTextWriter writer
= new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj
, preambleToSkip
), encoding
), size
);
10279 XmlWriterSettings writerSettings
= new XmlWriterSettings();
10280 writerSettings
.CloseOutput
= false; // don't close the memory stream
10281 writerSettings
.ConformanceLevel
= ConformanceLevel
.Fragment
;
10283 writerSettings
.Async
= true;
10285 XmlWriter ww
= XmlWriter
.Create(writer
, writerSettings
);
10287 if (feed
._source
.ReadState
== ReadState
.Initial
) {
10288 feed
._source
.Read();
10291 while (!feed
._source
.EOF
&& !writer
.IsComplete
) {
10293 // We are copying nodes from a reader to a writer. This will cause the
10294 // XmlDeclaration to be emitted despite ConformanceLevel.Fragment above.
10295 // Therefore, we filter out the XmlDeclaration while copying.
10296 if (feed
._source
.NodeType
== XmlNodeType
.XmlDeclaration
) {
10297 feed
._source
.Read();
10302 await ww
.WriteNodeAsync(feed
._source
, true).ConfigureAwait(false);
10305 ww
.WriteNode(feed
._source
, true);
10310 await ww
.FlushAsync().ConfigureAwait(false);
10317 private async Task
WriteTextFeed(TextDataFeed feed
, Encoding encoding
, bool needBom
, TdsParserStateObject stateObj
, int size
) {
10318 Debug
.Assert(encoding
== null || !needBom
);
10319 char[] inBuff
= new char[constTextBufferSize
];
10321 encoding
= encoding
?? new UnicodeEncoding(false, false);
10322 ConstrainedTextWriter writer
= new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj
, null), encoding
), size
);
10326 await writer
.WriteAsync((char)TdsEnums
.XMLUNICODEBOM
).ConfigureAwait(false);
10329 writer
.Write((char)TdsEnums
.XMLUNICODEBOM
);
10338 nRead
= await feed
._source
.ReadBlockAsync(inBuff
, 0, constTextBufferSize
).ConfigureAwait(false);
10341 nRead
= feed
._source
.ReadBlock(inBuff
, 0, constTextBufferSize
);
10349 await writer
.WriteAsync(inBuff
, 0, nRead
).ConfigureAwait(false);
10352 writer
.Write(inBuff
, 0, nRead
);
10356 } while (!writer
.IsComplete
);
10359 await writer
.FlushAsync().ConfigureAwait(false);
10366 private async Task
WriteStreamFeed(StreamDataFeed feed
, TdsParserStateObject stateObj
, int len
) {
10367 TdsOutputStream output
= new TdsOutputStream(this, stateObj
, null);
10368 byte[] buff
= new byte[constBinBufferSize
];
10372 int readSize
= constBinBufferSize
;
10373 if (len
> 0 && nWritten
+ readSize
> len
) {
10374 readSize
= len
- nWritten
;
10377 Debug
.Assert(readSize
>= 0);
10380 nRead
= await feed
._source
.ReadAsync(buff
, 0, readSize
).ConfigureAwait(false);
10383 nRead
= feed
._source
.Read(buff
, 0, readSize
);
10391 await output
.WriteAsync(buff
, 0, nRead
).ConfigureAwait(false);
10394 output
.Write(buff
, 0, nRead
);
10398 } while (len
<= 0 || nWritten
< len
);
10401 private Task
NullIfCompletedWriteTask(Task task
) {
10402 if (task
== null) {
10405 switch (task
.Status
) {
10406 case TaskStatus
.RanToCompletion
:
10408 case TaskStatus
.Faulted
:
10409 throw task
.Exception
.InnerException
;
10410 case TaskStatus
.Canceled
:
10411 throw SQL
.OperationCancelled();
10417 private Task
WriteValue(object value, MetaType type
, byte scale
, int actualLength
, int encodingByteSize
, int offset
, TdsParserStateObject stateObj
, int paramSize
, bool isDataFeed
) {
10418 return GetTerminationTask(WriteUnterminatedValue(value, type
, scale
, actualLength
, encodingByteSize
, offset
, stateObj
, paramSize
, isDataFeed
),
10419 value, type
, actualLength
, stateObj
, isDataFeed
);
10422 // For MAX types, this method can only write everything in one big chunk. If multiple
10423 // chunk writes needed, please use WritePlpBytes/WritePlpChars
10424 private Task
WriteUnterminatedValue(object value, MetaType type
, byte scale
, int actualLength
, int encodingByteSize
, int offset
, TdsParserStateObject stateObj
, int paramSize
, bool isDataFeed
)
10426 Debug
.Assert((null != value) && (DBNull
.Value
!= value), "unexpected missing or empty object");
10428 // parameters are always sent over as BIG or N types
10429 switch (type
.NullableType
) {
10430 case TdsEnums
.SQLFLTN
:
10431 if (type
.FixedLength
== 4)
10432 WriteFloat((Single
)value, stateObj
);
10434 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
10435 WriteDouble((Double
)value, stateObj
);
10440 case TdsEnums
.SQLBIGBINARY
:
10441 case TdsEnums
.SQLBIGVARBINARY
:
10442 case TdsEnums
.SQLIMAGE
:
10443 case TdsEnums
.SQLUDT
: {
10444 // An array should be in the object
10445 Debug
.Assert(isDataFeed
|| value is byte[], "Value should be an array of bytes");
10446 Debug
.Assert(!isDataFeed
|| value is StreamDataFeed
, "Value should be a stream");
10449 Debug
.Assert(type
.IsPlp
,"Stream assigned to non-PLP was not converted!");
10450 return NullIfCompletedWriteTask(WriteStreamFeed((StreamDataFeed
)value, stateObj
, paramSize
));
10454 WriteInt(actualLength
, stateObj
); // chunk length
10457 return stateObj
.WriteByteArray((byte[])value, actualLength
, offset
, canAccumulate
: false);
10461 case TdsEnums
.SQLUNIQUEID
: {
10462 System
.Guid guid
= (System
.Guid
)value;
10463 byte[] b
= guid
.ToByteArray();
10465 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
10466 stateObj
.WriteByteArray(b
, actualLength
, 0);
10470 case TdsEnums
.SQLBITN
: {
10471 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
10472 if ((bool)value == true)
10473 stateObj
.WriteByte(1);
10475 stateObj
.WriteByte(0);
10480 case TdsEnums
.SQLINTN
:
10481 if (type
.FixedLength
== 1)
10482 stateObj
.WriteByte((byte)value);
10483 else if (type
.FixedLength
== 2)
10484 WriteShort((Int16
)value, stateObj
);
10485 else if (type
.FixedLength
== 4)
10486 WriteInt((Int32
)value, stateObj
);
10488 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
10489 WriteLong((Int64
)value, stateObj
);
10494 case TdsEnums
.SQLBIGCHAR
:
10495 case TdsEnums
.SQLBIGVARCHAR
:
10496 case TdsEnums
.SQLTEXT
: {
10497 Debug
.Assert(!isDataFeed
|| (value is TextDataFeed
|| value is XmlDataFeed
), "Value must be a TextReader or XmlReader");
10498 Debug
.Assert(isDataFeed
|| (value is string || value is byte[]), "Value is a byte array or string");
10501 Debug
.Assert(type
.IsPlp
, "Stream assigned to non-PLP was not converted!");
10502 TextDataFeed tdf
= value as TextDataFeed
;
10504 return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed
)value, stateObj
, needBom
:true, encoding
:_defaultEncoding
, size
:paramSize
));
10507 return NullIfCompletedWriteTask(WriteTextFeed(tdf
, _defaultEncoding
, false, stateObj
, paramSize
));
10512 WriteInt(encodingByteSize
, stateObj
); // chunk length
10514 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10515 return stateObj
.WriteByteArray((byte[])value, actualLength
, 0, canAccumulate
: false);
10518 return WriteEncodingChar((string)value, actualLength
, offset
, _defaultEncoding
, stateObj
, canAccumulate
: false);
10522 case TdsEnums
.SQLNCHAR
:
10523 case TdsEnums
.SQLNVARCHAR
:
10524 case TdsEnums
.SQLNTEXT
:
10525 case TdsEnums
.SQLXMLTYPE
: {
10526 Debug
.Assert(!isDataFeed
|| (value is TextDataFeed
|| value is XmlDataFeed
), "Value must be a TextReader or XmlReader");
10527 Debug
.Assert(isDataFeed
|| (value is string || value is byte[]), "Value is a byte array or string");
10530 Debug
.Assert(type
.IsPlp
, "Stream assigned to non-PLP was not converted!");
10531 TextDataFeed tdf
= value as TextDataFeed
;
10533 return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed
)value, stateObj
, IsBOMNeeded(type
, value), Encoding
.Unicode
, paramSize
));
10536 return NullIfCompletedWriteTask(WriteTextFeed(tdf
, null, IsBOMNeeded(type
, value), stateObj
, paramSize
));
10541 if (IsBOMNeeded(type
, value)) {
10542 WriteInt(actualLength
+ 2, stateObj
); // chunk length
10543 WriteShort(TdsEnums
.XMLUNICODEBOM
, stateObj
);
10546 WriteInt(actualLength
, stateObj
); // chunk length
10549 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10550 return stateObj
.WriteByteArray((byte[])value, actualLength
, 0, canAccumulate
: false);
10553 // convert to cchars instead of cbytes
10554 actualLength
>>= 1;
10555 return WriteString((string)value, actualLength
, offset
, stateObj
, canAccumulate
: false);
10559 case TdsEnums
.SQLNUMERICN
:
10560 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
10561 WriteDecimal((Decimal
)value, stateObj
);
10564 case TdsEnums
.SQLDATETIMN
:
10565 Debug
.Assert(type
.FixedLength
<= 0xff, "Invalid Fixed Length");
10567 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, (byte)type
.FixedLength
);
10569 if (type
.FixedLength
== 4) {
10570 if (0 > dt
.days
|| dt
.days
> UInt16
.MaxValue
)
10571 throw SQL
.SmallDateTimeOverflow(MetaType
.ToDateTime(dt
.days
, dt
.time
, 4).ToString(CultureInfo
.InvariantCulture
));
10573 WriteShort(dt
.days
, stateObj
);
10574 WriteShort(dt
.time
, stateObj
);
10577 WriteInt(dt
.days
, stateObj
);
10578 WriteInt(dt
.time
, stateObj
);
10583 case TdsEnums
.SQLMONEYN
: {
10584 WriteCurrency((Decimal
)value, type
.FixedLength
, stateObj
);
10588 case TdsEnums
.SQLDATE
: {
10589 WriteDate((DateTime
)value, stateObj
);
10593 case TdsEnums
.SQLTIME
:
10594 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10595 throw SQL
.TimeScaleValueOutOfRange(scale
);
10597 WriteTime((TimeSpan
)value, scale
, actualLength
, stateObj
);
10600 case TdsEnums
.SQLDATETIME2
:
10601 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10602 throw SQL
.TimeScaleValueOutOfRange(scale
);
10604 WriteDateTime2((DateTime
)value, scale
, actualLength
, stateObj
);
10607 case TdsEnums
.SQLDATETIMEOFFSET
:
10608 WriteDateTimeOffset((DateTimeOffset
)value, scale
, actualLength
, stateObj
);
10612 Debug
.Assert(false, "Unknown TdsType!" + type
.NullableType
.ToString("x2", (IFormatProvider
)null));
10615 // return point for accumualated writes, note: non-accumulated writes returned from their case statements
10617 // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture));
10621 /// Write parameter encryption metadata and returns a task if necessary.
10623 private Task
WriteEncryptionMetadata(Task terminatedWriteTask
, SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo
, TdsParserStateObject stateObj
) {
10624 Debug
.Assert(columnEncryptionParameterInfo
!= null, @"columnEncryptionParameterInfo cannot be null");
10625 Debug
.Assert(stateObj
!= null, @"stateObj cannot be null");
10627 // If there is not task already, simply write the encryption metadata synchronously.
10628 if (terminatedWriteTask
== null) {
10629 WriteEncryptionMetadata(columnEncryptionParameterInfo
, stateObj
);
10633 // Otherwise, create a continuation task to write the encryption metadata after the previous write completes.
10634 return AsyncHelper
.CreateContinuationTask
<SqlColumnEncryptionInputParameterInfo
, TdsParserStateObject
>(terminatedWriteTask
,
10635 WriteEncryptionMetadata
, columnEncryptionParameterInfo
, stateObj
,
10636 connectionToDoom: _connHandler
);
10641 /// Write parameter encryption metadata.
10643 private void WriteEncryptionMetadata(SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo
, TdsParserStateObject stateObj
) {
10644 Debug
.Assert(columnEncryptionParameterInfo
!= null, @"columnEncryptionParameterInfo cannot be null");
10645 Debug
.Assert(stateObj
!= null, @"stateObj cannot be null");
10647 // Write the TypeInfo.
10648 WriteSmiTypeInfo(columnEncryptionParameterInfo
.ParameterMetadata
, stateObj
);
10650 // Write the serialized array in columnEncryptionParameterInfo.
10651 stateObj
.WriteByteArray(columnEncryptionParameterInfo
.SerializedWireFormat
,
10652 columnEncryptionParameterInfo
.SerializedWireFormat
.Length
,
10656 // For MAX types, this method can only write everything in one big chunk. If multiple
10657 // chunk writes needed, please use WritePlpBytes/WritePlpChars
10658 private byte[] SerializeUnencryptedValue(object value, MetaType type
, byte scale
, int actualLength
, int offset
, bool isDataFeed
, byte normalizationVersion
, TdsParserStateObject stateObj
) {
10659 Debug
.Assert((null != value) && (DBNull
.Value
!= value), "unexpected missing or empty object");
10661 if (normalizationVersion
!= 0x01) {
10662 throw SQL
.UnsupportedNormalizationVersion(normalizationVersion
);
10665 // parameters are always sent over as BIG or N types
10666 switch (type
.NullableType
) {
10667 case TdsEnums
.SQLFLTN
:
10668 if (type
.FixedLength
== 4)
10669 return SerializeFloat((Single
)value);
10671 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
10672 return SerializeDouble((Double
)value);
10675 case TdsEnums
.SQLBIGBINARY
:
10676 case TdsEnums
.SQLBIGVARBINARY
:
10677 case TdsEnums
.SQLIMAGE
:
10678 case TdsEnums
.SQLUDT
: {
10679 Debug
.Assert(!isDataFeed
, "We cannot seriliaze streams");
10680 Debug
.Assert(value is byte[], "Value should be an array of bytes");
10682 byte[] b
= new byte[actualLength
];
10683 Buffer
.BlockCopy((byte[])value, offset
, b
, 0, actualLength
);
10687 case TdsEnums
.SQLUNIQUEID
: {
10688 System
.Guid guid
= (System
.Guid
)value;
10689 byte[] b
= guid
.ToByteArray();
10691 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
10695 case TdsEnums
.SQLBITN
: {
10696 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
10698 // We normalize to allow conversion across data types. BIT is serialized into a BIGINT.
10699 return SerializeLong((bool)value == true ? 1 : 0, stateObj
);
10702 case TdsEnums
.SQLINTN
:
10703 if (type
.FixedLength
== 1)
10704 return SerializeLong((byte)value, stateObj
);
10706 if (type
.FixedLength
== 2)
10707 return SerializeLong((Int16
)value, stateObj
);
10709 if (type
.FixedLength
== 4)
10710 return SerializeLong((Int32
)value, stateObj
);
10712 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
10713 return SerializeLong((Int64
)value, stateObj
);
10715 case TdsEnums
.SQLBIGCHAR
:
10716 case TdsEnums
.SQLBIGVARCHAR
:
10717 case TdsEnums
.SQLTEXT
: {
10718 Debug
.Assert(!isDataFeed
, "We cannot seriliaze streams");
10719 Debug
.Assert((value is string || value is byte[]), "Value is a byte array or string");
10721 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10722 byte[] b
= new byte[actualLength
];
10723 Buffer
.BlockCopy((byte[])value, 0, b
, 0, actualLength
);
10727 return SerializeEncodingChar((string)value, actualLength
, offset
, _defaultEncoding
);
10730 case TdsEnums
.SQLNCHAR
:
10731 case TdsEnums
.SQLNVARCHAR
:
10732 case TdsEnums
.SQLNTEXT
:
10733 case TdsEnums
.SQLXMLTYPE
: {
10734 Debug
.Assert(!isDataFeed
, "We cannot seriliaze streams");
10735 Debug
.Assert((value is string || value is byte[]), "Value is a byte array or string");
10737 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10738 byte[] b
= new byte[actualLength
];
10739 Buffer
.BlockCopy((byte[])value, 0, b
, 0, actualLength
);
10742 else { // convert to cchars instead of cbytes
10743 actualLength
>>= 1;
10744 return SerializeString((string)value, actualLength
, offset
);
10747 case TdsEnums
.SQLNUMERICN
:
10748 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
10749 return SerializeDecimal((Decimal
)value, stateObj
);
10751 case TdsEnums
.SQLDATETIMN
:
10752 Debug
.Assert(type
.FixedLength
<= 0xff, "Invalid Fixed Length");
10754 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, (byte)type
.FixedLength
);
10756 if (type
.FixedLength
== 4) {
10757 if (0 > dt
.days
|| dt
.days
> UInt16
.MaxValue
)
10758 throw SQL
.SmallDateTimeOverflow(MetaType
.ToDateTime(dt
.days
, dt
.time
, 4).ToString(CultureInfo
.InvariantCulture
));
10760 if (null == stateObj
._bIntBytes
) {
10761 stateObj
._bIntBytes
= new byte[4];
10764 byte[] b
= stateObj
._bIntBytes
;
10767 byte[] bPart
= SerializeShort(dt
.days
, stateObj
);
10768 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10771 bPart
= SerializeShort(dt
.time
, stateObj
);
10772 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10777 if (null == stateObj
._bLongBytes
) {
10778 stateObj
._bLongBytes
= new byte[8];
10780 byte[] b
= stateObj
._bLongBytes
;
10783 byte[] bPart
= SerializeInt(dt
.days
, stateObj
);
10784 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10787 bPart
= SerializeInt(dt
.time
, stateObj
);
10788 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10793 case TdsEnums
.SQLMONEYN
: {
10794 return SerializeCurrency((Decimal
)value, type
.FixedLength
, stateObj
);
10797 case TdsEnums
.SQLDATE
: {
10798 return SerializeDate((DateTime
)value);
10801 case TdsEnums
.SQLTIME
:
10802 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10803 throw SQL
.TimeScaleValueOutOfRange(scale
);
10805 return SerializeTime((TimeSpan
)value, scale
, actualLength
);
10807 case TdsEnums
.SQLDATETIME2
:
10808 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10809 throw SQL
.TimeScaleValueOutOfRange(scale
);
10811 return SerializeDateTime2((DateTime
)value, scale
, actualLength
);
10813 case TdsEnums
.SQLDATETIMEOFFSET
:
10814 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10815 throw SQL
.TimeScaleValueOutOfRange(scale
);
10817 return SerializeDateTimeOffset((DateTimeOffset
)value, scale
, actualLength
);
10820 throw SQL
.UnsupportedDatatypeEncryption(type
.TypeName
);
10822 // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture));
10825 // For MAX types, this method can only write everything in one big chunk. If multiple
10826 // chunk writes needed, please use WritePlpBytes/WritePlpChars
10827 private byte[] SerializeUnencryptedSqlValue(object value, MetaType type
, int actualLength
, int offset
, byte normalizationVersion
, TdsParserStateObject stateObj
) {
10828 Debug
.Assert(((type
.NullableType
== TdsEnums
.SQLXMLTYPE
) ||
10829 (value is INullable
&& !((INullable
)value).IsNull
)),
10830 "unexpected null SqlType!");
10832 if (normalizationVersion
!= 0x01) {
10833 throw SQL
.UnsupportedNormalizationVersion(normalizationVersion
);
10836 // parameters are always sent over as BIG or N types
10837 switch (type
.NullableType
) {
10838 case TdsEnums
.SQLFLTN
:
10839 if (type
.FixedLength
== 4)
10840 return SerializeFloat(((SqlSingle
)value).Value
);
10842 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
10843 return SerializeDouble(((SqlDouble
)value).Value
);
10846 case TdsEnums
.SQLBIGBINARY
:
10847 case TdsEnums
.SQLBIGVARBINARY
:
10848 case TdsEnums
.SQLIMAGE
: {
10849 byte[] b
= new byte[actualLength
];
10851 if (value is SqlBinary
) {
10852 Buffer
.BlockCopy(((SqlBinary
)value).Value
, offset
, b
, 0, actualLength
);
10855 Debug
.Assert(value is SqlBytes
);
10856 Buffer
.BlockCopy(((SqlBytes
)value).Value
, offset
, b
, 0, actualLength
);
10861 case TdsEnums
.SQLUNIQUEID
: {
10862 byte[] b
= ((SqlGuid
)value).ToByteArray();
10864 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
10868 case TdsEnums
.SQLBITN
: {
10869 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
10871 // We normalize to allow conversion across data types. BIT is serialized into a BIGINT.
10872 return SerializeLong(((SqlBoolean
)value).Value
== true ? 1 : 0, stateObj
);
10875 case TdsEnums
.SQLINTN
:
10876 // We normalize to allow conversion across data types. All data types below are serialized into a BIGINT.
10877 if (type
.FixedLength
== 1)
10878 return SerializeLong(((SqlByte
)value).Value
, stateObj
);
10880 if (type
.FixedLength
== 2)
10881 return SerializeLong(((SqlInt16
)value).Value
, stateObj
);
10883 if (type
.FixedLength
== 4)
10884 return SerializeLong(((SqlInt32
)value).Value
, stateObj
);
10886 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
10887 return SerializeLong(((SqlInt64
)value).Value
, stateObj
);
10890 case TdsEnums
.SQLBIGCHAR
:
10891 case TdsEnums
.SQLBIGVARCHAR
:
10892 case TdsEnums
.SQLTEXT
:
10893 if (value is SqlChars
) {
10894 String sch
= new String(((SqlChars
)value).Value
);
10895 return SerializeEncodingChar(sch
, actualLength
, offset
, _defaultEncoding
);
10898 Debug
.Assert(value is SqlString
);
10899 return SerializeEncodingChar(((SqlString
)value).Value
, actualLength
, offset
, _defaultEncoding
);
10903 case TdsEnums
.SQLNCHAR
:
10904 case TdsEnums
.SQLNVARCHAR
:
10905 case TdsEnums
.SQLNTEXT
:
10906 case TdsEnums
.SQLXMLTYPE
:
10907 // convert to cchars instead of cbytes
10908 // Xml type is already converted to string through GetCoercedValue
10909 if (actualLength
!= 0)
10910 actualLength
>>= 1;
10912 if (value is SqlChars
) {
10913 return SerializeCharArray(((SqlChars
)value).Value
, actualLength
, offset
);
10916 Debug
.Assert(value is SqlString
);
10917 return SerializeString(((SqlString
)value).Value
, actualLength
, offset
);
10920 case TdsEnums
.SQLNUMERICN
:
10921 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
10922 return SerializeSqlDecimal((SqlDecimal
)value, stateObj
);
10924 case TdsEnums
.SQLDATETIMN
:
10925 SqlDateTime dt
= (SqlDateTime
)value;
10927 if (type
.FixedLength
== 4) {
10928 if (0 > dt
.DayTicks
|| dt
.DayTicks
> UInt16
.MaxValue
)
10929 throw SQL
.SmallDateTimeOverflow(dt
.ToString());
10931 if (null == stateObj
._bIntBytes
) {
10932 stateObj
._bIntBytes
= new byte[4];
10935 byte[] b
= stateObj
._bIntBytes
;
10938 byte[] bPart
= SerializeShort(dt
.DayTicks
, stateObj
);
10939 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10942 bPart
= SerializeShort(dt
.TimeTicks
/ SqlDateTime
.SQLTicksPerMinute
, stateObj
);
10943 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10948 if (null == stateObj
._bLongBytes
) {
10949 stateObj
._bLongBytes
= new byte[8];
10952 byte[] b
= stateObj
._bLongBytes
;
10955 byte[] bPart
= SerializeInt(dt
.DayTicks
, stateObj
);
10956 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10959 bPart
= SerializeInt(dt
.TimeTicks
, stateObj
);
10960 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10965 case TdsEnums
.SQLMONEYN
: {
10966 return SerializeSqlMoney((SqlMoney
)value, type
.FixedLength
, stateObj
);
10970 throw SQL
.UnsupportedDatatypeEncryption(type
.TypeName
);
10975 // we always send over nullable types for parameters so we always write the varlen fields
10978 internal void WriteParameterVarLen(MetaType type
, int size
, bool isNull
, TdsParserStateObject stateObj
, bool unknownLength
=false) {
10979 if (type
.IsLong
) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths
10982 WriteLong(unchecked((long)TdsEnums
.SQL_PLP_NULL
), stateObj
);
10985 WriteInt(unchecked((int)TdsEnums
.VARLONGNULL
), stateObj
);
10988 else if (type
.NullableType
== TdsEnums
.SQLXMLTYPE
|| unknownLength
) {
10989 WriteUnsignedLong(TdsEnums
.SQL_PLP_UNKNOWNLEN
, stateObj
);
10991 else if (type
.IsPlp
) {
10992 // Non-xml plp types
10993 WriteLong((long)size
, stateObj
);
10996 WriteInt(size
, stateObj
);
10999 else if (type
.IsVarTime
) {
11001 stateObj
.WriteByte(TdsEnums
.FIXEDNULL
);
11004 stateObj
.WriteByte((byte)size
);
11007 else if (false == type
.IsFixed
) { // non-long but variable length column, must be a BIG* type: 2 byte length
11009 WriteShort(TdsEnums
.VARNULL
, stateObj
);
11012 WriteShort(size
, stateObj
);
11017 stateObj
.WriteByte(TdsEnums
.FIXEDNULL
);
11020 Debug
.Assert(type
.FixedLength
<= 0xff, "WriteParameterVarLen: invalid one byte length!");
11021 stateObj
.WriteByte((byte)(type
.FixedLength
& 0xff)); // 1 byte for everything else
11026 // Reads the next chunk in a nvarchar(max) data stream.
11027 // This call must be preceeded by a call to ReadPlpLength or ReadDataLength.
11028 // Will not start reading into the next chunk if bytes requested is larger than
11029 // the current chunk length. Do another ReadPlpLength, ReadPlpUnicodeChars in that case.
11030 // Returns the actual chars read
11031 private bool TryReadPlpUnicodeCharsChunk(char[] buff
, int offst
, int len
, TdsParserStateObject stateObj
, out int charsRead
) {
11033 Debug
.Assert((buff
== null && len
== 0) || (buff
.Length
>= offst
+ len
), "Invalid length sent to ReadPlpUnicodeChars()!");
11034 Debug
.Assert((stateObj
._longlen
!= 0) && (stateObj
._longlen
!= TdsEnums
.SQL_PLP_NULL
),
11035 "Out of sync plp read request");
11036 if (stateObj
._longlenleft
== 0) {
11037 Debug
.Assert(false, "Out of sync read request");
11044 // stateObj._longlenleft is in bytes
11045 if ((stateObj
._longlenleft
>> 1) < (ulong)len
)
11046 charsRead
= (int)(stateObj
._longlenleft
>> 1);
11048 for (int ii
= 0; ii
< charsRead
; ii
++) {
11049 if (!stateObj
.TryReadChar(out buff
[offst
+ ii
])) {
11054 stateObj
._longlenleft
-= ((ulong)charsRead
<< 1);
11058 internal int ReadPlpUnicodeChars(ref char[] buff
, int offst
, int len
, TdsParserStateObject stateObj
) {
11060 Debug
.Assert(stateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
11061 bool result
= TryReadPlpUnicodeChars(ref buff
, offst
, len
, stateObj
, out charsRead
);
11062 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
11066 // Reads the requested number of chars from a plp data stream, or the entire data if
11067 // requested length is -1 or larger than the actual length of data. First call to this method
11068 // should be preceeded by a call to ReadPlpLength or ReadDataLength.
11069 // Returns the actual chars read.
11070 internal bool TryReadPlpUnicodeChars(ref char[] buff
, int offst
, int len
, TdsParserStateObject stateObj
, out int totalCharsRead
) {
11075 if (stateObj
._longlen
== 0) {
11076 Debug
.Assert(stateObj
._longlenleft
== 0);
11077 totalCharsRead
= 0;
11078 return true; // No data
11081 Debug
.Assert(((ulong)stateObj
._longlen
!= TdsEnums
.SQL_PLP_NULL
),
11082 "Out of sync plp read request");
11084 Debug
.Assert((buff
== null && offst
== 0) || (buff
.Length
>= offst
+ len
), "Invalid length sent to ReadPlpUnicodeChars()!");
11087 // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time
11088 if (buff
== null && stateObj
._longlen
!= TdsEnums
.SQL_PLP_UNKNOWNLEN
) {
11089 buff
= new char[(int)Math
.Min((int)stateObj
._longlen
, len
)];
11092 if (stateObj
._longlenleft
== 0) {
11094 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11095 totalCharsRead
= 0;
11098 if (stateObj
._longlenleft
== 0) { // Data read complete
11099 totalCharsRead
= 0;
11104 totalCharsRead
= 0;
11106 while (charsLeft
> 0) {
11107 charsRead
= (int)Math
.Min((stateObj
._longlenleft
+ 1) >> 1, (ulong)charsLeft
);
11108 if ((buff
== null) || (buff
.Length
< (offst
+ charsRead
))) {
11110 newbuf
= new char[offst
+ charsRead
];
11111 if (buff
!= null) {
11112 Buffer
.BlockCopy(buff
, 0, newbuf
, 0, offst
*2);
11116 if (charsRead
> 0) {
11117 if (!TryReadPlpUnicodeCharsChunk(buff
, offst
, charsRead
, stateObj
, out charsRead
)) {
11120 charsLeft
-= charsRead
;
11121 offst
+= charsRead
;
11122 totalCharsRead
+= charsRead
;
11124 // Special case single byte left
11125 if (stateObj
._longlenleft
== 1 && (charsLeft
> 0)) {
11127 if (!stateObj
.TryReadByte(out b1
)) {
11130 stateObj
._longlenleft
--;
11132 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11135 Debug
.Assert((stateObj
._longlenleft
!= 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
11137 if (!stateObj
.TryReadByte(out b2
)) {
11140 stateObj
._longlenleft
--;
11141 // Put it at the end of the array. At this point we know we have an extra byte.
11142 buff
[offst
] = (char)(((b2
& 0xff) << 8) + (b1
& 0xff));
11143 offst
= checked((int)offst
+ 1);
11148 if (stateObj
._longlenleft
== 0) { // Read the next chunk or cleanup state if hit the end
11150 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11155 if (stateObj
._longlenleft
== 0) // Data read complete
11161 internal int ReadPlpAnsiChars(ref char[] buff
, int offst
, int len
, SqlMetaDataPriv metadata
, TdsParserStateObject stateObj
) {
11165 int totalcharsRead
= 0;
11167 if (stateObj
._longlen
== 0) {
11168 Debug
.Assert(stateObj
._longlenleft
== 0);
11169 return 0; // No data
11172 Debug
.Assert(((ulong)stateObj
._longlen
!= TdsEnums
.SQL_PLP_NULL
),
11173 "Out of sync plp read request");
11175 Debug
.Assert((buff
== null && offst
== 0) || (buff
.Length
>= offst
+ len
), "Invalid length sent to ReadPlpAnsiChars()!");
11178 if (stateObj
._longlenleft
== 0) {
11179 stateObj
.ReadPlpLength(false);
11180 if (stateObj
._longlenleft
== 0) {// Data read complete
11181 stateObj
._plpdecoder
= null;
11186 if (stateObj
._plpdecoder
== null) {
11187 Encoding enc
= metadata
.encoding
;
11191 if (null == _defaultEncoding
) {
11192 ThrowUnsupportedCollationEncountered(stateObj
);
11196 enc
= _defaultEncoding
;
11198 stateObj
._plpdecoder
= enc
.GetDecoder();
11201 while (charsLeft
> 0) {
11202 bytesRead
= (int)Math
.Min(stateObj
._longlenleft
, (ulong)charsLeft
);
11203 if ((stateObj
._bTmp
== null) || (stateObj
._bTmp
.Length
< bytesRead
)) {
11205 stateObj
._bTmp
= new byte[bytesRead
];
11208 bytesRead
= stateObj
.ReadPlpBytesChunk(stateObj
._bTmp
, 0, bytesRead
);
11210 charsRead
= stateObj
._plpdecoder
.GetChars(stateObj
._bTmp
, 0, bytesRead
, buff
, offst
);
11211 charsLeft
-= charsRead
;
11212 offst
+= charsRead
;
11213 totalcharsRead
+= charsRead
;
11214 if (stateObj
._longlenleft
== 0) // Read the next chunk or cleanup state if hit the end
11215 stateObj
.ReadPlpLength(false);
11217 if (stateObj
._longlenleft
== 0) { // Data read complete
11218 stateObj
._plpdecoder
= null;
11222 return (totalcharsRead
);
11225 // ensure value is not null and does not have an NBC bit set for it before using this method
11226 internal ulong SkipPlpValue(ulong cb
, TdsParserStateObject stateObj
) {
11228 Debug
.Assert(stateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
11229 bool result
= TrySkipPlpValue(cb
, stateObj
, out skipped
);
11230 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
11234 internal bool TrySkipPlpValue(ulong cb
, TdsParserStateObject stateObj
, out ulong totalBytesSkipped
) {
11235 // Read and skip cb bytes or until ReadPlpLength returns 0.
11237 totalBytesSkipped
= 0;
11239 if (stateObj
._longlenleft
== 0) {
11241 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11246 while ((totalBytesSkipped
< cb
) &&
11247 (stateObj
._longlenleft
> 0)) {
11248 if (stateObj
._longlenleft
> Int32
.MaxValue
)
11249 bytesSkipped
= Int32
.MaxValue
;
11251 bytesSkipped
= (int)stateObj
._longlenleft
;
11252 bytesSkipped
= ((cb
- totalBytesSkipped
) < (ulong) bytesSkipped
) ? (int)(cb
- totalBytesSkipped
) : bytesSkipped
;
11254 if (!stateObj
.TrySkipBytes(bytesSkipped
)) {
11257 stateObj
._longlenleft
-= (ulong)bytesSkipped
;
11258 totalBytesSkipped
+= (ulong)bytesSkipped
;
11260 if (stateObj
._longlenleft
== 0) {
11262 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11271 internal ulong PlpBytesLeft(TdsParserStateObject stateObj
) {
11272 if ((stateObj
._longlen
!= 0) && (stateObj
._longlenleft
== 0))
11273 stateObj
.ReadPlpLength(false);
11275 return stateObj
._longlenleft
;
11278 internal bool TryPlpBytesLeft(TdsParserStateObject stateObj
, out ulong left
) {
11279 if ((stateObj
._longlen
!= 0) && (stateObj
._longlenleft
== 0)) {
11280 if (!stateObj
.TryReadPlpLength(false, out left
)) {
11285 left
= stateObj
._longlenleft
;
11289 private const ulong _indeterminateSize
= 0xffffffffffffffff; // Represents unknown size
11291 internal ulong PlpBytesTotalLength(TdsParserStateObject stateObj
) {
11292 if (stateObj
._longlen
== TdsEnums
.SQL_PLP_UNKNOWNLEN
)
11293 return _indeterminateSize
;
11294 else if (stateObj
._longlen
== TdsEnums
.SQL_PLP_NULL
)
11297 return stateObj
._longlen
;
11300 const string StateTraceFormatString
= "\n\t"
11301 + " _physicalStateObj = {0}\n\t"
11302 + " _pMarsPhysicalConObj = {1}\n\t"
11303 + " _state = {2}\n\t"
11304 + " _server = {3}\n\t"
11305 + " _fResetConnection = {4}\n\t"
11306 + " _defaultCollation = {5}\n\t"
11307 + " _defaultCodePage = {6}\n\t"
11308 + " _defaultLCID = {7}\n\t"
11309 + " _defaultEncoding = {8}\n\t"
11310 + " _encryptionOption = {10}\n\t"
11311 + " _currentTransaction = {11}\n\t"
11312 + " _pendingTransaction = {12}\n\t"
11313 + " _retainedTransactionId = {13}\n\t"
11314 + " _nonTransactedOpenResultCount = {14}\n\t"
11315 + " _connHandler = {15}\n\t"
11316 + " _fMARS = {16}\n\t"
11317 + " _sessionPool = {17}\n\t"
11318 + " _isShiloh = {18}\n\t"
11319 + " _isShilohSP1 = {19}\n\t"
11320 + " _isYukon = {20}\n\t"
11321 + " _sniSpnBuffer = {21}\n\t"
11322 + " _errors = {22}\n\t"
11323 + " _warnings = {23}\n\t"
11324 + " _attentionErrors = {24}\n\t"
11325 + " _attentionWarnings = {25}\n\t"
11326 + " _statistics = {26}\n\t"
11327 + " _statisticsIsInTransaction = {27}\n\t"
11328 + " _fPreserveTransaction = {28}"
11329 + " _fParallel = {29}"
11331 internal string TraceString() {
11332 return String
.Format(/*IFormatProvider*/ null,
11333 StateTraceFormatString
,
11334 null == _physicalStateObj
,
11335 null == _pMarsPhysicalConObj
,
11339 null == _defaultCollation
? "(null)" : _defaultCollation
.TraceString(),
11342 TraceObjectClass(_defaultEncoding
),
11345 null == _currentTransaction
? "(null)" : _currentTransaction
.TraceString(),
11346 null == _pendingTransaction
? "(null)" : _pendingTransaction
.TraceString(),
11347 _retainedTransactionId
,
11348 _nonTransactedOpenResultCount
,
11349 null == _connHandler
? "(null)" : _connHandler
.ObjectID
.ToString((IFormatProvider
)null),
11351 null == _sessionPool
? "(null)" : _sessionPool
.TraceString(),
11355 null == _sniSpnBuffer
? "(null)" : _sniSpnBuffer
.Length
.ToString((IFormatProvider
)null),
11356 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.ErrorCount
.ToString((IFormatProvider
)null),
11357 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.WarningCount
.ToString((IFormatProvider
)null),
11358 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.PreAttentionErrorCount
.ToString((IFormatProvider
)null),
11359 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.PreAttentionWarningCount
.ToString((IFormatProvider
)null),
11360 null == _statistics
,
11361 _statisticsIsInTransaction
,
11362 _fPreserveTransaction
,
11363 null == _connHandler
? "(null)" : _connHandler
.ConnectionOptions
.MultiSubnetFailover
.ToString((IFormatProvider
)null),
11364 null == _connHandler
? "(null)" : _connHandler
.ConnectionOptions
.TransparentNetworkIPResolution
.ToString((IFormatProvider
)null));
11367 private string TraceObjectClass(object instance
) {
11368 if (null == instance
) {
11372 return instance
.GetType().ToString();