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;
861 Debug
.Assert(_physicalStateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
862 bool result
= _physicalStateObj
.TryReadNetworkPacket();
863 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
865 if (_physicalStateObj
._inBytesRead
== 0) {
866 // If the server did not respond then something has gone wrong and we need to close the connection
867 _physicalStateObj
.AddError(new SqlError(0, (byte)0x00, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.PreloginError(), "", 0));
868 _physicalStateObj
.Dispose();
869 ThrowExceptionAndWarning(_physicalStateObj
);
873 byte[] payload
= new byte[_physicalStateObj
._inBytesRead
- _physicalStateObj
._inBytesUsed
- _physicalStateObj
._inputHeaderLen
];
875 Debug
.Assert(_physicalStateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
876 result
= _physicalStateObj
.TryReadByteArray(payload
, 0, payload
.Length
);
877 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
879 if (payload
[0] == 0xaa) {
880 // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which
881 // is not supported. SQL BU DT 296425
882 throw SQL
.InvalidSQLServerVersionUnknown();
886 int payloadOffset
= 0;
887 int payloadLength
= 0;
888 int option
= payload
[offset
++];
890 while (option
!= (byte)PreLoginOptions
.LASTOPT
) {
892 case (int)PreLoginOptions
.VERSION
:
893 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
894 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
896 byte majorVersion
= payload
[payloadOffset
];
897 byte minorVersion
= payload
[payloadOffset
+ 1];
898 int level
= (payload
[payloadOffset
+ 2] << 8) |
899 payload
[payloadOffset
+ 3];
901 isYukonOrLater
= majorVersion
>= 9;
902 if (!isYukonOrLater
) {
903 marsCapable
= false; // If pre-Yukon, MARS not supported.
908 case (int)PreLoginOptions
.ENCRYPT
:
909 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
910 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
912 EncryptionOptions serverOption
= (EncryptionOptions
)payload
[payloadOffset
];
914 /* internal enum EncryptionOptions {
922 switch (_encryptionOption
) {
923 case (EncryptionOptions
.ON
):
924 if (serverOption
== EncryptionOptions
.NOT_SUP
) {
925 _physicalStateObj
.AddError(new SqlError(TdsEnums
.ENCRYPTION_NOT_SUPPORTED
, (byte)0x00, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.EncryptionNotSupportedByServer(), "", 0));
926 _physicalStateObj
.Dispose();
927 ThrowExceptionAndWarning(_physicalStateObj
);
932 case (EncryptionOptions
.OFF
):
933 if (serverOption
== EncryptionOptions
.OFF
) {
934 // Only encrypt login.
935 _encryptionOption
= EncryptionOptions
.LOGIN
;
937 else if (serverOption
== EncryptionOptions
.REQ
) {
939 _encryptionOption
= EncryptionOptions
.ON
;
944 case (EncryptionOptions
.NOT_SUP
):
945 if (serverOption
== EncryptionOptions
.REQ
) {
946 _physicalStateObj
.AddError(new SqlError(TdsEnums
.ENCRYPTION_NOT_SUPPORTED
, (byte)0x00, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.EncryptionNotSupportedByClient(), "", 0));
947 _physicalStateObj
.Dispose();
948 ThrowExceptionAndWarning(_physicalStateObj
);
954 Debug
.Assert(false, "Invalid client encryption option detected");
958 if (_encryptionOption
== EncryptionOptions
.ON
||
959 _encryptionOption
== EncryptionOptions
.LOGIN
) {
962 // If we're using legacy server certificate validation behavior (Authentication keyword not provided and not using access token), then validate if
963 // Encrypt=true and Trust Sever Certificate = false.
964 // If using Authentication keyword or access token, validate if Trust Server Certificate=false.
965 bool shouldValidateServerCert
= (encrypt
&& !trustServerCert
) || ((authType
!= SqlAuthenticationMethod
.NotSpecified
|| _connHandler
._accessTokenInBytes
!= null) && !trustServerCert
);
967 UInt32 info
= (shouldValidateServerCert
? TdsEnums
.SNI_SSL_VALIDATE_CERTIFICATE
: 0)
968 | (isYukonOrLater
? TdsEnums
.SNI_SSL_USE_SCHANNEL_CACHE
: 0);
970 if (encrypt
&& !integratedSecurity
) {
971 // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI
972 // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context.
973 info
|= TdsEnums
.SNI_SSL_IGNORE_CHANNEL_BINDINGS
;
976 // Add SSL (Encryption) SNI provider.
977 error
= SNINativeMethodWrapper
.SNIAddProvider(_physicalStateObj
.Handle
, SNINativeMethodWrapper
.ProviderEnum
.SSL_PROV
, ref info
);
979 if (error
!= TdsEnums
.SNI_SUCCESS
) {
980 _physicalStateObj
.AddError(ProcessSNIError(_physicalStateObj
));
981 ThrowExceptionAndWarning(_physicalStateObj
);
984 // in the case where an async connection is made, encryption is used and Windows Authentication is used,
985 // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its
986 // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete
987 // before calling SNISecGenClientContext).
988 error
= SNINativeMethodWrapper
.SNIWaitForSSLHandshakeToComplete(_physicalStateObj
.Handle
, _physicalStateObj
.GetTimeoutRemaining());
989 if (error
!= TdsEnums
.SNI_SUCCESS
)
991 _physicalStateObj
.AddError(ProcessSNIError(_physicalStateObj
));
992 ThrowExceptionAndWarning(_physicalStateObj
);
995 // create a new packet encryption changes the internal packet size Bug# 228403
996 try {} // EmptyTry/Finally to avoid FXCop violation
998 _physicalStateObj
.ClearAllWritePackets();
1004 case (int)PreLoginOptions
.INSTANCE
:
1005 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
1006 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
1008 byte ERROR_INST
= 0x1;
1009 byte instanceResult
= payload
[payloadOffset
];
1011 if (instanceResult
== ERROR_INST
) {
1012 // Check if server says ERROR_INST. That either means the cached info
1013 // we used to connect is not valid or we connected to a named instance
1014 // listening on default params.
1015 return PreLoginHandshakeStatus
.InstanceFailure
;
1020 case (int)PreLoginOptions
.THREADID
:
1021 // DO NOTHING FOR THREADID
1025 case (int)PreLoginOptions
.MARS
:
1026 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
1027 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
1029 marsCapable
= (payload
[payloadOffset
] == 0 ? false : true);
1031 Debug
.Assert(payload
[payloadOffset
] == 0 || payload
[payloadOffset
] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
1034 case (int)PreLoginOptions
.TRACEID
:
1035 // DO NOTHING FOR TRACEID
1039 case (int)PreLoginOptions
.FEDAUTHREQUIRED
:
1040 payloadOffset
= payload
[offset
++] << 8 | payload
[offset
++];
1041 payloadLength
= payload
[offset
++] << 8 | payload
[offset
++];
1043 // Only 0x00 and 0x01 are accepted values from the server.
1044 if (payload
[payloadOffset
] != 0x00 && payload
[payloadOffset
] != 0x01) {
1045 Bid
.Trace("<sc.TdsParser.ConsumePreLoginHandshake|ERR> %d#, Server sent an unexpected value for FedAuthRequired PreLogin Option. Value was %d.\n", ObjectID
, (int)payload
[payloadOffset
]);
1046 throw SQL
.ParsingErrorValue(ParsingErrorState
.FedAuthRequiredPreLoginResponseInvalidValue
, (int)payload
[payloadOffset
]);
1049 // We must NOT use the response for the FEDAUTHREQUIRED PreLogin option, if the connection string option
1050 // was not using the new Authentication keyword or in other words, if Authentication=NotSpecified
1051 // Or AccessToken is not null, mean token based authentication is used.
1052 if ((_connHandler
.ConnectionOptions
!= null
1053 && _connHandler
.ConnectionOptions
.Authentication
!= SqlAuthenticationMethod
.NotSpecified
)
1054 || _connHandler
._accessTokenInBytes
!= null)
1056 fedAuthRequired
= payload
[payloadOffset
] == 0x01 ? true : false;
1061 Debug
.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option
);
1063 // DO NOTHING FOR THESE UNKNOWN OPTIONS
1069 if (offset
< payload
.Length
) {
1070 option
= payload
[offset
++];
1077 return PreLoginHandshakeStatus
.Successful
;
1080 internal void Deactivate(bool connectionIsDoomed
) {
1081 // Called when the connection that owns us is deactivated.
1083 if (Bid
.AdvancedOn
) {
1084 Bid
.Trace("<sc.TdsParser.Deactivate|ADV> %d# deactivating\n", ObjectID
);
1087 if (Bid
.IsOn(Bid
.ApiGroup
.StateDump
)) {
1088 Bid
.Trace("<sc.TdsParser.Deactivate|STATE> %d#, %ls\n", ObjectID
, TraceString());
1092 _sessionPool
.Deactivate();
1095 Debug
.Assert(connectionIsDoomed
|| null == _pendingTransaction
, "pending transaction at disconnect?");
1097 if (!connectionIsDoomed
&& null != _physicalStateObj
) {
1098 if (_physicalStateObj
._pendingData
) {
1099 DrainData(_physicalStateObj
);
1102 if (_physicalStateObj
.HasOpenResult
) { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
1103 _physicalStateObj
.DecrementOpenResultCount();
1107 // Any active, non-distributed transaction must be rolled back. We
1108 // need to wait for distributed transactions to be completed by the
1109 // transaction manager -- we don't want to automatically roll them
1112 // Note that when there is a transaction delegated to this connection,
1113 // we will defer the deactivation of this connection until the
1114 // transaction manager completes the transaction.
1115 SqlInternalTransaction currentTransaction
= CurrentTransaction
;
1117 if (null != currentTransaction
&& currentTransaction
.HasParentTransaction
) {
1118 currentTransaction
.CloseFromConnection();
1119 Debug
.Assert(null == CurrentTransaction
, "rollback didn't clear current transaction?");
1122 Statistics
= null; // must come after CleanWire or we won't count the stuff that happens there...
1125 // Used to close the connection and then free the memory allocated for the netlib connection.
1126 internal void Disconnect() {
1127 if (null != _sessionPool
) {
1128 // MARSOn may be true, but _sessionPool not yet created
1129 _sessionPool
.Dispose();
1132 // Can close the connection if its open or broken
1133 if (_state
!= TdsParserState
.Closed
) {
1134 //benign assert - the user could close the connection before consuming all the data
1135 //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
1137 _state
= TdsParserState
.Closed
;
1140 // If the _physicalStateObj has an owner, we will delay the disposal until the owner is finished with it
1141 if (!_physicalStateObj
.HasOwner
) {
1142 _physicalStateObj
.SniContext
= SniContext
.Snix_Close
;
1144 _physicalStateObj
.InvalidateDebugOnlyCopyOfSniContext();
1146 _physicalStateObj
.Dispose();
1149 // Remove the "initial" callback (this will allow the stateObj to be GC collected if need be)
1150 _physicalStateObj
.DecrementPendingCallbacks(false);
1153 // Not allocated until MARS is actually enabled in SNI.
1154 if (null != _pMarsPhysicalConObj
) {
1155 _pMarsPhysicalConObj
.Dispose();
1159 _pMarsPhysicalConObj
= null;
1164 // Fires a single InfoMessageEvent
1165 private void FireInfoMessageEvent(SqlConnection connection
, TdsParserStateObject stateObj
, SqlError error
) {
1167 string serverVersion
= null;
1169 Debug
.Assert(connection
!= null && _connHandler
.Connection
== connection
);
1171 if (_state
== TdsParserState
.OpenLoggedIn
) {
1172 serverVersion
= _connHandler
.ServerVersion
;
1175 SqlErrorCollection sqlErs
= new SqlErrorCollection();
1179 SqlException exc
= SqlException
.CreateException(sqlErs
, serverVersion
, _connHandler
);
1182 connection
.OnInfoMessage(new SqlInfoMessageEventArgs(exc
), out notified
);
1184 // observable side-effects, no retry
1185 stateObj
._syncOverAsync
= true;
1190 internal void DisconnectTransaction(SqlInternalTransaction internalTransaction
) {
1191 Debug
.Assert(_currentTransaction
!= null && _currentTransaction
== internalTransaction
, "disconnecting different transaction");
1193 if (_currentTransaction
!= null && _currentTransaction
== internalTransaction
) {
1194 _currentTransaction
= null;
1198 internal void RollbackOrphanedAPITransactions() {
1199 // Any active, non-distributed transaction must be rolled back.
1200 SqlInternalTransaction currentTransaction
= CurrentTransaction
;
1202 if (null != currentTransaction
&& currentTransaction
.HasParentTransaction
&& currentTransaction
.IsOrphaned
) {
1203 currentTransaction
.CloseFromConnection();
1204 Debug
.Assert(null == CurrentTransaction
, "rollback didn't clear current transaction?");
1208 internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj
, bool callerHasConnectionLock
= false, bool asyncClose
= false) {
1209 Debug
.Assert(!callerHasConnectionLock
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Caller claims to have lock, but connection lock is not taken");
1211 SqlException exception
= null;
1212 bool breakConnection
;
1214 // This function should only be called when there was an error or warning. If there aren't any
1215 // errors, the handler will be called for the warning(s). If there was an error, the warning(s) will
1216 // be copied to the end of the error collection so that the user may see all the errors and also the
1217 // warnings that occurred.
1219 SqlErrorCollection temp
= stateObj
.GetFullErrorAndWarningCollection(out breakConnection
);
1221 Debug
.Assert(temp
.Count
> 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
1222 if (temp
.Count
== 0)
1224 Bid
.Trace("<sc.TdsParser.ThrowExceptionAndWarning|ERR> Potential multi-threaded misuse of connection, unexpectedly empty warnings/errors under lock %d#\n", ObjectID
);
1226 Debug
.Assert(_connHandler
!= null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!");
1228 // Don't break the connection if it is already closed
1229 breakConnection
&= (TdsParserState
.Closed
!= _state
);
1230 if (breakConnection
) {
1231 if ((_state
== TdsParserState
.OpenNotLoggedIn
) && (_connHandler
.ConnectionOptions
.TransparentNetworkIPResolution
|| _connHandler
.ConnectionOptions
.MultiSubnetFailover
|| _loginWithFailover
) && (temp
.Count
== 1) && ((temp
[0].Number
== TdsEnums
.TIMEOUT_EXPIRED
) || (temp
[0].Number
== TdsEnums
.SNI_WAIT_TIMEOUT
))) {
1232 // DevDiv2 Bug 459546: With "MultiSubnetFailover=yes" in the Connection String, SQLClient incorrectly throws a Timeout using shorter time slice (3-4 seconds), not honoring the actual 'Connect Timeout'
1233 // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/459546
1234 // For Multisubnet Failover we slice the timeout to make reconnecting faster (with the assumption that the server will not failover instantaneously)
1235 // However, when timeout occurs we need to not doom the internal connection and also to mark the TdsParser as closed such that the login will be will retried
1236 breakConnection
= false;
1240 _state
= TdsParserState
.Broken
;
1244 Debug
.Assert(temp
!= null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection");
1245 if (temp
!= null && temp
.Count
> 0) {
1246 // Construct the exception now that we've collected all the errors
1247 string serverVersion
= null;
1248 if (_state
== TdsParserState
.OpenLoggedIn
) {
1249 serverVersion
= _connHandler
.ServerVersion
;
1251 exception
= SqlException
.CreateException(temp
, serverVersion
, _connHandler
);
1252 if (exception
.Procedure
== TdsEnums
.INIT_ADAL_PACKAGE
|| exception
.Procedure
== TdsEnums
.INIT_SSPI_PACKAGE
) {
1253 exception
._doNotReconnect
= true;
1257 // call OnError outside of _ErrorCollectionLock to avoid deadlock
1258 if (exception
!= null) {
1259 if (breakConnection
) {
1260 // report exception to pending async operation
1261 // before OnConnectionClosed overrides the exception
1262 // due to connection close notification through references
1263 var taskSource
= stateObj
._networkPacketTaskSource
;
1264 if (taskSource
!= null) {
1265 taskSource
.TrySetException(ADP
.ExceptionWithStackTrace(exception
));
1270 // Wait until we have the parser lock, then try to close
1271 var connHandler
= _connHandler
;
1272 Action
<Action
> wrapCloseAction
= closeAction
=> {
1273 Task
.Factory
.StartNew(() => {
1274 connHandler
._parserLock
.Wait(canReleaseFromAnyThread
: false);
1275 connHandler
.ThreadHasParserLockForClose
= true;
1280 connHandler
.ThreadHasParserLockForClose
= false;
1281 connHandler
._parserLock
.Release();
1286 _connHandler
.OnError(exception
, breakConnection
, wrapCloseAction
);
1289 // Let close know that we already have the _parserLock
1290 bool threadAlreadyHadParserLockForClose
= _connHandler
.ThreadHasParserLockForClose
;
1291 if (callerHasConnectionLock
) {
1292 _connHandler
.ThreadHasParserLockForClose
= true;
1295 // the following handler will throw an exception or generate a warning event
1296 _connHandler
.OnError(exception
, breakConnection
);
1299 if (callerHasConnectionLock
) {
1300 _connHandler
.ThreadHasParserLockForClose
= threadAlreadyHadParserLockForClose
;
1307 internal SqlError
ProcessSNIError(TdsParserStateObject stateObj
) {
1309 // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error
1310 Debug
.Assert(SniContext
.Undefined
!=stateObj
.DebugOnlyCopyOfSniContext
|| ((_fMARS
) && ((_state
== TdsParserState
.Closed
) || (_state
== TdsParserState
.Broken
))), "SniContext must not be None");
1312 SNINativeMethodWrapper
.SNI_Error sniError
= new SNINativeMethodWrapper
.SNI_Error();
1313 SNINativeMethodWrapper
.SNIGetLastError(sniError
);
1315 if (sniError
.sniError
!= 0) {
1317 // handle special SNI error codes that are converted into exception which is not a SqlException.
1318 switch (sniError
.sniError
) {
1319 case (int)SNINativeMethodWrapper
.SniSpecialErrors
.MultiSubnetFailoverWithMoreThan64IPs
:
1320 // Connecting with the MultiSubnetFailover connection option to a SQL Server instance configured with more than 64 IP addresses is not supported.
1321 throw SQL
.MultiSubnetFailoverWithMoreThan64IPs();
1323 case (int)SNINativeMethodWrapper
.SniSpecialErrors
.MultiSubnetFailoverWithInstanceSpecified
:
1324 // Connecting to a named SQL Server instance using the MultiSubnetFailover connection option is not supported.
1325 throw SQL
.MultiSubnetFailoverWithInstanceSpecified();
1327 case (int)SNINativeMethodWrapper
.SniSpecialErrors
.MultiSubnetFailoverWithNonTcpProtocol
:
1328 // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol.
1329 throw SQL
.MultiSubnetFailoverWithNonTcpProtocol();
1331 // continue building SqlError instance
1335 // error.errorMessage is null terminated with garbage beyond that, since fixed length
1336 string errorMessage
;
1337 int MessageLength
= Array
.IndexOf(sniError
.errorMessage
, '\0');
1338 if (MessageLength
== -1) {
1339 errorMessage
= String
.Empty
; // If we don't see the expected null return nothing.
1341 errorMessage
= new String(sniError
.errorMessage
, 0, MessageLength
);
1344 // Format SNI errors and add Context Information
1346 // General syntax is:
1347 // <sqlclient message>
1348 // (provider:<SNIx provider>, error: <SNIx error code> - <SNIx error message>)
1350 // errorMessage | sniError |
1351 // -------------------------------------------
1352 // ==null | x | must never happen
1353 // !=null | != 0 | retrieve corresponding errorMessage from resources
1354 // !=null | == 0 | replace text left of errorMessage
1357 Debug
.Assert(!ADP
.IsEmpty(errorMessage
),"Empty error message received from SNI");
1359 string sqlContextInfo
= Res
.GetString(Enum
.GetName(typeof(SniContext
), stateObj
.SniContext
));
1361 string providerRid
= String
.Format((IFormatProvider
)null,"SNI_PN{0}", (int)sniError
.provider
);
1362 string providerName
= Res
.GetString(providerRid
);
1363 Debug
.Assert(!ADP
.IsEmpty(providerName
), String
.Format((IFormatProvider
)null,"invalid providerResourceId '{0}'", providerRid
));
1364 uint win32ErrorCode
= sniError
.nativeError
;
1366 if (sniError
.sniError
== 0) {
1367 // Provider error. The message from provider is preceeded with non-localizable info from SNI
1368 // strip provider info from SNI
1370 int iColon
= errorMessage
.IndexOf(':');
1371 Debug
.Assert(0<=iColon
, "':' character missing in sni errorMessage");
1372 Debug
.Assert(errorMessage
.Length
>iColon
+1 && errorMessage
[iColon
+1]==' ', "Expecting a space after the ':' character");
1374 // extract the message excluding the colon and trailing cr/lf chars
1376 int len
= errorMessage
.Length
;
1377 len
-=2; // exclude "\r\n" sequence
1378 iColon
+=2; // skip over ": " sequence
1381 The error message should come back in the following format: "TCP Provider: MESSAGE TEXT"
1382 Fix Bug 370686, if the message is recieved on a Win9x OS, the error message will not contain MESSAGE TEXT
1383 per Bug: 269574. If we get a errormessage with no message text, just return the entire message otherwise
1384 return just the message text.
1387 errorMessage
= errorMessage
.Substring(iColon
, len
);
1392 // SNI error. Replace the entire message
1394 errorMessage
= SQL
.GetSNIErrorMessage((int)sniError
.sniError
);
1396 // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
1397 if (sniError
.sniError
== (int)SNINativeMethodWrapper
.SniSpecialErrors
.LocalDBErrorCode
) {
1398 errorMessage
+= LocalDBAPI
.GetLocalDBMessage((int)sniError
.nativeError
);
1402 errorMessage
= String
.Format((IFormatProvider
)null, "{0} (provider: {1}, error: {2} - {3})",
1403 sqlContextInfo
, providerName
, (int)sniError
.sniError
, errorMessage
);
1405 return new SqlError((int)sniError
.nativeError
, 0x00, TdsEnums
.FATAL_ERROR_CLASS
,
1406 _server
, errorMessage
, sniError
.function
, (int)sniError
.lineNumber
, win32ErrorCode
);
1409 internal void CheckResetConnection(TdsParserStateObject stateObj
) {
1410 if (_fResetConnection
&& !stateObj
._fResetConnectionSent
) {
1411 Debug
.Assert(stateObj
._outputPacketNumber
== 1 || stateObj
._outputPacketNumber
== 2, "In ResetConnection logic unexpectedly!");
1413 if (_fMARS
&& !stateObj
._fResetEventOwned
) {
1414 // If using Async & MARS and we do not own ResetEvent - grab it. We need to not grab lock here
1415 // for case where multiple packets are sent to server from one execute.
1416 stateObj
._fResetEventOwned
= _resetConnectionEvent
.WaitOne(stateObj
.GetTimeoutRemaining(), false);
1418 if (stateObj
._fResetEventOwned
) {
1419 if (stateObj
.TimeoutHasExpired
) {
1420 // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining.
1421 stateObj
._fResetEventOwned
= !_resetConnectionEvent
.Set();
1422 stateObj
.TimeoutTime
= 0;
1426 if (!stateObj
._fResetEventOwned
) {
1427 // We timed out waiting for ResetEvent. Throw timeout exception and reset
1428 // the buffer. Nothing else to do since we did not actually send anything
1430 stateObj
.ResetBuffer();
1431 Debug
.Assert(_connHandler
!= null, "SqlConnectionInternalTds handler can not be null at this point.");
1432 stateObj
.AddError(new SqlError(TdsEnums
.TIMEOUT_EXPIRED
, (byte)0x00, TdsEnums
.MIN_ERROR_CLASS
, _server
, _connHandler
.TimeoutErrorInternal
.GetErrorMessage(), "", 0, TdsEnums
.SNI_WAIT_TIMEOUT
));
1433 Debug
.Assert(_connHandler
._parserLock
.ThreadMayHaveLock(), "Thread is writing without taking the connection lock");
1434 ThrowExceptionAndWarning(stateObj
, callerHasConnectionLock
: true);
1438 if (_fResetConnection
) {
1439 // Check again to see if we need to send reset.
1441 Debug
.Assert(!stateObj
._fResetConnectionSent
, "Unexpected state for sending reset connection");
1442 Debug
.Assert(_isShiloh
, "TdsParser.cs: Error! _fResetConnection true when not going against Shiloh or later!");
1444 if (_fPreserveTransaction
) {
1445 // if we are reseting, set bit in header by or'ing with other value
1446 stateObj
._outBuff
[1] = (Byte
)(stateObj
._outBuff
[1] | TdsEnums
.ST_RESET_CONNECTION_PRESERVE_TRANSACTION
);
1449 // if we are reseting, set bit in header by or'ing with other value
1450 stateObj
._outBuff
[1] = (Byte
)(stateObj
._outBuff
[1] | TdsEnums
.ST_RESET_CONNECTION
);
1454 _fResetConnection
= false; // If not MARS, can turn off flag now.
1455 _fPreserveTransaction
= false;
1458 stateObj
._fResetConnectionSent
= true; // Otherwise set flag so we don't resend on multiple packet execute.
1461 else if (_fMARS
&& stateObj
._fResetEventOwned
) {
1462 Debug
.Assert(!stateObj
._fResetConnectionSent
, "Unexpected state on WritePacket ResetConnection");
1464 // Otherwise if Yukon and we grabbed the event, free it. Another execute grabbed the event and
1465 // took care of sending the reset.
1466 stateObj
._fResetEventOwned
= !_resetConnectionEvent
.Set();
1467 Debug
.Assert(!stateObj
._fResetEventOwned
, "Invalid AutoResetEvent state!");
1471 if (_fMARS
&& stateObj
._fResetEventOwned
) {
1472 // If exception thrown, and we are on Yukon and own the event, release it!
1473 stateObj
._fResetConnectionSent
= false;
1474 stateObj
._fResetEventOwned
= !_resetConnectionEvent
.Set();
1475 Debug
.Assert(!stateObj
._fResetEventOwned
, "Invalid AutoResetEvent state!");
1483 Debug
.Assert(!_fResetConnection
||
1484 (_fResetConnection
&& stateObj
._fResetConnectionSent
&& stateObj
._fResetEventOwned
),
1485 "Unexpected state on else ResetConnection block in WritePacket");
1491 // Takes a 16 bit short and writes it.
1493 internal byte[] SerializeShort(int v
, TdsParserStateObject stateObj
) {
1494 if (null == stateObj
._bShortBytes
) {
1495 stateObj
._bShortBytes
= new byte[2];
1498 Debug
.Assert(2 == stateObj
._bShortBytes
.Length
);
1501 byte[] bytes
= stateObj
._bShortBytes
;
1503 bytes
[current
++] = (byte)(v
& 0xff);
1504 bytes
[current
++] = (byte)((v
>> 8) & 0xff);
1508 internal void WriteShort(int v
, TdsParserStateObject stateObj
) {
1509 ReliabilitySection
.Assert("unreliable call to WriteShort"); // you need to setup for a thread abort somewhere before you call this method
1511 if ((stateObj
._outBytesUsed
+ 2) > stateObj
._outBuff
.Length
) {
1512 // if all of the short doesn't fit into the buffer
1513 stateObj
.WriteByte((byte)(v
& 0xff));
1514 stateObj
.WriteByte((byte)((v
>> 8) & 0xff));
1517 // all of the short fits into the buffer
1518 stateObj
._outBuff
[stateObj
._outBytesUsed
] = (byte)(v
& 0xff);
1519 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 1] = (byte)((v
>> 8) & 0xff);
1520 stateObj
._outBytesUsed
+= 2;
1524 internal void WriteUnsignedShort(ushort us
, TdsParserStateObject stateObj
) {
1525 WriteShort((short)us
, stateObj
);
1529 // Takes a long and writes out an unsigned int
1531 internal byte[] SerializeUnsignedInt(uint i
, TdsParserStateObject stateObj
) {
1532 return SerializeInt((int)i
, stateObj
);
1535 internal void WriteUnsignedInt(uint i
, TdsParserStateObject stateObj
) {
1536 WriteInt((int)i
, stateObj
);
1540 // Takes an int and writes it as an int.
1542 internal byte[] SerializeInt(int v
, TdsParserStateObject stateObj
) {
1543 if (null == stateObj
._bIntBytes
) {
1544 stateObj
._bIntBytes
= new byte[4];
1547 Debug
.Assert (4 == stateObj
._bIntBytes
.Length
);
1551 byte[] bytes
= stateObj
._bIntBytes
;
1552 bytes
[current
++] = (byte)(v
& 0xff);
1553 bytes
[current
++] = (byte)((v
>> 8) & 0xff);
1554 bytes
[current
++] = (byte)((v
>> 16) & 0xff);
1555 bytes
[current
++] = (byte)((v
>> 24) & 0xff);
1559 internal void WriteInt(int v
, TdsParserStateObject stateObj
) {
1560 ReliabilitySection
.Assert("unreliable call to WriteInt"); // you need to setup for a thread abort somewhere before you call this method
1562 if ((stateObj
._outBytesUsed
+ 4) > stateObj
._outBuff
.Length
) {
1563 // if all of the int doesn't fit into the buffer
1564 for (int shiftValue
= 0; shiftValue
< sizeof(int) * 8; shiftValue
+= 8) {
1565 stateObj
.WriteByte((byte)((v
>> shiftValue
) & 0xff));
1569 // all of the int fits into the buffer
1570 // NOTE: We don't use a loop here for performance
1571 stateObj
._outBuff
[stateObj
._outBytesUsed
] = (byte)(v
& 0xff);
1572 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 1] = (byte)((v
>> 8) & 0xff);
1573 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 2] = (byte)((v
>> 16) & 0xff);
1574 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 3] = (byte)((v
>> 24) & 0xff);
1575 stateObj
._outBytesUsed
+= 4;
1580 // Takes a float and writes it as a 32 bit float.
1582 internal byte[] SerializeFloat(float v
) {
1583 if (Single
.IsInfinity(v
) || Single
.IsNaN(v
)) {
1584 throw ADP
.ParameterValueOutOfRange(v
.ToString());
1587 return BitConverter
.GetBytes(v
);
1590 internal void WriteFloat(float v
, TdsParserStateObject stateObj
) {
1591 byte[] bytes
= BitConverter
.GetBytes(v
);
1593 stateObj
.WriteByteArray(bytes
, bytes
.Length
, 0);
1597 // Takes a long and writes it as a long.
1599 internal byte[] SerializeLong(long v
, TdsParserStateObject stateObj
) {
1601 if (null == stateObj
._bLongBytes
) {
1602 stateObj
._bLongBytes
= new byte[8];
1605 byte[] bytes
= stateObj
._bLongBytes
;
1606 Debug
.Assert (8 == bytes
.Length
, "Cached buffer has wrong size");
1608 bytes
[current
++] = (byte)(v
& 0xff);
1609 bytes
[current
++] = (byte)((v
>> 8) & 0xff);
1610 bytes
[current
++] = (byte)((v
>> 16) & 0xff);
1611 bytes
[current
++] = (byte)((v
>> 24) & 0xff);
1612 bytes
[current
++] = (byte)((v
>> 32) & 0xff);
1613 bytes
[current
++] = (byte)((v
>> 40) & 0xff);
1614 bytes
[current
++] = (byte)((v
>> 48) & 0xff);
1615 bytes
[current
++] = (byte)((v
>> 56) & 0xff);
1620 internal void WriteLong(long v
, TdsParserStateObject stateObj
) {
1621 ReliabilitySection
.Assert("unreliable call to WriteLong"); // you need to setup for a thread abort somewhere before you call this method
1623 if ((stateObj
._outBytesUsed
+ 8) > stateObj
._outBuff
.Length
) {
1624 // if all of the long doesn't fit into the buffer
1625 for (int shiftValue
= 0; shiftValue
< sizeof(long) * 8; shiftValue
+= 8) {
1626 stateObj
.WriteByte((byte)((v
>> shiftValue
) & 0xff));
1630 // all of the long fits into the buffer
1631 // NOTE: We don't use a loop here for performance
1632 stateObj
._outBuff
[stateObj
._outBytesUsed
] = (byte)(v
& 0xff);
1633 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 1] = (byte)((v
>> 8) & 0xff);
1634 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 2] = (byte)((v
>> 16) & 0xff);
1635 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 3] = (byte)((v
>> 24) & 0xff);
1636 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 4] = (byte)((v
>> 32) & 0xff);
1637 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 5] = (byte)((v
>> 40) & 0xff);
1638 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 6] = (byte)((v
>> 48) & 0xff);
1639 stateObj
._outBuff
[stateObj
._outBytesUsed
+ 7] = (byte)((v
>> 56) & 0xff);
1640 stateObj
._outBytesUsed
+= 8;
1645 // Takes a long and writes part of it
1647 internal byte[] SerializePartialLong(long v
, int length
) {
1648 Debug
.Assert(length
<= 8, "Length specified is longer than the size of a long");
1649 Debug
.Assert(length
>= 0, "Length should not be negative");
1651 byte[] bytes
= new byte[length
];
1653 // all of the long fits into the buffer
1654 for (int index
= 0; index
< length
; index
++) {
1655 bytes
[index
] = (byte)((v
>> (index
* 8)) & 0xff);
1661 internal void WritePartialLong(long v
, int length
, TdsParserStateObject stateObj
) {
1662 ReliabilitySection
.Assert("unreliable call to WritePartialLong"); // you need to setup for a thread abort somewhere before you call this method
1663 Debug
.Assert(length
<= 8, "Length specified is longer than the size of a long");
1664 Debug
.Assert(length
>= 0, "Length should not be negative");
1666 if ((stateObj
._outBytesUsed
+ length
) > stateObj
._outBuff
.Length
) {
1667 // if all of the long doesn't fit into the buffer
1668 for (int shiftValue
= 0; shiftValue
< length
* 8; shiftValue
+= 8) {
1669 stateObj
.WriteByte((byte)((v
>> shiftValue
) & 0xff));
1673 // all of the long fits into the buffer
1674 for (int index
= 0; index
< length
; index
++) {
1675 stateObj
._outBuff
[stateObj
._outBytesUsed
+ index
] = (byte)((v
>> (index
* 8)) & 0xff);
1677 stateObj
._outBytesUsed
+= length
;
1682 // Takes a ulong and writes it as a ulong.
1684 internal void WriteUnsignedLong(ulong uv
, TdsParserStateObject stateObj
) {
1685 WriteLong((long)uv
, stateObj
);
1689 // Takes a double and writes it as a 64 bit double.
1691 internal byte[] SerializeDouble(double v
) {
1692 if (Double
.IsInfinity(v
) || Double
.IsNaN(v
)) {
1693 throw ADP
.ParameterValueOutOfRange(v
.ToString());
1696 return BitConverter
.GetBytes(v
);
1699 internal void WriteDouble(double v
, TdsParserStateObject stateObj
) {
1700 byte[] bytes
= BitConverter
.GetBytes(v
);
1702 stateObj
.WriteByteArray(bytes
, bytes
.Length
, 0);
1705 internal void PrepareResetConnection(bool preserveTransaction
) {
1706 // Set flag to reset connection upon next use - only for use on shiloh!
1707 _fResetConnection
= true;
1708 _fPreserveTransaction
= preserveTransaction
;
1711 internal bool RunReliably(RunBehavior runBehavior
, SqlCommand cmdHandler
, SqlDataReader dataStream
, BulkCopySimpleResultSet bulkCopyHandler
, TdsParserStateObject stateObj
) {
1712 RuntimeHelpers
.PrepareConstrainedRegions();
1715 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
1716 RuntimeHelpers
.PrepareConstrainedRegions();
1718 tdsReliabilitySection
.Start();
1720 return Run(runBehavior
, cmdHandler
, dataStream
, bulkCopyHandler
, stateObj
);
1724 tdsReliabilitySection
.Stop();
1728 catch (OutOfMemoryException
) {
1729 _connHandler
.DoomThisConnection();
1732 catch (StackOverflowException
) {
1733 _connHandler
.DoomThisConnection();
1736 catch (ThreadAbortException
) {
1737 _connHandler
.DoomThisConnection();
1743 internal bool Run(RunBehavior runBehavior
, SqlCommand cmdHandler
, SqlDataReader dataStream
, BulkCopySimpleResultSet bulkCopyHandler
, TdsParserStateObject stateObj
) {
1744 bool syncOverAsync
= stateObj
._syncOverAsync
;
1747 stateObj
._syncOverAsync
= true;
1750 bool result
= TryRun(runBehavior
, cmdHandler
, dataStream
, bulkCopyHandler
, stateObj
, out dataReady
);
1751 Debug
.Assert(result
== true, "Should never return false when _syncOverAsync is set");
1756 stateObj
._syncOverAsync
= syncOverAsync
;
1761 /// Checks if the given token is a valid TDS token
1763 /// <param name="token">Token to check</param>
1764 /// <returns>True if the token is a valid TDS token, otherwise false</returns>
1765 internal static bool IsValidTdsToken(byte token
) {
1767 token
== TdsEnums
.SQLERROR
||
1768 token
== TdsEnums
.SQLINFO
||
1769 token
== TdsEnums
.SQLLOGINACK
||
1770 token
== TdsEnums
.SQLENVCHANGE
||
1771 token
== TdsEnums
.SQLRETURNVALUE
||
1772 token
== TdsEnums
.SQLRETURNSTATUS
||
1773 token
== TdsEnums
.SQLCOLNAME
||
1774 token
== TdsEnums
.SQLCOLFMT
||
1775 token
== TdsEnums
.SQLCOLMETADATA
||
1776 token
== TdsEnums
.SQLALTMETADATA
||
1777 token
== TdsEnums
.SQLTABNAME
||
1778 token
== TdsEnums
.SQLCOLINFO
||
1779 token
== TdsEnums
.SQLORDER
||
1780 token
== TdsEnums
.SQLALTROW
||
1781 token
== TdsEnums
.SQLROW
||
1782 token
== TdsEnums
.SQLNBCROW
||
1783 token
== TdsEnums
.SQLDONE
||
1784 token
== TdsEnums
.SQLDONEPROC
||
1785 token
== TdsEnums
.SQLDONEINPROC
||
1786 token
== TdsEnums
.SQLROWCRC
||
1787 token
== TdsEnums
.SQLSECLEVEL
||
1788 token
== TdsEnums
.SQLPROCID
||
1789 token
== TdsEnums
.SQLOFFSET
||
1790 token
== TdsEnums
.SQLSSPI
||
1791 token
== TdsEnums
.SQLFEATUREEXTACK
||
1792 token
== TdsEnums
.SQLSESSIONSTATE
||
1793 token
== TdsEnums
.SQLFEDAUTHINFO
);
1796 // Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces
1797 internal bool TryRun(RunBehavior runBehavior
, SqlCommand cmdHandler
, SqlDataReader dataStream
, BulkCopySimpleResultSet bulkCopyHandler
, TdsParserStateObject stateObj
, out bool dataReady
) {
1798 ReliabilitySection
.Assert("unreliable call to Run"); // you need to setup for a thread abort somewhere before you call this method
1799 Debug
.Assert((SniContext
.Undefined
!= stateObj
.SniContext
) && // SniContext must not be Undefined
1800 ((stateObj
._attentionSent
) || ((SniContext
.Snix_Execute
!= stateObj
.SniContext
) && (SniContext
.Snix_SendRows
!= stateObj
.SniContext
))), // SniContext should not be Execute or SendRows unless attention was sent (and, therefore, we are looking for an ACK)
1801 String
.Format("Unexpected SniContext on call to TryRun; SniContext={0}", stateObj
.SniContext
));
1803 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
) {
1805 return true; // Just in case this is called in a loop, expecting data to be returned.
1811 // If there is data ready, but we didn't exit the loop, then something is wrong
1812 Debug
.Assert(!dataReady
, "dataReady not expected - did we forget to skip the row?");
1814 if (stateObj
._internalTimeout
) {
1815 runBehavior
= RunBehavior
.Attention
;
1818 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
)
1819 break; // jump out of the loop if the state is already broken or closed.
1821 if (!stateObj
._accumulateInfoEvents
&& (stateObj
._pendingInfoEvents
!= null)) {
1822 if (RunBehavior
.Clean
!= (RunBehavior
.Clean
& runBehavior
)) {
1823 SqlConnection connection
= null;
1824 if (_connHandler
!= null)
1825 connection
= _connHandler
.Connection
; // SqlInternalConnection holds the user connection object as a weak ref
1826 // We are omitting checks for error.Class in the code below (see processing of INFO) since we know (and assert) that error class
1827 // error.Class < TdsEnums.MIN_ERROR_CLASS for info message.
1828 // Also we know that TdsEnums.MIN_ERROR_CLASS<TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS
1829 if ((connection
!= null) && connection
.FireInfoMessageEventOnUserErrors
)
1831 foreach (SqlError error
in stateObj
._pendingInfoEvents
)
1832 FireInfoMessageEvent(connection
, stateObj
, error
);
1835 foreach (SqlError error
in stateObj
._pendingInfoEvents
)
1836 stateObj
.AddWarning(error
);
1839 stateObj
._pendingInfoEvents
=null;
1843 if (!stateObj
.TryReadByte(out token
)) {
1847 if (!IsValidTdsToken(token
)) {
1848 Debug
.Assert(false, String
.Format((IFormatProvider
)null, "unexpected token; token = {0,-2:X2}", token
));
1849 _state
= TdsParserState
.Broken
;
1850 _connHandler
.BreakConnection();
1851 Bid
.Trace("<sc.TdsParser.Run|ERR> Potential multi-threaded misuse of connection, unexpected TDS token found %d#\n", ObjectID
);
1852 throw SQL
.ParsingErrorToken(ParsingErrorState
.InvalidTdsTokenReceived
, token
); // MDAC 82443
1856 if (!TryGetTokenLength(token
, stateObj
, out tokenLength
)) {
1861 case TdsEnums
.SQLERROR
:
1862 case TdsEnums
.SQLINFO
:
1864 if (token
== TdsEnums
.SQLERROR
) {
1865 stateObj
._errorTokenReceived
= true; // Keep track of the fact error token was received - for Done processing.
1869 if (!TryProcessError(token
, stateObj
, out error
)) {
1873 if (token
== TdsEnums
.SQLINFO
&& stateObj
._accumulateInfoEvents
)
1875 Debug
.Assert(error
.Class
< TdsEnums
.MIN_ERROR_CLASS
, "INFO with class > TdsEnums.MIN_ERROR_CLASS");
1877 if (stateObj
._pendingInfoEvents
== null)
1878 stateObj
._pendingInfoEvents
= new List
<SqlError
>();
1879 stateObj
._pendingInfoEvents
.Add(error
);
1880 stateObj
._syncOverAsync
= true;
1884 if (RunBehavior
.Clean
!= (RunBehavior
.Clean
& runBehavior
)) {
1885 // If FireInfoMessageEventOnUserErrors is true, we have to fire event without waiting.
1886 // Otherwise we can go ahead and add it to errors/warnings collection.
1887 SqlConnection connection
= null;
1888 if (_connHandler
!= null)
1889 connection
= _connHandler
.Connection
; // SqlInternalConnection holds the user connection object as a weak ref
1891 if ((connection
!= null) &&
1892 (connection
.FireInfoMessageEventOnUserErrors
== true) &&
1893 (error
.Class
<= TdsEnums
.MAX_USER_CORRECTABLE_ERROR_CLASS
)) {
1894 // Fire SqlInfoMessage here
1895 FireInfoMessageEvent(connection
, stateObj
, error
);
1898 // insert error/info into the appropriate exception - warning if info, exception if error
1899 if (error
.Class
< TdsEnums
.MIN_ERROR_CLASS
) {
1900 stateObj
.AddWarning(error
);
1902 else if (error
.Class
< TdsEnums
.FATAL_ERROR_CLASS
) {
1903 // VSTFDEVDIV 479643: continue results processing for all non-fatal errors (<20)
1905 stateObj
.AddError(error
);
1907 // Add it to collection - but do NOT change run behavior UNLESS
1908 // we are in an ExecuteReader call - at which time we will be throwing
1909 // anyways so we need to consume all errors. This is not the case
1910 // if we have already given out a reader. If we have already given out
1911 // a reader we need to throw the error but not halt further processing. We used to
1912 // halt processing and that was a bug preventing the user from
1913 // processing subsequent results.
1915 if (null != dataStream
) { // Webdata 104560
1916 if (!dataStream
.IsInitialized
) {
1917 runBehavior
= RunBehavior
.UntilDone
;
1922 stateObj
.AddError(error
);
1924 // Else we have a fatal error and we need to change the behavior
1925 // since we want the complete error information in the exception.
1926 // Besides - no further results will be received.
1927 runBehavior
= RunBehavior
.UntilDone
;
1931 else if (error
.Class
>= TdsEnums
.FATAL_ERROR_CLASS
) {
1932 stateObj
.AddError(error
);
1937 case TdsEnums
.SQLCOLINFO
:
1939 if (null != dataStream
) {
1940 _SqlMetaDataSet metaDataSet
;
1941 if (!TryProcessColInfo(dataStream
.MetaData
, dataStream
, stateObj
, out metaDataSet
)) {
1944 if (!dataStream
.TrySetMetaData(metaDataSet
, false)) {
1947 dataStream
.BrowseModeInfoConsumed
= true;
1949 else { // no dataStream
1950 if (!stateObj
.TrySkipBytes(tokenLength
)) {
1957 case TdsEnums
.SQLDONE
:
1958 case TdsEnums
.SQLDONEPROC
:
1959 case TdsEnums
.SQLDONEINPROC
:
1961 // RunBehavior can be modified - see SQL BU DT 269516 & 290090
1962 if (!TryProcessDone(cmdHandler
, dataStream
, ref runBehavior
, stateObj
)) {
1965 if ((token
== TdsEnums
.SQLDONEPROC
) && (cmdHandler
!= null)) {
1966 // If the current parse/read is for the results of describe parameter encryption RPC requests,
1967 // call a different handler which will update the describe parameter encryption RPC structures
1968 // with the results, instead of the actual user RPC requests.
1969 if (cmdHandler
.IsDescribeParameterEncryptionRPCCurrentlyInProgress
) {
1970 cmdHandler
.OnDoneDescribeParameterEncryptionProc(stateObj
);
1973 cmdHandler
.OnDoneProc();
1980 case TdsEnums
.SQLORDER
:
1982 // don't do anything with the order token so read off the pipe
1983 if (!stateObj
.TrySkipBytes(tokenLength
)) {
1989 case TdsEnums
.SQLALTMETADATA
:
1991 stateObj
.CloneCleanupAltMetaDataSetArray();
1993 if (stateObj
._cleanupAltMetaDataSetArray
== null) {
1994 // create object on demand (lazy creation)
1995 stateObj
._cleanupAltMetaDataSetArray
= new _SqlMetaDataSetCollection();
1998 _SqlMetaDataSet cleanupAltMetaDataSet
;
1999 if (!TryProcessAltMetaData(tokenLength
, stateObj
, out cleanupAltMetaDataSet
)) {
2003 stateObj
._cleanupAltMetaDataSetArray
.SetAltMetaData(cleanupAltMetaDataSet
);
2004 if (null != dataStream
) {
2005 byte metadataConsumedByte
;
2006 if (!stateObj
.TryPeekByte(out metadataConsumedByte
)) {
2009 if (!dataStream
.TrySetAltMetaDataSet(cleanupAltMetaDataSet
, (TdsEnums
.SQLALTMETADATA
!= metadataConsumedByte
))) {
2017 case TdsEnums
.SQLALTROW
:
2019 if (!stateObj
.TryStartNewRow(isNullCompressed
:false)) { // altrows are not currently null compressed
2023 // read will call run until dataReady. Must not read any data if returnimmetiately set
2024 if (RunBehavior
.ReturnImmediately
!= (RunBehavior
.ReturnImmediately
& runBehavior
)) {
2026 if (!stateObj
.TryReadUInt16(out altRowId
)) { // get altRowId
2030 if (!TrySkipRow(stateObj
._cleanupAltMetaDataSetArray
.GetAltMetaData(altRowId
), stateObj
)) { // skip altRow
2041 case TdsEnums
.SQLENVCHANGE
:
2043 // ENVCHANGE must be processed synchronously (since it can modify the state of many objects)
2044 stateObj
._syncOverAsync
= true;
2047 if (!TryProcessEnvChange(tokenLength
, stateObj
, out env
)) {
2051 for (int ii
= 0; ii
< env
.Length
; ii
++) {
2052 if (env
[ii
] != null && !this.Connection
.IgnoreEnvChange
) {
2053 switch (env
[ii
].type
) {
2054 case TdsEnums
.ENV_BEGINTRAN
:
2055 case TdsEnums
.ENV_ENLISTDTC
:
2056 // When we get notification from the server of a new
2057 // transaction, we move any pending transaction over to
2058 // the current transaction, then we store the token in it.
2059 // if there isn't a pending transaction, then it's either
2060 // a TSQL transaction or a distributed transaction.
2061 Debug
.Assert(null == _currentTransaction
, "non-null current transaction with an ENV Change");
2062 _currentTransaction
= _pendingTransaction
;
2063 _pendingTransaction
= null;
2065 if (null != _currentTransaction
) {
2066 _currentTransaction
.TransactionId
= env
[ii
].newLongValue
; // this is defined as a ULongLong in the server and in the TDS Spec.
2069 TransactionType transactionType
= (TdsEnums
.ENV_BEGINTRAN
== env
[ii
].type
) ? TransactionType
.LocalFromTSQL
: TransactionType
.Distributed
;
2070 _currentTransaction
= new SqlInternalTransaction(_connHandler
, transactionType
, null, env
[ii
].newLongValue
);
2072 if (null != _statistics
&& !_statisticsIsInTransaction
) {
2073 _statistics
.SafeIncrement(ref _statistics
._transactions
);
2075 _statisticsIsInTransaction
= true;
2076 _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
2078 case TdsEnums
.ENV_DEFECTDTC
:
2079 case TdsEnums
.ENV_TRANSACTIONENDED
:
2080 case TdsEnums
.ENV_COMMITTRAN
:
2082 // Must clear the retain id if the server-side transaction ends by anything other
2084 _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
2085 goto case TdsEnums
.ENV_ROLLBACKTRAN
;
2086 case TdsEnums
.ENV_ROLLBACKTRAN
:
2087 // When we get notification of a completed transaction
2088 // we null out the current transaction.
2089 if (null != _currentTransaction
) {
2091 // Check null for case where Begin and Rollback obtained in the same message.
2092 if (SqlInternalTransaction
.NullTransactionId
!= _currentTransaction
.TransactionId
) {
2093 Debug
.Assert(_currentTransaction
.TransactionId
!= env
[ii
].newLongValue
, "transaction id's are not equal!");
2097 if (TdsEnums
.ENV_COMMITTRAN
== env
[ii
].type
) {
2098 _currentTransaction
.Completed(TransactionState
.Committed
);
2100 else if (TdsEnums
.ENV_ROLLBACKTRAN
== env
[ii
].type
) {
2101 // Hold onto transaction id if distributed tran is rolled back. This must
2102 // be sent to the server on subsequent executions even though the transaction
2103 // is considered to be rolled back.
2104 if (_currentTransaction
.IsDistributed
&& _currentTransaction
.IsActive
) {
2105 _retainedTransactionId
= env
[ii
].oldLongValue
;
2107 _currentTransaction
.Completed(TransactionState
.Aborted
);
2111 _currentTransaction
.Completed(TransactionState
.Unknown
);
2113 _currentTransaction
= null;
2115 _statisticsIsInTransaction
= false;
2118 _connHandler
.OnEnvChange(env
[ii
]);
2125 case TdsEnums
.SQLLOGINACK
:
2127 Bid
.Trace("<sc.TdsParser.TryRun|SEC> Received login acknowledgement token\n");
2129 if (!TryProcessLoginAck(stateObj
, out ack
)) {
2133 _connHandler
.OnLoginAck(ack
);
2136 case TdsEnums
.SQLFEATUREEXTACK
:
2138 if (!TryProcessFeatureExtAck(stateObj
)) {
2143 case TdsEnums
.SQLFEDAUTHINFO
:
2145 _connHandler
._federatedAuthenticationInfoReceived
= true;
2146 SqlFedAuthInfo info
;
2147 Bid
.Trace("<sc.TdsParser.TryRun|SEC> Received federated authentication info token\n");
2148 if (!TryProcessFedAuthInfo(stateObj
, tokenLength
, out info
)) {
2151 _connHandler
.OnFedAuthInfo(info
);
2154 case TdsEnums
.SQLSESSIONSTATE
:
2156 if (!TryProcessSessionState(stateObj
, tokenLength
, _connHandler
._currentSessionData
)) {
2161 case TdsEnums
.SQLCOLMETADATA
:
2163 if (tokenLength
!= TdsEnums
.VARNULL
) {
2164 _SqlMetaDataSet metadata
;
2165 if (!TryProcessMetaData(tokenLength
, stateObj
, out metadata
,
2166 cmdHandler
!= null ? cmdHandler
.ColumnEncryptionSetting
: SqlCommandColumnEncryptionSetting
.UseConnectionSetting
)) {
2169 stateObj
._cleanupMetaData
= metadata
;
2172 if (cmdHandler
!= null) {
2173 stateObj
._cleanupMetaData
= cmdHandler
.MetaData
;
2177 if (null != dataStream
) {
2179 if (!stateObj
.TryPeekByte(out peekedToken
)) { // temporarily cache next byte
2183 if (!dataStream
.TrySetMetaData(stateObj
._cleanupMetaData
, (TdsEnums
.SQLTABNAME
== peekedToken
|| TdsEnums
.SQLCOLINFO
== peekedToken
))) {
2187 else if (null != bulkCopyHandler
) {
2188 bulkCopyHandler
.SetMetaData(stateObj
._cleanupMetaData
);
2192 case TdsEnums
.SQLROW
:
2193 case TdsEnums
.SQLNBCROW
:
2195 Debug
.Assert(stateObj
._cleanupMetaData
!= null, "Reading a row, but the metadata is null");
2197 if (token
== TdsEnums
.SQLNBCROW
) {
2198 if (!stateObj
.TryStartNewRow(isNullCompressed
: true, nullBitmapColumnsCount
: stateObj
._cleanupMetaData
.Length
)) {
2203 if (!stateObj
.TryStartNewRow(isNullCompressed
:false)) {
2208 if (null != bulkCopyHandler
) {
2210 if (!TryProcessRow(stateObj
._cleanupMetaData
, bulkCopyHandler
.CreateRowBuffer(), bulkCopyHandler
.CreateIndexMap(), stateObj
)) {
2214 else if (RunBehavior
.ReturnImmediately
!= (RunBehavior
.ReturnImmediately
& runBehavior
)) {
2215 if (!TrySkipRow(stateObj
._cleanupMetaData
, stateObj
)) { // skip rows
2223 if (_statistics
!= null) {
2224 _statistics
.WaitForDoneAfterRow
= true;
2228 case TdsEnums
.SQLRETURNSTATUS
:
2230 if (!stateObj
.TryReadInt32(out status
)) {
2233 if (cmdHandler
!= null) {
2234 cmdHandler
.OnReturnStatus(status
);
2237 case TdsEnums
.SQLRETURNVALUE
:
2239 SqlReturnValue returnValue
;
2240 if (!TryProcessReturnValue(tokenLength
, stateObj
, out returnValue
,
2241 cmdHandler
!= null ? cmdHandler
.ColumnEncryptionSetting
: SqlCommandColumnEncryptionSetting
.UseConnectionSetting
)) {
2244 if (cmdHandler
!= null) {
2245 cmdHandler
.OnReturnValue(returnValue
, stateObj
);
2249 case TdsEnums
.SQLSSPI
:
2251 // token length is length of SSPI data - call ProcessSSPI with it
2253 Debug
.Assert(stateObj
._syncOverAsync
, "ProcessSSPI does not support retry, do not attempt asynchronously");
2254 stateObj
._syncOverAsync
= true;
2256 ProcessSSPI(tokenLength
);
2259 case TdsEnums
.SQLTABNAME
:
2261 if (null != dataStream
) {
2262 MultiPartTableName
[] tableNames
;
2263 if (!TryProcessTableName(tokenLength
, stateObj
, out tableNames
)) {
2266 dataStream
.TableNames
= tableNames
;
2269 if (!stateObj
.TrySkipBytes(tokenLength
)) {
2277 Debug
.Assert(false, "Unhandled token: " + token
.ToString(CultureInfo
.InvariantCulture
));
2281 Debug
.Assert(stateObj
._pendingData
|| !dataReady
, "dataReady is set, but there is no pending data");
2284 // Loop while data pending & runbehavior not return immediately, OR
2285 // if in attention case, loop while no more pending data & attention has not yet been
2287 while ((stateObj
._pendingData
&&
2288 (RunBehavior
.ReturnImmediately
!= (RunBehavior
.ReturnImmediately
& runBehavior
))) ||
2289 (!stateObj
._pendingData
&& stateObj
._attentionSent
&& !stateObj
._attentionReceived
));
2292 if ((stateObj
._pendingData
) && (!dataReady
)) {
2294 if (!stateObj
.TryPeekByte(out token
)) {
2297 Debug
.Assert(IsValidTdsToken(token
), string.Format("DataReady is false, but next token is not valid: {0,-2:X2}", token
));
2301 if (!stateObj
._pendingData
) {
2302 if (null != CurrentTransaction
) {
2303 CurrentTransaction
.Activate();
2307 // if we recieved an attention (but this thread didn't send it) then
2308 // we throw an Operation Cancelled error
2309 if (stateObj
._attentionReceived
) {
2310 // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close
2311 // Spin until SendAttention has cleared _attentionSending, this prevents a race condition between receiving the attention ACK and setting _attentionSent
2312 SpinWait
.SpinUntil(() => !stateObj
._attentionSending
);
2314 Debug
.Assert(stateObj
._attentionSent
, "Attention ACK has been received without attention sent");
2315 if (stateObj
._attentionSent
) {
2316 // Reset attention state.
2317 stateObj
._attentionSent
= false;
2318 stateObj
._attentionReceived
= false;
2320 if (RunBehavior
.Clean
!= (RunBehavior
.Clean
& runBehavior
) && !stateObj
._internalTimeout
) {
2321 // Add attention error to collection - if not RunBehavior.Clean!
2322 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.MIN_ERROR_CLASS
, _server
, SQLMessage
.OperationCancelled(), "", 0));
2327 if (stateObj
.HasErrorOrWarning
) {
2328 ThrowExceptionAndWarning(stateObj
);
2333 private bool TryProcessEnvChange(int tokenLength
, TdsParserStateObject stateObj
, out SqlEnvChange
[] sqlEnvChange
) {
2334 // There could be multiple environment change messages following this token.
2336 int processedLength
= 0;
2338 SqlEnvChange
[] envarray
= new SqlEnvChange
[3]; // Why is this hardcoded to 3?
2340 sqlEnvChange
= null;
2342 while (tokenLength
> processedLength
) {
2344 if (nvalues
>= envarray
.Length
) {
2345 // This is a rare path. Most of the time we will have 1 or 2 envchange data streams.
2346 SqlEnvChange
[] newenvarray
= new SqlEnvChange
[envarray
.Length
+ 3];
2348 for (int ii
= 0; ii
< envarray
.Length
; ii
++)
2349 newenvarray
[ii
] = envarray
[ii
];
2351 envarray
= newenvarray
;
2354 SqlEnvChange env
= new SqlEnvChange();
2356 if (!stateObj
.TryReadByte(out env
.type
)) {
2360 envarray
[nvalues
] = env
;
2364 case TdsEnums
.ENV_DATABASE
:
2365 case TdsEnums
.ENV_LANG
:
2366 if (!TryReadTwoStringFields(env
, stateObj
)) {
2371 case TdsEnums
.ENV_CHARSET
:
2372 // we copied this behavior directly from luxor - see charset envchange
2373 // section from sqlctokn.c
2374 Debug
.Assert(!_isShiloh
, "Received ENV_CHARSET on non 7.0 server!");
2375 if (!TryReadTwoStringFields(env
, stateObj
)) {
2378 if (env
.newValue
== TdsEnums
.DEFAULT_ENGLISH_CODE_PAGE_STRING
) {
2379 _defaultCodePage
= TdsEnums
.DEFAULT_ENGLISH_CODE_PAGE_VALUE
;
2380 _defaultEncoding
= System
.Text
.Encoding
.GetEncoding(_defaultCodePage
);
2383 Debug
.Assert(env
.newValue
.Length
> TdsEnums
.CHARSET_CODE_PAGE_OFFSET
, "TdsParser.ProcessEnvChange(): charset value received with length <=10");
2385 string stringCodePage
= env
.newValue
.Substring(TdsEnums
.CHARSET_CODE_PAGE_OFFSET
);
2387 _defaultCodePage
= Int32
.Parse(stringCodePage
, NumberStyles
.Integer
, CultureInfo
.InvariantCulture
);
2388 _defaultEncoding
= System
.Text
.Encoding
.GetEncoding(_defaultCodePage
);
2393 case TdsEnums
.ENV_PACKETSIZE
:
2394 // take care of packet size right here
2395 Debug
.Assert(stateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
2396 if (!TryReadTwoStringFields(env
, stateObj
)) {
2397 // Changing packet size does not support retry, should not pend"
2398 throw SQL
.SynchronousCallMayNotPend();
2400 // Only set on physical state object - this should only occur on LoginAck prior
2401 // to MARS initialization!
2402 Int32 packetSize
= Int32
.Parse(env
.newValue
, NumberStyles
.Integer
, CultureInfo
.InvariantCulture
);
2404 if (_physicalStateObj
.SetPacketSize(packetSize
)) {
2405 // If packet size changed, we need to release our SNIPackets since
2406 // those are tied to packet size of connection.
2407 _physicalStateObj
.ClearAllWritePackets();
2409 // Update SNI ConsumerInfo value to be resulting packet size
2410 UInt32 unsignedPacketSize
= (UInt32
) packetSize
;
2411 UInt32 result
= SNINativeMethodWrapper
.SNISetInfo(_physicalStateObj
.Handle
, SNINativeMethodWrapper
.QTypes
.SNI_QUERY_CONN_BUFSIZE
, ref unsignedPacketSize
);
2413 Debug
.Assert(result
== TdsEnums
.SNI_SUCCESS
, "Unexpected failure state upon calling SNISetInfo");
2418 case TdsEnums
.ENV_LOCALEID
:
2421 if (!TryReadTwoStringFields(env
, stateObj
)) {
2424 _defaultLCID
= Int32
.Parse(env
.newValue
, NumberStyles
.Integer
, CultureInfo
.InvariantCulture
);
2427 case TdsEnums
.ENV_COMPFLAGS
:
2428 if (!TryReadTwoStringFields(env
, stateObj
)) {
2433 case TdsEnums
.ENV_COLLATION
:
2434 Debug
.Assert(env
.newLength
== 5 || env
.newLength
== 0, "Improper length in new collation!");
2435 if (!stateObj
.TryReadByte(out byteLength
)) {
2438 env
.newLength
= byteLength
;
2439 if (env
.newLength
== 5) {
2440 if (!TryProcessCollation(stateObj
, out env
.newCollation
)) {
2444 // give the parser the new collation values in case parameters don't specify one
2445 _defaultCollation
= env
.newCollation
;
2446 int newCodePage
= GetCodePage(env
.newCollation
, stateObj
);
2447 if (newCodePage
!= _defaultCodePage
) {
2448 _defaultCodePage
= newCodePage
;
2449 _defaultEncoding
= System
.Text
.Encoding
.GetEncoding(_defaultCodePage
);
2451 _defaultLCID
= env
.newCollation
.LCID
;
2454 if (!stateObj
.TryReadByte(out byteLength
)) {
2457 env
.oldLength
= byteLength
;
2458 Debug
.Assert(env
.oldLength
== 5 || env
.oldLength
== 0, "Improper length in old collation!");
2459 if (env
.oldLength
== 5) {
2460 if (!TryProcessCollation(stateObj
, out env
.oldCollation
)) {
2465 env
.length
= 3 + env
.newLength
+ env
.oldLength
;
2468 case TdsEnums
.ENV_BEGINTRAN
:
2469 case TdsEnums
.ENV_COMMITTRAN
:
2470 case TdsEnums
.ENV_ROLLBACKTRAN
:
2471 case TdsEnums
.ENV_ENLISTDTC
:
2472 case TdsEnums
.ENV_DEFECTDTC
:
2473 case TdsEnums
.ENV_TRANSACTIONENDED
:
2474 Debug
.Assert(_isYukon
, "Received new ENVCHANGE transaction/DTC token on pre 9.0 server!");
2476 if (!stateObj
.TryReadByte(out byteLength
)) {
2479 env
.newLength
= byteLength
;
2480 Debug
.Assert(env
.newLength
== 0 || env
.newLength
== 8, "Improper length for new transaction id!");
2482 if (env
.newLength
> 0) {
2483 if (!stateObj
.TryReadInt64(out env
.newLongValue
)) {
2486 Debug
.Assert(env
.newLongValue
!= SqlInternalTransaction
.NullTransactionId
, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
2489 env
.newLongValue
= SqlInternalTransaction
.NullTransactionId
; // the server guarantees that zero is an invalid transaction id.
2492 if (!stateObj
.TryReadByte(out byteLength
)) {
2495 env
.oldLength
= byteLength
;
2496 Debug
.Assert(env
.oldLength
== 0 || env
.oldLength
== 8, "Improper length for old transaction id!");
2498 if (env
.oldLength
> 0) {
2499 if (!stateObj
.TryReadInt64(out env
.oldLongValue
)) {
2502 Debug
.Assert(env
.oldLongValue
!= SqlInternalTransaction
.NullTransactionId
, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id.
2505 env
.oldLongValue
= SqlInternalTransaction
.NullTransactionId
; // the server guarantees that zero is an invalid transaction id.
2508 // env.length includes 1 byte type token
2509 env
.length
= 3 + env
.newLength
+ env
.oldLength
;
2512 case TdsEnums
.ENV_LOGSHIPNODE
:
2513 // env.newBinValue is secondary node, env.oldBinValue is witness node
2514 // comes before LoginAck so we can't assert this
2515 if (!TryReadTwoStringFields(env
, stateObj
)) {
2520 case TdsEnums
.ENV_PROMOTETRANSACTION
:
2521 Debug
.Assert(_isYukon
, "Received new ENVCHANGE tokens on pre 9.0 server!");
2523 if (!stateObj
.TryReadInt32(out env
.newLength
)) { // new value has 4 byte length
2526 env
.newBinValue
= new byte[env
.newLength
];
2527 if (!stateObj
.TryReadByteArray(env
.newBinValue
, 0, env
.newLength
)) { // read new value with 4 byte length
2531 if (!stateObj
.TryReadByte(out byteLength
)) {
2534 env
.oldLength
= byteLength
;
2535 Debug
.Assert(0 == env
.oldLength
, "old length should be zero");
2537 // env.length includes 1 byte for type token
2538 env
.length
= 5 + env
.newLength
;
2541 case TdsEnums
.ENV_TRANSACTIONMANAGERADDRESS
:
2542 case TdsEnums
.ENV_SPRESETCONNECTIONACK
:
2544 Debug
.Assert(_isYukon
, "Received new ENVCHANGE tokens on pre 9.0 server!");
2545 if (!TryReadTwoBinaryFields(env
, stateObj
)) {
2550 case TdsEnums
.ENV_USERINSTANCE
:
2551 Debug
.Assert(!_isYukon
, "Received ENV_USERINSTANCE on non 9.0 server!");
2552 if (!TryReadTwoStringFields(env
, stateObj
)) {
2557 case TdsEnums
.ENV_ROUTING
:
2559 if (!stateObj
.TryReadUInt16(out newLength
)) {
2562 env
.newLength
= newLength
;
2564 if (!stateObj
.TryReadByte(out protocol
)) {
2568 if (!stateObj
.TryReadUInt16(out port
)) {
2572 if (!stateObj
.TryReadUInt16(out serverLen
)) {
2576 if (!stateObj
.TryReadString(serverLen
, out serverName
)) {
2579 env
.newRoutingInfo
= new RoutingInfo(protocol
, port
, serverName
);
2581 if (!stateObj
.TryReadUInt16(out oldLength
)) {
2584 if (!stateObj
.TrySkipBytes(oldLength
)) {
2587 env
.length
= env
.newLength
+ oldLength
+ 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength]
2591 Debug
.Assert(false, "Unknown environment change token: " + env
.type
);
2594 processedLength
+= env
.length
;
2597 sqlEnvChange
= envarray
;
2601 private bool TryReadTwoBinaryFields(SqlEnvChange env
, TdsParserStateObject stateObj
) {
2602 // Used by ProcessEnvChangeToken
2604 if (!stateObj
.TryReadByte(out byteLength
)) {
2607 env
.newLength
= byteLength
;
2608 env
.newBinValue
= new byte[env
.newLength
];
2609 if (!stateObj
.TryReadByteArray(env
.newBinValue
, 0, env
.newLength
)) {
2612 if (!stateObj
.TryReadByte(out byteLength
)) {
2615 env
.oldLength
= byteLength
;
2616 env
.oldBinValue
= new byte[env
.oldLength
];
2617 if (!stateObj
.TryReadByteArray(env
.oldBinValue
, 0, env
.oldLength
)) {
2621 // env.length includes 1 byte type token
2622 env
.length
= 3 + env
.newLength
+ env
.oldLength
;
2626 private bool TryReadTwoStringFields(SqlEnvChange env
, TdsParserStateObject stateObj
) {
2627 // Used by ProcessEnvChangeToken
2628 byte newLength
, oldLength
;
2629 string newValue
, oldValue
;
2630 if (!stateObj
.TryReadByte(out newLength
)) {
2633 if (!stateObj
.TryReadString(newLength
, out newValue
)) {
2636 if (!stateObj
.TryReadByte(out oldLength
)) {
2639 if (!stateObj
.TryReadString(oldLength
, out oldValue
)) {
2643 env
.newLength
= newLength
;
2644 env
.newValue
= newValue
;
2645 env
.oldLength
= oldLength
;
2646 env
.oldValue
= oldValue
;
2648 // env.length includes 1 byte type token
2649 env
.length
= 3 + env
.newLength
* 2 + env
.oldLength
* 2;
2653 private bool TryProcessDone(SqlCommand cmd
, SqlDataReader reader
, ref RunBehavior run
, TdsParserStateObject stateObj
) {
2658 // Can't retry TryProcessDone
2659 stateObj
._syncOverAsync
= true;
2663 // rowcount (valid only if DONE_COUNT bit is set)
2665 if (!stateObj
.TryReadUInt16(out status
)) {
2668 if (!stateObj
.TryReadUInt16(out curCmd
)) {
2674 if (!stateObj
.TryReadInt64(out longCount
)) {
2677 count
= (int) longCount
;
2680 if (!stateObj
.TryReadInt32(out count
)) {
2683 // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
2684 // In that case we still have to read another 4 bytes
2685 // But don't try to read beyond the TDS stream in this case, because it generates errors if login failed.
2686 if ( _state
== TdsParserState
.OpenNotLoggedIn
) {
2687 // Login incomplete, if we are reading from Yukon we need to read another int
2688 if (stateObj
._inBytesRead
> stateObj
._inBytesUsed
) {
2690 if (!stateObj
.TryPeekByte(out b
)) {
2694 // This is an invalid token value
2695 if (!stateObj
.TryReadInt32(out count
)) {
2703 // We get a done token with the attention bit set
2704 if (TdsEnums
.DONE_ATTN
== (status
& TdsEnums
.DONE_ATTN
)) {
2705 Debug
.Assert(TdsEnums
.DONE_MORE
!= (status
& TdsEnums
.DONE_MORE
),"Not expecting DONE_MORE when receiving DONE_ATTN");
2706 Debug
.Assert(stateObj
._attentionSent
, "Received attention done without sending one!");
2707 stateObj
._attentionReceived
= true;
2708 Debug
.Assert(stateObj
._inBytesUsed
== stateObj
._inBytesRead
&& stateObj
._inBytesPacket
== 0, "DONE_ATTN received with more data left on wire");
2710 if ((null != cmd
) && (TdsEnums
.DONE_COUNT
== (status
& TdsEnums
.DONE_COUNT
))) {
2711 if (curCmd
!= TdsEnums
.SELECT
) {
2712 if (cmd
.IsDescribeParameterEncryptionRPCCurrentlyInProgress
) {
2713 // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
2714 cmd
.RowsAffectedByDescribeParameterEncryption
= count
;
2717 cmd
.InternalRecordsAffected
= count
;
2720 // Skip the bogus DONE counts sent by the server
2721 if (stateObj
._receivedColMetaData
|| (curCmd
!= TdsEnums
.SELECT
)) {
2722 cmd
.OnStatementCompleted(count
);
2726 stateObj
._receivedColMetaData
= false;
2728 // Surface exception for DONE_ERROR in the case we did not receive an error token
2729 // in the stream, but an error occurred. In these cases, we throw a general server error. The
2730 // situations where this can occur are: an invalid buffer received from client, login error
2731 // and the server refused our connection, and the case where we are trying to log in but
2732 // the server has reached its max connection limit. Bottom line, we need to throw general
2733 // error in the cases where we did not receive a error token along with the DONE_ERROR.
2734 if ((TdsEnums
.DONE_ERROR
== (TdsEnums
.DONE_ERROR
& status
)) && stateObj
.ErrorCount
== 0 &&
2735 stateObj
._errorTokenReceived
== false && (RunBehavior
.Clean
!= (RunBehavior
.Clean
& run
))) {
2736 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.MIN_ERROR_CLASS
, _server
, SQLMessage
.SevereError(), "", 0));
2738 if (null != reader
) { // SQL BU DT 269516
2739 if (!reader
.IsInitialized
) {
2740 run
= RunBehavior
.UntilDone
;
2745 // Similar to above, only with a more severe error. In this case, if we received
2746 // the done_srverror, this exception will be added to the collection regardless.
2747 // MDAC #93896. Also, per Ashwin, the server will always break the connection in this case.
2748 if ((TdsEnums
.DONE_SRVERROR
== (TdsEnums
.DONE_SRVERROR
& status
)) && (RunBehavior
.Clean
!= (RunBehavior
.Clean
& run
))) {
2749 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.FATAL_ERROR_CLASS
, _server
, SQLMessage
.SevereError(), "", 0));
2751 if (null != reader
) { // SQL BU DT 269516
2752 if (!reader
.IsInitialized
) {
2753 run
= RunBehavior
.UntilDone
;
2758 ProcessSqlStatistics(curCmd
, status
, count
);
2760 // stop if the DONE_MORE bit isn't set (see above for attention handling)
2761 if (TdsEnums
.DONE_MORE
!= (status
& TdsEnums
.DONE_MORE
)) {
2762 stateObj
._errorTokenReceived
= false;
2763 if (stateObj
._inBytesUsed
>= stateObj
._inBytesRead
) {
2764 stateObj
._pendingData
= false;
2768 // _pendingData set by e.g. 'TdsExecuteSQLBatch'
2769 // _hasOpenResult always set to true by 'WriteMarsHeader'
2771 if (!stateObj
._pendingData
&& stateObj
._hasOpenResult
) {
2773 Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) ||
2774 (_userStartedLocalTransaction != null && _distributedTransaction != null))
2775 , "ProcessDone - have both distributed and local transactions not null!");
2776 */ // WebData 112722
2778 stateObj
.DecrementOpenResultCount();
2784 private void ProcessSqlStatistics(ushort curCmd
, ushort status
, int count
) {
2785 // SqlStatistics bookkeeping stuff
2787 if (null != _statistics
) {
2788 // any done after row(s) counts as a resultset
2789 if (_statistics
.WaitForDoneAfterRow
) {
2790 _statistics
.SafeIncrement(ref _statistics
._sumResultSets
);
2791 _statistics
.WaitForDoneAfterRow
= false;
2794 // clear row count DONE_COUNT flag is not set
2795 if (!(TdsEnums
.DONE_COUNT
== (status
& TdsEnums
.DONE_COUNT
))) {
2800 case TdsEnums
.INSERT
:
2801 case TdsEnums
.DELETE
:
2802 case TdsEnums
.UPDATE
:
2803 case TdsEnums
.MERGE
:
2804 _statistics
.SafeIncrement(ref _statistics
._iduCount
);
2805 _statistics
.SafeAdd(ref _statistics
._iduRows
, count
);
2806 if (!_statisticsIsInTransaction
) {
2807 _statistics
.SafeIncrement(ref _statistics
._transactions
);
2812 case TdsEnums
.SELECT
:
2813 _statistics
.SafeIncrement(ref _statistics
._selectCount
);
2814 _statistics
.SafeAdd(ref _statistics
._selectRows
, count
);
2817 case TdsEnums
.BEGINXACT
:
2818 if (!_statisticsIsInTransaction
) {
2819 _statistics
.SafeIncrement(ref _statistics
._transactions
);
2821 _statisticsIsInTransaction
= true;
2824 case TdsEnums
.OPENCURSOR
:
2825 _statistics
.SafeIncrement(ref _statistics
._cursorOpens
);
2828 case TdsEnums
.ABORT
:
2829 _statisticsIsInTransaction
= false;
2832 case TdsEnums
.ENDXACT
:
2833 _statisticsIsInTransaction
= false;
2839 case TdsEnums
.BEGINXACT
:
2840 _statisticsIsInTransaction
= true;
2843 case TdsEnums
.ABORT
:
2844 case TdsEnums
.ENDXACT
:
2845 _statisticsIsInTransaction
= false;
2851 private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj
) {
2855 if (!stateObj
.TryReadByte(out featureId
)) {
2858 if (featureId
!= TdsEnums
.FEATUREEXT_TERMINATOR
) {
2860 if (!stateObj
.TryReadUInt32(out dataLen
)) {
2863 byte[] data
= new byte[dataLen
];
2865 if (!stateObj
.TryReadByteArray(data
, 0, checked ((int)dataLen
))) {
2869 _connHandler
.OnFeatureExtAck(featureId
, data
);
2871 } while (featureId
!= TdsEnums
.FEATUREEXT_TERMINATOR
);
2873 // Check if column encryption was on and feature wasn't acknowledged.
2874 if (_connHandler
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
&& !IsColumnEncryptionSupported
) {
2875 throw SQL
.TceNotSupported ();
2881 private bool TryProcessSessionState(TdsParserStateObject stateObj
, int length
, SessionData sdata
) {
2883 throw SQL
.ParsingErrorLength(ParsingErrorState
.SessionStateLengthTooShort
, length
);
2886 if (!stateObj
.TryReadUInt32(out seqNum
)) {
2889 if (seqNum
== UInt32
.MaxValue
) {
2890 _connHandler
.DoNotPoolThisConnection();
2893 if (!stateObj
.TryReadByte(out status
)) {
2897 throw SQL
.ParsingErrorStatus(ParsingErrorState
.SessionStateInvalidStatus
, status
);
2899 bool recoverable
= status
!= 0;
2901 while (length
> 0) {
2903 if (!stateObj
.TryReadByte(out stateId
)) {
2908 if (!stateObj
.TryReadByte(out stateLenByte
)) {
2911 if (stateLenByte
< 0xFF) {
2912 stateLen
= stateLenByte
;
2915 if (!stateObj
.TryReadInt32(out stateLen
)) {
2919 byte[] buffer
= null;
2920 lock (sdata
._delta
) {
2921 if (sdata
._delta
[stateId
] == null) {
2922 buffer
= new byte[stateLen
];
2923 sdata
._delta
[stateId
] = new SessionStateRecord { _version = seqNum, _dataLength = stateLen, _data = buffer, _recoverable = recoverable }
;
2924 sdata
._deltaDirty
= true;
2926 checked { sdata._unrecoverableStatesCount++; }
2930 if (sdata
._delta
[stateId
]._version
<= seqNum
) {
2931 SessionStateRecord sv
= sdata
._delta
[stateId
];
2932 sv
._version
= seqNum
;
2933 sv
._dataLength
= stateLen
;
2934 if (sv
._recoverable
!= recoverable
) {
2936 Debug
.Assert(sdata
._unrecoverableStatesCount
> 0, "Unrecoverable states count >0");
2937 sdata
._unrecoverableStatesCount
--;
2940 checked { sdata._unrecoverableStatesCount++; }
2942 sv
._recoverable
= recoverable
;
2945 if (buffer
.Length
< stateLen
) {
2946 buffer
= new byte[stateLen
];
2952 if (buffer
!= null) {
2953 if (!stateObj
.TryReadByteArray(buffer
, 0, stateLen
)) {
2958 if (!stateObj
.TrySkipBytes(stateLen
))
2962 if (stateLenByte
< 0xFF) {
2963 length
-= 2 + stateLen
;
2966 length
-= 6 + stateLen
;
2969 sdata
.AssertUnrecoverableStateCountIsCorrect();
2974 private bool TryProcessLoginAck(TdsParserStateObject stateObj
, out SqlLoginAck sqlLoginAck
) {
2975 SqlLoginAck a
= new SqlLoginAck();
2979 // read past interface type and version
2980 if (!stateObj
.TrySkipBytes(1)) {
2984 byte[] b
= new byte[TdsEnums
.VERSION_SIZE
];
2985 if (!stateObj
.TryReadByteArray(b
, 0, b
.Length
)) {
2988 a
.tdsVersion
= (UInt32
)((((((b
[0]<<8)|b
[1])<<8)|b
[2])<<8)|b
[3]); // bytes are in motorola order (high byte first)
2989 UInt32 majorMinor
= a
.tdsVersion
& 0xff00ffff;
2990 UInt32 increment
= (a
.tdsVersion
>> 16) & 0xff;
2993 // 0x07000000 -> Sphinx // Notice server response format is different for bwd compat
2994 // 0x07010000 -> Shiloh RTM // Notice server response format is different for bwd compat
2995 // 0x71000001 -> Shiloh SP1
2996 // 0x72xx0002 -> Yukon RTM
2997 // information provided by S. Ashwin
2999 switch (majorMinor
) {
3000 case TdsEnums
.SPHINXORSHILOH_MAJOR
<<24|TdsEnums
.DEFAULT_MINOR
: // Sphinx & Shiloh RTM
3001 // note that sphinx and shiloh_rtm can only be distinguished by the increment
3002 switch (increment
) {
3003 case TdsEnums
.SHILOH_INCREMENT
:
3006 case TdsEnums
.SPHINX_INCREMENT
:
3007 // no flag will be set
3010 throw SQL
.InvalidTDSVersion();
3013 case TdsEnums
.SHILOHSP1_MAJOR
<<24|TdsEnums
.SHILOHSP1_MINOR
: // Shiloh SP1
3014 if (increment
!= TdsEnums
.SHILOHSP1_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3015 _isShilohSP1
= true;
3017 case TdsEnums
.YUKON_MAJOR
<<24|TdsEnums
.YUKON_RTM_MINOR
: // Yukon
3018 if (increment
!= TdsEnums
.YUKON_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3021 case TdsEnums
.KATMAI_MAJOR
<<24|TdsEnums
.KATMAI_MINOR
:
3022 if (increment
!= TdsEnums
.KATMAI_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3025 case TdsEnums
.DENALI_MAJOR
<< 24|TdsEnums
.DENALI_MINOR
:
3026 if (increment
!= TdsEnums
.DENALI_INCREMENT
) { throw SQL.InvalidTDSVersion(); }
3030 throw SQL
.InvalidTDSVersion();
3033 _isKatmai
|= _isDenali
;
3034 _isYukon
|= _isKatmai
;
3035 _isShilohSP1
|= _isYukon
; // includes all lower versions
3036 _isShiloh
|= _isShilohSP1
; //
3038 a
.isVersion8
= _isShiloh
;
3040 stateObj
._outBytesUsed
= stateObj
._outputHeaderLen
;
3042 if (!stateObj
.TryReadByte(out len
)) {
3046 if (!stateObj
.TryReadString(len
, out a
.programName
)) {
3049 if (!stateObj
.TryReadByte(out a
.majorVersion
)) {
3052 if (!stateObj
.TryReadByte(out a
.minorVersion
)) {
3055 byte buildNumHi
, buildNumLo
;
3056 if (!stateObj
.TryReadByte(out buildNumHi
)) {
3059 if (!stateObj
.TryReadByte(out buildNumLo
)) {
3063 a
.buildNum
= (short)((buildNumHi
<< 8) + buildNumLo
);
3065 Debug
.Assert(_state
== TdsParserState
.OpenNotLoggedIn
, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn");
3066 _state
= TdsParserState
.OpenLoggedIn
;
3070 _resetConnectionEvent
= new AutoResetEvent(true);
3074 // Fail if SSE UserInstance and we have not received this info.
3075 if ( _connHandler
.ConnectionOptions
.UserInstance
&&
3076 ADP
.IsEmpty(_connHandler
.InstanceName
)) {
3077 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.FATAL_ERROR_CLASS
, Server
, SQLMessage
.UserInstanceFailure(), "", 0));
3078 ThrowExceptionAndWarning(stateObj
);
3085 private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj
, int tokenLen
, out SqlFedAuthInfo sqlFedAuthInfo
) {
3086 sqlFedAuthInfo
= null;
3087 SqlFedAuthInfo tempFedAuthInfo
= new SqlFedAuthInfo();
3089 // Skip reading token length, since it has already been read in caller
3091 if (Bid
.AdvancedOn
) {
3092 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FEDAUTHINFO token stream length = {0}\n", tokenLen
);
3095 if (tokenLen
< sizeof(uint)) {
3096 // the token must at least contain a DWORD indicating the number of info IDs
3097 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream length too short for CountOfInfoIDs.\n");
3098 throw SQL
.ParsingErrorLength(ParsingErrorState
.FedAuthInfoLengthTooShortForCountOfInfoIds
, tokenLen
);
3101 // read how many FedAuthInfo options there are
3103 if (!stateObj
.TryReadUInt32(out optionsCount
)) {
3104 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read CountOfInfoIDs in FEDAUTHINFO token stream.\n");
3105 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoFailedToReadCountOfInfoIds
);
3107 tokenLen
-= sizeof(uint); // remaining length is shortened since we read optCount
3109 if (Bid
.AdvancedOn
) {
3110 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> CountOfInfoIDs = {0}\n", optionsCount
.ToString(CultureInfo
.InvariantCulture
));
3114 // read the rest of the token
3115 byte[] tokenData
= new byte[tokenLen
];
3117 bool successfulRead
= stateObj
.TryReadByteArray(tokenData
, 0, tokenLen
, out totalRead
);
3119 if (Bid
.AdvancedOn
) {
3120 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Read rest of FEDAUTHINFO token stream: {0}\n", BitConverter
.ToString(tokenData
, 0, totalRead
));
3123 if (!successfulRead
|| totalRead
!= tokenLen
) {
3124 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read FEDAUTHINFO token stream. Attempted to read {0} bytes, actually read {1}\n", tokenLen
, totalRead
);
3125 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoFailedToReadTokenStream
);
3128 // each FedAuthInfoOpt is 9 bytes:
3129 // 1 byte for FedAuthInfoID
3130 // 4 bytes for FedAuthInfoDataLen
3131 // 4 bytes for FedAuthInfoDataOffset
3132 // So this is the index in tokenData for the i-th option
3133 const uint optionSize
= 9;
3135 // the total number of bytes for all FedAuthInfoOpts together
3136 uint totalOptionsSize
= checked(optionsCount
* optionSize
);
3138 for (uint i
= 0; i
< optionsCount
; i
++) {
3139 uint currentOptionOffset
= checked(i
* optionSize
);
3141 byte id
= tokenData
[currentOptionOffset
];
3142 uint dataLen
= BitConverter
.ToUInt32(tokenData
, checked((int)(currentOptionOffset
+ 1)));
3143 uint dataOffset
= BitConverter
.ToUInt32(tokenData
, checked((int)(currentOptionOffset
+ 5)));
3145 if (Bid
.AdvancedOn
) {
3146 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}\n", id
, dataLen
.ToString(CultureInfo
.InvariantCulture
), dataOffset
.ToString(CultureInfo
.InvariantCulture
));
3149 // offset is measured from optCount, so subtract to make offset measured
3150 // from the beginning of tokenData
3152 dataOffset
-= sizeof(uint);
3155 // if dataOffset points to a region within FedAuthInfoOpt or after the end of the token, throw
3156 if (dataOffset
< totalOptionsSize
|| dataOffset
>= tokenLen
) {
3157 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FedAuthInfoDataOffset points to an invalid location.\n");
3158 throw SQL
.ParsingErrorOffset(ParsingErrorState
.FedAuthInfoInvalidOffset
, unchecked((int)dataOffset
));
3161 // try to read data and throw if the arguments are bad, meaning the server sent us a bad token
3164 data
= System
.Text
.Encoding
.Unicode
.GetString(tokenData
, checked((int)dataOffset
), checked((int)dataLen
));
3166 catch (ArgumentOutOfRangeException e
) {
3167 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> Failed to read FedAuthInfoData.\n");
3168 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoFailedToReadData
, e
);
3170 catch (ArgumentException e
) {
3171 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FedAuthInfoData is not in unicode format.\n");
3172 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoDataNotUnicode
, e
);
3175 if (Bid
.AdvancedOn
) {
3176 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoData: {0}\n", data
);
3179 // store data in tempFedAuthInfo
3180 switch ((TdsEnums
.FedAuthInfoId
)id
) {
3181 case TdsEnums
.FedAuthInfoId
.Spn
:
3182 tempFedAuthInfo
.spn
= data
;
3184 case TdsEnums
.FedAuthInfoId
.Stsurl
:
3185 tempFedAuthInfo
.stsurl
= data
;
3188 if (Bid
.AdvancedOn
) {
3189 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Ignoring unknown federated authentication info option: {0}\n", id
);
3196 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream is not long enough to contain the data it claims to.\n");
3197 throw SQL
.ParsingErrorLength(ParsingErrorState
.FedAuthInfoLengthTooShortForData
, tokenLen
);
3200 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo> Processed FEDAUTHINFO token stream: {0}\n", tempFedAuthInfo
.ToString());
3202 if (String
.IsNullOrWhiteSpace(tempFedAuthInfo
.stsurl
) || String
.IsNullOrWhiteSpace(tempFedAuthInfo
.spn
)) {
3203 // We should be receiving both stsurl and spn
3204 Bid
.Trace("<sc.TdsParser.TryProcessFedAuthInfo|ERR> FEDAUTHINFO token stream does not contain both STSURL and SPN.\n");
3205 throw SQL
.ParsingError(ParsingErrorState
.FedAuthInfoDoesNotContainStsurlAndSpn
);
3208 sqlFedAuthInfo
= tempFedAuthInfo
;
3212 internal bool TryProcessError(byte token
, TdsParserStateObject stateObj
, out SqlError error
) {
3221 if (!stateObj
.TryReadInt32(out number
)) {
3224 if (!stateObj
.TryReadByte(out state
)) {
3227 if (!stateObj
.TryReadByte(out errorClass
)) {
3231 Debug
.Assert(((errorClass
>= TdsEnums
.MIN_ERROR_CLASS
) && token
== TdsEnums
.SQLERROR
) ||
3232 ((errorClass
< TdsEnums
.MIN_ERROR_CLASS
) && token
== TdsEnums
.SQLINFO
), "class and token don't match!");
3234 if (!stateObj
.TryReadUInt16(out shortLen
)) {
3238 if (!stateObj
.TryReadString(shortLen
, out message
)) {
3242 if (!stateObj
.TryReadByte(out byteLen
)) {
3248 // MDAC bug #49307 - server sometimes does not send over server field! In those cases
3249 // we will use our locally cached value.
3254 if (!stateObj
.TryReadString(byteLen
, out server
)) {
3259 if (!stateObj
.TryReadByte(out byteLen
)) {
3263 if (!stateObj
.TryReadString(byteLen
, out procedure
)) {
3269 if (!stateObj
.TryReadInt32(out line
)) {
3275 if (!stateObj
.TryReadUInt16(out shortLine
)) {
3279 // If we haven't yet completed processing login token stream yet, we may be talking to a Yukon server
3280 // In that case we still have to read another 2 bytes
3281 if ( _state
== TdsParserState
.OpenNotLoggedIn
) {
3284 if (!stateObj
.TryPeekByte(out b
)) {
3288 // This is an invalid token value
3290 if (!stateObj
.TryReadUInt16(out value)) {
3293 line
= (line
<< 16) + value;
3298 error
= new SqlError(number
, state
, errorClass
, _server
, message
, procedure
, line
);
3303 internal bool TryProcessReturnValue(int length
,
3304 TdsParserStateObject stateObj
,
3305 out SqlReturnValue returnValue
,
3306 SqlCommandColumnEncryptionSetting columnEncryptionSetting
) {
3308 SqlReturnValue rec
= new SqlReturnValue();
3309 rec
.length
= length
; // In Yukon this length is -1
3311 if (!stateObj
.TryReadUInt16(out rec
.parmIndex
)) {
3316 if (!stateObj
.TryReadByte(out len
)) { // Length of parameter name
3320 rec
.parameter
= null;
3322 if (!stateObj
.TryReadString(len
, out rec
.parameter
)) {
3327 // read status and ignore
3329 if (!stateObj
.TryReadByte(out ignored
)) {
3335 // read user type - 4 bytes Yukon, 2 backwards
3336 if (IsYukonOrNewer
) {
3337 if (!stateObj
.TryReadUInt32(out userType
)) {
3342 ushort userTypeShort
;
3343 if (!stateObj
.TryReadUInt16(out userTypeShort
)) {
3346 userType
= userTypeShort
;
3349 // Read off the flags.
3350 // The first byte is ignored since it doesn't contain any interesting information.
3352 if (!stateObj
.TryReadByte(out flags
)) {
3356 if (!stateObj
.TryReadByte(out flags
)) {
3360 // Check if the column is encrypted.
3361 if (_serverSupportsColumnEncryption
) {
3362 rec
.isEncrypted
= (TdsEnums
.IsEncrypted
== (flags
& TdsEnums
.IsEncrypted
));
3367 if (!stateObj
.TryReadByte(out tdsType
)) {
3372 // For xml datatpyes, there is no tokenLength
3375 if (tdsType
== TdsEnums
.SQLXMLTYPE
) {
3376 tdsLen
= TdsEnums
.SQL_USHORTVARMAXLEN
;
3378 else if (IsVarTimeTds(tdsType
))
3379 tdsLen
= 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
3380 else if (tdsType
== TdsEnums
.SQLDATE
) {
3384 if (!TryGetTokenLength(tdsType
, stateObj
, out tdsLen
)) {
3389 rec
.metaType
= MetaType
.GetSqlDataType(tdsType
, userType
, tdsLen
);
3390 rec
.type
= rec
.metaType
.SqlDbType
;
3392 // always use the nullable type for parameters if Shiloh or later
3393 // Sphinx sometimes sends fixed length return values
3395 rec
.tdsType
= rec
.metaType
.NullableType
;
3396 rec
.isNullable
= true;
3397 if (tdsLen
== TdsEnums
.SQL_USHORTVARMAXLEN
) {
3398 Debug
.Assert(_isYukon
, "plp data from pre-Yukon server");
3399 rec
.metaType
= MetaType
.GetMaxMetaTypeFromMetaType(rec
.metaType
);
3402 else { // For sphinx, keep the fixed type if that is what is returned
3403 if (rec
.metaType
.NullableType
== tdsType
)
3404 rec
.isNullable
= true;
3406 rec
.tdsType
= (byte)tdsType
;
3409 if (rec
.type
== SqlDbType
.Decimal
) {
3410 if (!stateObj
.TryReadByte(out rec
.precision
)) {
3413 if (!stateObj
.TryReadByte(out rec
.scale
)) {
3418 if (rec
.metaType
.IsVarTime
) {
3419 if (!stateObj
.TryReadByte(out rec
.scale
)) {
3424 if (tdsType
== TdsEnums
.SQLUDT
) {
3425 if (!TryProcessUDTMetaData((SqlMetaDataPriv
) rec
, stateObj
)) {
3430 if (rec
.type
== SqlDbType
.Xml
) {
3433 if (!stateObj
.TryReadByte(out schemapresent
)) {
3437 if ((schemapresent
& 1) != 0) {
3438 if (!stateObj
.TryReadByte(out len
)) {
3442 if (!stateObj
.TryReadString(len
, out rec
.xmlSchemaCollectionDatabase
)) {
3447 if (!stateObj
.TryReadByte(out len
)) {
3451 if (!stateObj
.TryReadString(len
, out rec
.xmlSchemaCollectionOwningSchema
)) {
3457 if (!stateObj
.TryReadInt16(out slen
)) {
3462 if (!stateObj
.TryReadString(slen
, out rec
.xmlSchemaCollectionName
)) {
3469 else if (_isShiloh
&& rec
.metaType
.IsCharType
) {
3470 // read the collation for 8.x servers
3471 if (!TryProcessCollation(stateObj
, out rec
.collation
)) {
3475 int codePage
= GetCodePage(rec
.collation
, stateObj
);
3477 // if the column lcid is the same as the default, use the default encoder
3478 if (codePage
== _defaultCodePage
) {
3479 rec
.codePage
= _defaultCodePage
;
3480 rec
.encoding
= _defaultEncoding
;
3483 rec
.codePage
= codePage
;
3484 rec
.encoding
= System
.Text
.Encoding
.GetEncoding(rec
.codePage
);
3488 // For encrypted parameters, read the unencrypted type and encryption information.
3489 if (_serverSupportsColumnEncryption
&& rec
.isEncrypted
) {
3490 if (!TryProcessTceCryptoMetadata(stateObj
, rec
, cipherTable
: null, columnEncryptionSetting
: columnEncryptionSetting
, isReturnValue
: true)) {
3495 // for now we coerce return values into a SQLVariant, not good...
3496 bool isNull
= false;
3498 if (!TryProcessColumnHeaderNoNBC(rec
, stateObj
, out isNull
, out valLen
)) {
3502 // always read as sql types
3503 Debug
.Assert(valLen
< (ulong)(Int32
.MaxValue
), "ProcessReturnValue received data size > 2Gb");
3505 int intlen
= valLen
> (ulong)(Int32
.MaxValue
) ? Int32
.MaxValue
: (int)valLen
;
3507 if (rec
.metaType
.IsPlp
) {
3508 intlen
= Int32
.MaxValue
; // If plp data, read it all
3512 GetNullSqlValue(rec
.value, rec
, SqlCommandColumnEncryptionSetting
.Disabled
, _connHandler
);
3515 // We should never do any decryption here, so pass disabled as the command encryption override.
3516 // We only read the binary value and decryption will be performed by OnReturnValue().
3517 if (!TryReadSqlValue(rec
.value, rec
, intlen
, stateObj
, SqlCommandColumnEncryptionSetting
.Disabled
, columnName
:null /*Not used*/)) {
3526 internal bool TryProcessTceCryptoMetadata (TdsParserStateObject stateObj
,
3527 SqlMetaDataPriv col
,
3528 SqlTceCipherInfoTable
? cipherTable
,
3529 SqlCommandColumnEncryptionSetting columnEncryptionSetting
,
3530 bool isReturnValue
) {
3531 Debug
.Assert(isReturnValue
== (cipherTable
== null), "Ciphertable is not set iff this is a return value");
3533 // Read the ordinal into cipher table
3537 // For return values there is not cipher table and no ordinal.
3538 if (cipherTable
.HasValue
) {
3539 if (!stateObj
.TryReadUInt16(out index
)) {
3543 // validate the index (ordinal passed)
3544 if (index
>= cipherTable
.Value
.Size
) {
3545 Bid
.Trace("<sc.TdsParser.TryProcessTceCryptoMetadata|TCE> Incorrect ordinal received %d, max tab size: %d\n", index
, cipherTable
.Value
.Size
);
3546 throw SQL
.ParsingErrorValue(ParsingErrorState
.TceInvalidOrdinalIntoCipherInfoTable
, index
);
3550 // Read the user type
3551 if (!stateObj
.TryReadUInt32(out userType
)) {
3555 // Read the base TypeInfo
3556 col
.baseTI
= new SqlMetaDataPriv();
3557 if (!TryProcessTypeInfo(stateObj
, col
.baseTI
, userType
)) {
3561 // Read the cipher algorithm Id
3562 byte cipherAlgorithmId
;
3563 if (!stateObj
.TryReadByte(out cipherAlgorithmId
)) {
3567 string cipherAlgorithmName
= null;
3568 if (TdsEnums
.CustomCipherAlgorithmId
== cipherAlgorithmId
) {
3569 // Custom encryption algorithm, read the name
3571 if (!stateObj
.TryReadByte(out nameSize
)) {
3575 if (!stateObj
.TryReadString(nameSize
, out cipherAlgorithmName
)) {
3580 // Read Encryption Type.
3581 byte encryptionType
;
3582 if (!stateObj
.TryReadByte(out encryptionType
)) {
3586 // Read Normalization Rule Version.
3587 byte normalizationRuleVersion
;
3588 if (!stateObj
.TryReadByte(out normalizationRuleVersion
)) {
3592 Debug
.Assert(col
.cipherMD
== null, "col.cipherMD should be null in TryProcessTceCryptoMetadata.");
3594 // Check if TCE is enable and if it is set the crypto MD for the column.
3595 // TCE is enabled if the command is set to enabled or to resultset only and this is not a return value
3596 // or if it is set to use connection setting and the connection has TCE enabled.
3597 if ((columnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.Enabled
||
3598 (columnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.ResultSetOnly
&& !isReturnValue
)) ||
3599 (columnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.UseConnectionSetting
&&
3600 _connHandler
!= null && _connHandler
.ConnectionOptions
!= null &&
3601 _connHandler
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
)) {
3602 col
.cipherMD
= new SqlCipherMetadata(cipherTable
.HasValue
? (SqlTceCipherInfoEntry
?)cipherTable
.Value
[index
] : null,
3604 cipherAlgorithmId: cipherAlgorithmId
,
3605 cipherAlgorithmName: cipherAlgorithmName
,
3606 encryptionType: encryptionType
,
3607 normalizationRuleVersion: normalizationRuleVersion
);
3610 // If TCE is disabled mark the MD as not encrypted.
3611 col
.isEncrypted
= false;
3617 internal bool TryProcessCollation(TdsParserStateObject stateObj
, out SqlCollation collation
) {
3618 SqlCollation newCollation
= new SqlCollation();
3620 if (!stateObj
.TryReadUInt32(out newCollation
.info
)) {
3624 if (!stateObj
.TryReadByte(out newCollation
.sortId
)) {
3629 collation
= newCollation
;
3633 private void WriteCollation(SqlCollation collation
, TdsParserStateObject stateObj
) {
3634 if (collation
== null) {
3635 _physicalStateObj
.WriteByte(0);
3638 _physicalStateObj
.WriteByte(sizeof(UInt32
)+sizeof(byte));
3639 WriteUnsignedInt(collation
.info
, _physicalStateObj
);
3640 _physicalStateObj
.WriteByte(collation
.sortId
);
3645 internal int GetCodePage(SqlCollation collation
, TdsParserStateObject stateObj
) {
3648 if (0 != collation
.sortId
) {
3649 codePage
= TdsEnums
.CODE_PAGE_FROM_SORT_ID
[collation
.sortId
];
3650 Debug
.Assert(0 != codePage
, "GetCodePage accessed codepage array and produced 0!, sortID =" + ((Byte
)(collation
.sortId
)).ToString((IFormatProvider
)null));
3653 int cultureId
= collation
.LCID
;
3654 bool success
= false;
3657 codePage
= CultureInfo
.GetCultureInfo(cultureId
).TextInfo
.ANSICodePage
;
3659 // SqlHot 50001398: CodePage can be zero, but we should defer such errors until
3660 // we actually MUST use the code page (i.e. don't error if no ANSI data is sent).
3663 catch (ArgumentException e
) {
3664 ADP
.TraceExceptionWithoutRethrow(e
);
3667 // If we failed, it is quite possible this is because certain culture id's
3668 // were removed in Win2k and beyond, however Sql Server still supports them.
3669 // There is a workaround for the culture id's listed below, which is to mask
3670 // off the sort id (the leading 1). If that fails, or we have a culture id
3671 // other than the special cases below, we throw an error and throw away the
3672 // rest of the results. For additional info, see MDAC 65963.
3674 // SqlHot 50001398: Sometimes GetCultureInfo will return CodePage 0 instead of throwing.
3675 // treat this as an error also, and switch into the special-case logic.
3676 if (!success
|| codePage
== 0) {
3677 CultureInfo ci
= null;
3678 switch (cultureId
) {
3679 case 0x10404: // zh-TW
3680 case 0x10804: // zh-CN
3681 case 0x10c04: // zh-HK
3682 case 0x11004: // zh-SG
3683 case 0x11404: // zh-MO
3684 case 0x10411: // ja-JP
3685 case 0x10412: // ko-KR
3686 // If one of the following special cases, mask out sortId and
3688 cultureId
= cultureId
& 0x03fff;
3691 ci
= new CultureInfo(cultureId
);
3694 catch (ArgumentException e
) {
3695 ADP
.TraceExceptionWithoutRethrow(e
);
3698 case 0x827: // Non-supported Lithuanian code page, map it to supported Lithuanian.
3700 ci
= new CultureInfo(0x427);
3703 catch (ArgumentException e
) {
3704 ADP
.TraceExceptionWithoutRethrow(e
);
3711 // I don't believe we should still be in failure case, but just in case.
3713 ThrowUnsupportedCollationEncountered(stateObj
);
3717 codePage
= ci
.TextInfo
.ANSICodePage
;
3726 internal void DrainData(TdsParserStateObject stateObj
) {
3727 RuntimeHelpers
.PrepareConstrainedRegions();
3730 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
3732 RuntimeHelpers
.PrepareConstrainedRegions();
3734 tdsReliabilitySection
.Start();
3739 SqlDataReader
.SharedState sharedState
= stateObj
._readerState
;
3740 if (sharedState
!= null && sharedState
._dataReady
) {
3741 var metadata
= stateObj
._cleanupMetaData
;
3742 if (stateObj
._partialHeaderBytesRead
> 0) {
3743 if (!stateObj
.TryProcessHeader()) {
3744 throw SQL
.SynchronousCallMayNotPend();
3747 if (0 == sharedState
._nextColumnHeaderToRead
) {
3748 // i. user called read but didn't fetch anything
3749 if (!stateObj
.Parser
.TrySkipRow(stateObj
._cleanupMetaData
, stateObj
)) {
3750 throw SQL
.SynchronousCallMayNotPend();
3754 // iia. if we still have bytes left from a partially read column, skip
3755 if (sharedState
._nextColumnDataToRead
< sharedState
._nextColumnHeaderToRead
) {
3756 if ((sharedState
._nextColumnHeaderToRead
> 0) && (metadata
[sharedState
._nextColumnHeaderToRead
- 1].metaType
.IsPlp
)) {
3757 if (stateObj
._longlen
!= 0) {
3759 if (!TrySkipPlpValue(UInt64
.MaxValue
, stateObj
, out ignored
)) {
3760 throw SQL
.SynchronousCallMayNotPend();
3765 else if (0 < sharedState
._columnDataBytesRemaining
) {
3766 if (!stateObj
.TrySkipLongBytes(sharedState
._columnDataBytesRemaining
)) {
3767 throw SQL
.SynchronousCallMayNotPend();
3775 // now read the remaining values off the wire for this row
3776 if (!stateObj
.Parser
.TrySkipRow(metadata
, sharedState
._nextColumnHeaderToRead
, stateObj
)) {
3777 throw SQL
.SynchronousCallMayNotPend();
3782 Run(RunBehavior
.Clean
, null, null, null, stateObj
);
3785 _connHandler
.DoomThisConnection();
3791 tdsReliabilitySection
.Stop();
3795 catch (System
.OutOfMemoryException
) {
3796 _connHandler
.DoomThisConnection();
3799 catch (System
.StackOverflowException
) {
3800 _connHandler
.DoomThisConnection();
3803 catch (System
.Threading
.ThreadAbortException
) {
3804 _connHandler
.DoomThisConnection();
3810 internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj
) {
3811 stateObj
.AddError(new SqlError(0, 0, TdsEnums
.MIN_ERROR_CLASS
, _server
, SQLMessage
.CultureIdError(), "", 0));
3813 if (null != stateObj
) {
3814 DrainData(stateObj
);
3816 stateObj
._pendingData
= false;
3819 ThrowExceptionAndWarning(stateObj
);
3824 internal bool TryProcessAltMetaData(int cColumns
, TdsParserStateObject stateObj
, out _SqlMetaDataSet metaData
) {
3825 Debug
.Assert(cColumns
> 0, "should have at least 1 column in altMetaData!");
3828 _SqlMetaDataSet altMetaDataSet
= new _SqlMetaDataSet(cColumns
, null);
3829 int[] indexMap
= new int[cColumns
];
3831 if (!stateObj
.TryReadUInt16(out altMetaDataSet
.id
)) {
3836 if (!stateObj
.TryReadByte(out byCols
)) {
3840 while (byCols
> 0) {
3841 if (!stateObj
.TrySkipBytes(2)) { // ignore ColNum ...
3847 // pass 1, read the meta data off the wire
3848 for (int i
= 0; i
< cColumns
; i
++) {
3849 // internal meta data class
3850 _SqlMetaData col
= altMetaDataSet
[i
];
3852 if (!stateObj
.TryReadByte(out col
.op
)) {
3855 if (!stateObj
.TryReadUInt16(out col
.operand
)) {
3859 // TCE is not applicable to AltMetadata.
3860 if (!TryCommonProcessMetaData(stateObj
, col
, null, fColMD
: false, columnEncryptionSetting
: SqlCommandColumnEncryptionSetting
.Disabled
)) {
3864 if (ADP
.IsEmpty(col
.column
)) {
3865 // create column name from op
3867 case TdsEnums
.AOPAVG
:
3871 case TdsEnums
.AOPCNT
:
3875 case TdsEnums
.AOPCNTB
:
3876 col
.column
= "cntb";
3879 case TdsEnums
.AOPMAX
:
3883 case TdsEnums
.AOPMIN
:
3887 case TdsEnums
.AOPSUM
:
3891 case TdsEnums
.AOPANY
:
3895 case TdsEnums
.AOPNOOP
:
3896 col
.column
= "noop";
3899 case TdsEnums
.AOPSTDEV
:
3900 col
.column
= "stdev";
3903 case TdsEnums
.AOPSTDEVP
:
3904 col
.column
= "stdevp";
3907 case TdsEnums
.AOPVAR
:
3911 case TdsEnums
.AOPVARP
:
3912 col
.column
= "varp";
3919 altMetaDataSet
.indexMap
= indexMap
;
3920 altMetaDataSet
.visibleColumns
= cColumns
;
3922 metaData
= altMetaDataSet
;
3927 /// <para> Parses the TDS message to read single CIPHER_INFO entry.</para>
3929 internal bool TryReadCipherInfoEntry (TdsParserStateObject stateObj
, out SqlTceCipherInfoEntry entry
) {
3930 byte cekValueCount
= 0;
3931 entry
= new SqlTceCipherInfoEntry(ordinal
: 0);
3935 if (!stateObj
.TryReadInt32(out dbId
)) {
3941 if (!stateObj
.TryReadInt32(out keyId
)) {
3945 // Read the key version
3947 if (!stateObj
.TryReadInt32(out keyVersion
)) {
3951 // Read the key MD Version
3952 byte[] keyMDVersion
= new byte[8];
3953 if (!stateObj
.TryReadByteArray(keyMDVersion
, 0, 8)) {
3957 // Read the value count
3958 if (!stateObj
.TryReadByte (out cekValueCount
)) {
3962 for (int i
= 0; i
< cekValueCount
; i
++) {
3963 // Read individual CEK values
3964 byte[] encryptedCek
;
3966 string keyStoreName
;
3967 byte algorithmLength
;
3968 string algorithmName
;
3973 // Read the length of encrypted CEK
3974 if (!stateObj
.TryReadUInt16 (out shortValue
)) {
3978 length
= shortValue
;
3979 encryptedCek
= new byte[length
];
3981 // Read the actual encrypted CEK
3982 if (!stateObj
.TryReadByteArray (encryptedCek
, 0, length
)) {
3986 // Read the length of key store name
3987 if (!stateObj
.TryReadByte (out byteValue
)) {
3993 // And read the key store name now
3994 if (!stateObj
.TryReadString(length
, out keyStoreName
)) {
3998 // Read the length of key Path
3999 if (!stateObj
.TryReadUInt16 (out shortValue
)) {
4003 length
= shortValue
;
4005 // Read the key path string
4006 if (!stateObj
.TryReadString(length
, out keyPath
)) {
4010 // Read the length of the string carrying the encryption algo
4011 if (!stateObj
.TryReadByte(out algorithmLength
)) {
4015 length
= (int)algorithmLength
;
4017 // Read the string carrying the encryption algo (eg. RSA_PKCS_OAEP)
4018 if (!stateObj
.TryReadString(length
, out algorithmName
)) {
4022 // Add this encrypted CEK blob to our list of encrypted values for the CEK
4023 entry
.Add(encryptedCek
,
4026 cekVersion: keyVersion
,
4027 cekMdVersion: keyMDVersion
,
4029 keyStoreName: keyStoreName
,
4030 algorithmName: algorithmName
);
4037 /// <para> Parses the TDS message to read a single CIPHER_INFO table.</para>
4039 internal bool TryProcessCipherInfoTable (TdsParserStateObject stateObj
, out SqlTceCipherInfoTable
? cipherTable
) {
4041 short tableSize
= 0;
4043 if (!stateObj
.TryReadInt16(out tableSize
)) {
4047 if (0 != tableSize
) {
4048 SqlTceCipherInfoTable tempTable
= new SqlTceCipherInfoTable(tableSize
);
4050 // Read individual entries
4051 for (int i
= 0; i
< tableSize
; i
++) {
4052 SqlTceCipherInfoEntry entry
;
4053 if (!TryReadCipherInfoEntry (stateObj
, out entry
)) {
4057 tempTable
[i
] = entry
;
4060 cipherTable
= tempTable
;
4066 internal bool TryProcessMetaData(int cColumns
, TdsParserStateObject stateObj
, out _SqlMetaDataSet metaData
, SqlCommandColumnEncryptionSetting columnEncryptionSetting
) {
4067 Debug
.Assert(cColumns
> 0, "should have at least 1 column in metadata!");
4069 // Read the cipher info table first
4070 SqlTceCipherInfoTable
? cipherTable
= null;
4071 if (_serverSupportsColumnEncryption
) {
4072 if (!TryProcessCipherInfoTable (stateObj
, out cipherTable
)) {
4078 // Read the ColumnData fields
4079 _SqlMetaDataSet newMetaData
= new _SqlMetaDataSet(cColumns
, cipherTable
);
4080 for (int i
= 0; i
< cColumns
; i
++) {
4081 if (!TryCommonProcessMetaData(stateObj
, newMetaData
[i
], cipherTable
, fColMD
: true, columnEncryptionSetting
: columnEncryptionSetting
)) {
4087 // DEVNOTE: cipherTable is discarded at this point since its no longer needed.
4088 metaData
= newMetaData
;
4092 private bool IsVarTimeTds(byte tdsType
) {
4093 return tdsType
== TdsEnums
.SQLTIME
|| tdsType
== TdsEnums
.SQLDATETIME2
|| tdsType
== TdsEnums
.SQLDATETIMEOFFSET
;
4096 private bool TryProcessTypeInfo (TdsParserStateObject stateObj
, SqlMetaDataPriv col
, UInt32 userType
) {
4099 if (!stateObj
.TryReadByte(out tdsType
)) {
4103 if (tdsType
== TdsEnums
.SQLXMLTYPE
)
4104 col
.length
= TdsEnums
.SQL_USHORTVARMAXLEN
; //Use the same length as other plp datatypes
4105 else if (IsVarTimeTds(tdsType
))
4106 col
.length
= 0; // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
4107 else if (tdsType
== TdsEnums
.SQLDATE
) {
4111 if (!TryGetTokenLength(tdsType
, stateObj
, out col
.length
)) {
4116 col
.metaType
= MetaType
.GetSqlDataType(tdsType
, userType
, col
.length
);
4117 col
.type
= col
.metaType
.SqlDbType
;
4119 // If sphinx, do not change to nullable type
4121 col
.tdsType
= (col
.isNullable
? col
.metaType
.NullableType
: col
.metaType
.TDSType
);
4123 col
.tdsType
= tdsType
;
4126 if (TdsEnums
.SQLUDT
== tdsType
) {
4127 if (!TryProcessUDTMetaData((SqlMetaDataPriv
) col
, stateObj
)) {
4132 if (col
.length
== TdsEnums
.SQL_USHORTVARMAXLEN
) {
4133 Debug
.Assert(tdsType
== TdsEnums
.SQLXMLTYPE
||
4134 tdsType
== TdsEnums
.SQLBIGVARCHAR
||
4135 tdsType
== TdsEnums
.SQLBIGVARBINARY
||
4136 tdsType
== TdsEnums
.SQLNVARCHAR
||
4137 tdsType
== TdsEnums
.SQLUDT
,
4138 "Invalid streaming datatype");
4139 col
.metaType
= MetaType
.GetMaxMetaTypeFromMetaType(col
.metaType
);
4140 Debug
.Assert(col
.metaType
.IsLong
, "Max datatype not IsLong");
4141 col
.length
= Int32
.MaxValue
;
4142 if (tdsType
== TdsEnums
.SQLXMLTYPE
) {
4144 if (!stateObj
.TryReadByte(out schemapresent
)) {
4148 if ((schemapresent
& 1) != 0) {
4149 if (!stateObj
.TryReadByte(out byteLen
)) {
4153 if (!stateObj
.TryReadString(byteLen
, out col
.xmlSchemaCollectionDatabase
)) {
4158 if (!stateObj
.TryReadByte(out byteLen
)) {
4162 if (!stateObj
.TryReadString(byteLen
, out col
.xmlSchemaCollectionOwningSchema
)) {
4168 if (!stateObj
.TryReadInt16(out shortLen
)) {
4172 if (!stateObj
.TryReadString(shortLen
, out col
.xmlSchemaCollectionName
)) {
4181 if (col
.type
== SqlDbType
.Decimal
) {
4182 if (!stateObj
.TryReadByte(out col
.precision
)) {
4185 if (!stateObj
.TryReadByte(out col
.scale
)) {
4190 if (col
.metaType
.IsVarTime
) {
4191 if (!stateObj
.TryReadByte(out col
.scale
)) {
4195 Debug
.Assert(0 <= col
.scale
&& col
.scale
<= 7);
4197 // calculate actual column length here
4199 switch (col
.metaType
.SqlDbType
)
4201 case SqlDbType
.Time
:
4202 col
.length
= MetaType
.GetTimeSizeFromScale(col
.scale
);
4204 case SqlDbType
.DateTime2
:
4205 // Date in number of days (3 bytes) + time
4206 col
.length
= 3 + MetaType
.GetTimeSizeFromScale(col
.scale
);
4208 case SqlDbType
.DateTimeOffset
:
4209 // Date in days (3 bytes) + offset in minutes (2 bytes) + time
4210 col
.length
= 5 + MetaType
.GetTimeSizeFromScale(col
.scale
);
4214 Debug
.Assert(false, "Unknown VariableTime type!");
4219 // read the collation for 7.x servers
4220 if (_isShiloh
&& col
.metaType
.IsCharType
&& (tdsType
!= TdsEnums
.SQLXMLTYPE
)) {
4221 if (!TryProcessCollation(stateObj
, out col
.collation
)) {
4225 int codePage
= GetCodePage(col
.collation
, stateObj
);
4227 if (codePage
== _defaultCodePage
) {
4228 col
.codePage
= _defaultCodePage
;
4229 col
.encoding
= _defaultEncoding
;
4232 col
.codePage
= codePage
;
4233 col
.encoding
= System
.Text
.Encoding
.GetEncoding(col
.codePage
);
4240 private bool TryCommonProcessMetaData(TdsParserStateObject stateObj
, _SqlMetaData col
, SqlTceCipherInfoTable
? cipherTable
, bool fColMD
, SqlCommandColumnEncryptionSetting columnEncryptionSetting
) {
4244 // read user type - 4 bytes Yukon, 2 backwards
4245 if (IsYukonOrNewer
) {
4246 if (!stateObj
.TryReadUInt32(out userType
)) {
4251 ushort userTypeShort
;
4252 if (!stateObj
.TryReadUInt16(out userTypeShort
)) {
4255 userType
= userTypeShort
;
4258 // read flags and set appropriate flags in structure
4260 if (!stateObj
.TryReadByte(out flags
)) {
4264 col
.updatability
= (byte)((flags
& TdsEnums
.Updatability
) >> 2);
4265 col
.isNullable
= (TdsEnums
.Nullable
== (flags
& TdsEnums
.Nullable
));
4266 col
.isIdentity
= (TdsEnums
.Identity
== (flags
& TdsEnums
.Identity
));
4268 // read second byte of column metadata flags
4269 if (!stateObj
.TryReadByte(out flags
)) {
4273 col
.isColumnSet
= (TdsEnums
.IsColumnSet
== (flags
& TdsEnums
.IsColumnSet
));
4274 if (fColMD
&& _serverSupportsColumnEncryption
) {
4275 col
.isEncrypted
= (TdsEnums
.IsEncrypted
== (flags
& TdsEnums
.IsEncrypted
));
4279 if (!TryProcessTypeInfo (stateObj
, col
, userType
)) {
4283 // Read tablename if present
4284 if (col
.metaType
.IsLong
&& !col
.metaType
.IsPlp
) {
4286 int unusedLen
= 0xFFFF; //We ignore this value
4287 if (!TryProcessOneTable(stateObj
, ref unusedLen
, out col
.multiPartTableName
)) {
4292 if (!stateObj
.TryReadUInt16(out shortLen
)) {
4296 if (!stateObj
.TryReadString(shortLen
, out tableName
)) {
4299 // with Sql2000 this is returned as an unquoted mix of catalog.owner.table
4300 // all of which may contain "." and unable to parse correctly from the string alone
4301 // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column
4302 // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios
4303 col
.multiPartTableName
= new MultiPartTableName(tableName
);
4307 // Read the TCE column cryptoinfo
4308 if (fColMD
&& _serverSupportsColumnEncryption
&& col
.isEncrypted
) {
4309 // If the column is encrypted, we should have a valid cipherTable
4310 if (cipherTable
.HasValue
&& !TryProcessTceCryptoMetadata (stateObj
, col
, cipherTable
.Value
, columnEncryptionSetting
, isReturnValue
: false)) {
4315 // Read the column name
4316 if (!stateObj
.TryReadByte(out byteLen
)) {
4319 if (!stateObj
.TryReadString(byteLen
, out col
.column
)) {
4323 // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings.
4324 // We only need to fire this event when we actually have a meta data stream with 0 or more rows.
4325 stateObj
._receivedColMetaData
= true;
4329 private bool TryProcessUDTMetaData(SqlMetaDataPriv metaData
, TdsParserStateObject stateObj
) {
4333 if (!stateObj
.TryReadUInt16(out shortLength
)) { // max byte size
4336 metaData
.length
= shortLength
;
4339 if (!stateObj
.TryReadByte(out byteLength
)) {
4342 if (byteLength
!= 0) {
4343 if (!stateObj
.TryReadString(byteLength
, out metaData
.udtDatabaseName
)) {
4349 if (!stateObj
.TryReadByte(out byteLength
)) {
4352 if (byteLength
!= 0) {
4353 if (!stateObj
.TryReadString(byteLength
, out metaData
.udtSchemaName
)) {
4359 if (!stateObj
.TryReadByte(out byteLength
)) {
4362 if (byteLength
!= 0) {
4363 if (!stateObj
.TryReadString(byteLength
, out metaData
.udtTypeName
)) {
4368 if (!stateObj
.TryReadUInt16(out shortLength
)) {
4371 if (shortLength
!= 0) {
4372 if (!stateObj
.TryReadString(shortLength
, out metaData
.udtAssemblyQualifiedName
)) {
4380 private void WriteUDTMetaData(object value, string database
, string schema
, string type
,
4381 TdsParserStateObject stateObj
) {
4383 if (ADP
.IsEmpty(database
)) {
4384 stateObj
.WriteByte(0);
4387 stateObj
.WriteByte((byte)database
.Length
);
4388 WriteString(database
, stateObj
);
4392 if (ADP
.IsEmpty(schema
)) {
4393 stateObj
.WriteByte(0);
4396 stateObj
.WriteByte((byte)schema
.Length
);
4397 WriteString(schema
, stateObj
);
4401 if (ADP
.IsEmpty(type
)) {
4402 stateObj
.WriteByte(0);
4405 stateObj
.WriteByte((byte)type
.Length
);
4406 WriteString(type
, stateObj
);
4410 internal bool TryProcessTableName(int length
, TdsParserStateObject stateObj
, out MultiPartTableName
[] multiPartTableNames
) {
4411 int tablesAdded
= 0;
4413 MultiPartTableName
[] tables
= new MultiPartTableName
[1];
4414 MultiPartTableName mpt
;
4415 while (length
> 0) {
4419 if (!TryProcessOneTable(stateObj
, ref length
, out mpt
)) {
4420 multiPartTableNames
= null;
4423 if (tablesAdded
== 0) {
4424 tables
[tablesAdded
] = mpt
;
4427 MultiPartTableName
[] newTables
= new MultiPartTableName
[tables
.Length
+ 1];
4428 Array
.Copy(tables
, 0, newTables
, 0, tables
.Length
);
4429 newTables
[tables
.Length
] = mpt
;
4436 multiPartTableNames
= tables
;
4440 private bool TryProcessOneTable(TdsParserStateObject stateObj
, ref int length
, out MultiPartTableName multiPartTableName
) {
4442 MultiPartTableName mpt
;
4445 multiPartTableName
= default(MultiPartTableName
);
4449 mpt
= new MultiPartTableName();
4452 // Find out how many parts in the TDS stream
4453 if (!stateObj
.TryReadByte(out nParts
)) {
4458 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4462 if (!stateObj
.TryReadString(tableLen
, out value)) {
4465 mpt
.ServerName
= value;
4467 length
-= (tableLen
* 2); // wide bytes
4470 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4474 if (!stateObj
.TryReadString(tableLen
, out value)) {
4477 mpt
.CatalogName
= value;
4478 length
-= (tableLen
* 2); // wide bytes
4482 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4486 if (!stateObj
.TryReadString(tableLen
, out value)) {
4489 mpt
.SchemaName
= value;
4490 length
-= (tableLen
* 2); // wide bytes
4494 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4498 if (!stateObj
.TryReadString(tableLen
, out value)) {
4501 mpt
.TableName
= value;
4502 length
-= (tableLen
* 2); // wide bytes
4505 Debug
.Assert(nParts
== 0 , "ProcessTableName:Unidentified parts in the table name token stream!");
4509 if (!stateObj
.TryReadUInt16(out tableLen
)) {
4513 if (!stateObj
.TryReadString(tableLen
, out value)) {
4516 string tableName
= value;
4517 length
-= (tableLen
* 2); // wide bytes
4518 mpt
= new MultiPartTableName(MultipartIdentifier
.ParseMultipartIdentifier(tableName
, "[\"", "]\"", Res
.SQL_TDSParserTableName
, false));
4521 multiPartTableName
= mpt
;
4525 // augments current metadata with table and key information
4526 private bool TryProcessColInfo(_SqlMetaDataSet columns
, SqlDataReader reader
, TdsParserStateObject stateObj
, out _SqlMetaDataSet metaData
) {
4527 Debug
.Assert(columns
!= null && columns
.Length
> 0, "no metadata available!");
4531 for (int i
= 0; i
< columns
.Length
; i
++) {
4532 _SqlMetaData col
= columns
[i
];
4535 if (!stateObj
.TryReadByte(out ignored
)) { // colnum, ignore
4538 if (!stateObj
.TryReadByte(out col
.tableNum
)) {
4544 if (!stateObj
.TryReadByte(out status
)) {
4548 col
.isDifferentName
= (TdsEnums
.SQLDifferentName
== (status
& TdsEnums
.SQLDifferentName
));
4549 col
.isExpression
= (TdsEnums
.SQLExpression
== (status
& TdsEnums
.SQLExpression
));
4550 col
.isKey
= (TdsEnums
.SQLKey
== (status
& TdsEnums
.SQLKey
));
4551 col
.isHidden
= (TdsEnums
.SQLHidden
== (status
& TdsEnums
.SQLHidden
));
4553 // read off the base table name if it is different than the select list column name
4554 if (col
.isDifferentName
) {
4556 if (!stateObj
.TryReadByte(out len
)) {
4559 if (!stateObj
.TryReadString(len
, out col
.baseColumn
)) {
4564 // Fixup column name - only if result of a table - that is if it was not the result of
4566 if ((reader
.TableNames
!= null) && (col
.tableNum
> 0)) {
4567 Debug
.Assert(reader
.TableNames
.Length
>= col
.tableNum
, "invalid tableNames array!");
4568 col
.multiPartTableName
= reader
.TableNames
[col
.tableNum
- 1];
4571 // MDAC 60109: expressions are readonly
4572 if (col
.isExpression
) {
4573 col
.updatability
= 0;
4577 // set the metadata so that the stream knows some metadata info has changed
4582 // takes care of any per data header information:
4583 // for long columns, reads off textptrs, reads length, check nullability
4584 // for other columns, reads length, checks nullability
4585 // returns length and nullability
4586 internal bool TryProcessColumnHeader(SqlMetaDataPriv col
, TdsParserStateObject stateObj
, int columnOrdinal
, out bool isNull
, out ulong length
) {
4587 // query NBC row information first
4588 if (stateObj
.IsNullCompressionBitSet(columnOrdinal
)) {
4590 // column information is not present in TDS if null compression bit is set, return now
4595 return TryProcessColumnHeaderNoNBC(col
, stateObj
, out isNull
, out length
);
4598 private bool TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col
, TdsParserStateObject stateObj
, out bool isNull
, out ulong length
) {
4599 if (col
.metaType
.IsLong
&& !col
.metaType
.IsPlp
) {
4601 // we don't care about TextPtrs, simply go after the data after it
4604 if (!stateObj
.TryReadByte(out textPtrLen
)) {
4610 if (0 != textPtrLen
) {
4611 // read past text pointer
4612 if (!stateObj
.TrySkipBytes(textPtrLen
)) {
4618 // read past timestamp
4619 if (!stateObj
.TrySkipBytes(TdsEnums
.TEXT_TIME_STAMP_LEN
)) {
4626 return TryGetDataLength(col
, stateObj
, out length
);
4638 if (!TryGetDataLength(col
, stateObj
, out longlen
)) {
4643 isNull
= IsNull(col
.metaType
, longlen
);
4644 length
= (isNull
? 0 : longlen
);
4649 // assumes that the current position is at the start of an altrow!
4650 internal bool TryGetAltRowId(TdsParserStateObject stateObj
, out int id
) {
4652 if (!stateObj
.TryReadByte(out token
)) { // skip over ALTROW token
4656 Debug
.Assert((token
== TdsEnums
.SQLALTROW
), "");
4658 // Start a fresh row - disable NBC since Alt Rows are never compressed
4659 if (!stateObj
.TryStartNewRow(isNullCompressed
: false)) {
4665 if (!stateObj
.TryReadUInt16(out shortId
)) {
4674 // Used internally by BulkCopy only
4675 private bool TryProcessRow(_SqlMetaDataSet columns
, object[] buffer
, int[] map
, TdsParserStateObject stateObj
) {
4676 SqlBuffer data
= new SqlBuffer();
4678 for (int i
= 0; i
< columns
.Length
; i
++) {
4679 _SqlMetaData md
= columns
[i
];
4680 Debug
.Assert(md
!= null, "_SqlMetaData should not be null for column " + i
.ToString(CultureInfo
.InvariantCulture
));
4684 if (!TryProcessColumnHeader(md
, stateObj
, i
, out isNull
, out len
)) {
4689 GetNullSqlValue(data
, md
, SqlCommandColumnEncryptionSetting
.Disabled
/*Column Encryption Disabled for Bulk Copy*/, _connHandler
);
4690 buffer
[map
[i
]] = data
.SqlValue
;
4693 // We only read up to 2Gb. Throw if data is larger. Very large data
4694 // should be read in chunks in sequential read mode
4695 // For Plp columns, we may have gotten only the length of the first chunk
4696 if (!TryReadSqlValue(data
, md
, md
.metaType
.IsPlp
? (Int32
.MaxValue
) : (int)len
, stateObj
,
4697 SqlCommandColumnEncryptionSetting
.Disabled
/*Column Encryption Disabled for Bulk Copy*/,
4701 buffer
[map
[i
]] = data
.SqlValue
;
4702 if (stateObj
._longlen
!= 0) {
4703 throw new SqlTruncateException(Res
.GetString(Res
.SqlMisc_TruncationMaxDataMessage
));
4713 /// Determines if a column value should be transparently decrypted (based on SqlCommand and Connection String settings).
4715 /// <returns>true if the value should be transparently decrypted, false otherwise</returns>
4716 internal static bool ShouldHonorTceForRead (SqlCommandColumnEncryptionSetting columnEncryptionSetting
,
4717 SqlInternalConnectionTds connection
) {
4719 // Command leve setting trumps all
4720 switch (columnEncryptionSetting
) {
4721 case SqlCommandColumnEncryptionSetting
.Disabled
:
4723 case SqlCommandColumnEncryptionSetting
.Enabled
:
4725 case SqlCommandColumnEncryptionSetting
.ResultSetOnly
:
4728 // Check connection level setting!
4729 Debug
.Assert(SqlCommandColumnEncryptionSetting
.UseConnectionSetting
== columnEncryptionSetting
,
4730 "Unexpected value for command level override");
4731 return (connection
!= null && connection
.ConnectionOptions
!= null &&
4732 connection
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
);
4736 internal static object GetNullSqlValue(
4739 SqlCommandColumnEncryptionSetting columnEncryptionSetting
,
4740 SqlInternalConnectionTds connection
) {
4741 SqlDbType type
= md
.type
;
4743 if (type
== SqlDbType
.VarBinary
&& // if its a varbinary
4744 md
.isEncrypted
&&// and encrypted
4745 ShouldHonorTceForRead(columnEncryptionSetting
, connection
)){
4746 type
= md
.baseTI
.type
; // the use the actual (plaintext) type
4750 case SqlDbType
.Real
:
4751 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Single
);
4754 case SqlDbType
.Float
:
4755 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Double
);
4759 case SqlDbType
.Binary
:
4760 case SqlDbType
.VarBinary
:
4761 case SqlDbType
.Image
:
4762 nullVal
.SqlBinary
= SqlBinary
.Null
;
4765 case SqlDbType
.UniqueIdentifier
:
4766 nullVal
.SqlGuid
= SqlGuid
.Null
;
4770 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Boolean
);
4773 case SqlDbType
.TinyInt
:
4774 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Byte
);
4777 case SqlDbType
.SmallInt
:
4778 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Int16
);
4782 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Int32
);
4785 case SqlDbType
.BigInt
:
4786 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Int64
);
4789 case SqlDbType
.Char
:
4790 case SqlDbType
.VarChar
:
4791 case SqlDbType
.NChar
:
4792 case SqlDbType
.NVarChar
:
4793 case SqlDbType
.Text
:
4794 case SqlDbType
.NText
:
4795 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.String
);
4798 case SqlDbType
.Decimal
:
4799 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Decimal
);
4802 case SqlDbType
.DateTime
:
4803 case SqlDbType
.SmallDateTime
:
4804 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.DateTime
);
4807 case SqlDbType
.Money
:
4808 case SqlDbType
.SmallMoney
:
4809 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Money
);
4812 case SqlDbType
.Variant
:
4813 // DBNull.Value will have to work here
4814 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Empty
);
4818 nullVal
.SqlCachedBuffer
= SqlCachedBuffer
.Null
;
4821 case SqlDbType
.Date
:
4822 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Date
);
4825 case SqlDbType
.Time
:
4826 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.Time
);
4829 case SqlDbType
.DateTime2
:
4830 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.DateTime2
);
4833 case SqlDbType
.DateTimeOffset
:
4834 nullVal
.SetToNullOfType(SqlBuffer
.StorageType
.DateTimeOffset
);
4837 case SqlDbType
.Timestamp
:
4838 // Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change
4839 // Dev10 Bug #752790 - don't assert when it does happen
4843 Debug
.Assert(false, "unknown null sqlType!" + md
.type
.ToString());
4850 internal bool TrySkipRow(_SqlMetaDataSet columns
, TdsParserStateObject stateObj
) {
4851 return TrySkipRow(columns
, 0, stateObj
);
4854 internal bool TrySkipRow(_SqlMetaDataSet columns
, int startCol
, TdsParserStateObject stateObj
) {
4855 for (int i
= startCol
; i
< columns
.Length
; i
++) {
4856 _SqlMetaData md
= columns
[i
];
4858 if (!TrySkipValue(md
, i
, stateObj
)) {
4866 /// This method skips bytes of a single column value from the media. It supports NBCROW and handles all types of values, including PLP and long
4868 internal bool TrySkipValue(SqlMetaDataPriv md
, int columnOrdinal
, TdsParserStateObject stateObj
) {
4869 if (stateObj
.IsNullCompressionBitSet(columnOrdinal
)) {
4873 if (md
.metaType
.IsPlp
) {
4875 if (!TrySkipPlpValue(UInt64
.MaxValue
, stateObj
, out ignored
)) {
4879 else if (md
.metaType
.IsLong
) {
4881 Debug
.Assert(!md
.metaType
.IsPlp
, "Plp types must be handled using SkipPlpValue");
4884 if (!stateObj
.TryReadByte(out textPtrLen
)) {
4888 if (0 != textPtrLen
) {
4889 if (!stateObj
.TrySkipBytes(textPtrLen
+ TdsEnums
.TEXT_TIME_STAMP_LEN
)) {
4894 if (!TryGetTokenLength(md
.tdsType
, stateObj
, out length
)) {
4897 if (!stateObj
.TrySkipBytes(length
)) {
4904 if (!TryGetTokenLength(md
.tdsType
, stateObj
, out length
)) {
4908 // if false, no value to skip - it's null
4909 if (!IsNull(md
.metaType
, (ulong)length
)) {
4910 if (!stateObj
.TrySkipBytes(length
)) {
4919 private bool IsNull(MetaType mt
, ulong length
) {
4920 // null bin and char types have a length of -1 to represent null
4922 return (TdsEnums
.SQL_PLP_NULL
== length
);
4925 // HOTFIX #50000415: for image/text, 0xFFFF is the length, not representing null
4926 if ((TdsEnums
.VARNULL
== length
) && !mt
.IsLong
) {
4930 // other types have a length of 0 to represent null
4931 // long and non-PLP types will always return false because these types are either char or binary
4932 // this is expected since for long and non-plp types isnull is checked based on textptr field and not the length
4933 return ((TdsEnums
.FIXEDNULL
== length
) && !mt
.IsCharType
&& !mt
.IsBinType
);
4936 private bool TryReadSqlStringValue(SqlBuffer
value, byte type
, int length
, Encoding encoding
, bool isPlp
, TdsParserStateObject stateObj
) {
4938 case TdsEnums
.SQLCHAR
:
4939 case TdsEnums
.SQLBIGCHAR
:
4940 case TdsEnums
.SQLVARCHAR
:
4941 case TdsEnums
.SQLBIGVARCHAR
:
4942 case TdsEnums
.SQLTEXT
:
4943 // If bigvarchar(max), we only read the first chunk here,
4944 // expecting the caller to read the rest
4945 if (encoding
== null) {
4946 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
4947 // 7.0 has no support for multiple code pages in data - single code page support only
4948 encoding
= _defaultEncoding
;
4951 if (!stateObj
.TryReadStringWithEncoding(length
, encoding
, isPlp
, out stringValue
)) {
4954 value.SetToString(stringValue
);
4957 case TdsEnums
.SQLNCHAR
:
4958 case TdsEnums
.SQLNVARCHAR
:
4959 case TdsEnums
.SQLNTEXT
:
4966 if (!TryReadPlpUnicodeChars(ref cc
, 0, length
>> 1, stateObj
, out length
)) {
4970 s
= new String(cc
, 0, length
);
4977 if (!stateObj
.TryReadString(length
>> 1, out s
)) {
4982 value.SetToString(s
);
4987 Debug
.Assert(false, "Unknown tds type for SqlString!" + type
.ToString(CultureInfo
.InvariantCulture
));
4995 /// Deserializes the unencrypted bytes into a value based on the target type info.
4997 internal bool DeserializeUnencryptedValue (SqlBuffer
value, byte[] unencryptedBytes
, SqlMetaDataPriv md
, TdsParserStateObject stateObj
, byte normalizationVersion
) {
4998 if (normalizationVersion
!= 0x01) {
4999 throw SQL
.UnsupportedNormalizationVersion(normalizationVersion
);
5002 byte tdsType
= md
.baseTI
.tdsType
;
5003 int length
= unencryptedBytes
.Length
;
5005 // For normalized types, the length and scale of the actual type might be different than the value's.
5006 int denormalizedLength
= md
.baseTI
.length
;
5007 byte denormalizedScale
= md
.baseTI
.scale
;
5009 Debug
.Assert (false == md
.baseTI
.isEncrypted
, "Double encryption detected");
5011 // We normalize to allow conversion across data types. All data types below are serialized into a BIGINT.
5012 case TdsEnums
.SQLBIT
:
5013 case TdsEnums
.SQLBITN
:
5014 case TdsEnums
.SQLINTN
:
5015 case TdsEnums
.SQLINT1
:
5016 case TdsEnums
.SQLINT2
:
5017 case TdsEnums
.SQLINT4
:
5018 case TdsEnums
.SQLINT8
:
5019 Debug
.Assert(length
== 8, "invalid length for SqlInt64 type!");
5023 if (unencryptedBytes
.Length
!= 8) {
5027 longValue
= BitConverter
.ToInt64(unencryptedBytes
, 0);
5029 if (tdsType
== TdsEnums
.SQLBIT
||
5030 tdsType
== TdsEnums
.SQLBITN
) {
5031 value.Boolean
= (longValue
!= 0);
5035 if (tdsType
== TdsEnums
.SQLINT1
|| denormalizedLength
== 1)
5036 value.Byte
= (byte)longValue
;
5037 else if (tdsType
== TdsEnums
.SQLINT2
|| denormalizedLength
== 2)
5038 value.Int16
= (Int16
)longValue
;
5039 else if (tdsType
== TdsEnums
.SQLINT4
|| denormalizedLength
== 4)
5040 value.Int32
= (Int32
)longValue
;
5042 value.Int64
= longValue
;
5046 case TdsEnums
.SQLFLTN
:
5048 goto case TdsEnums
.SQLFLT4
;
5051 goto case TdsEnums
.SQLFLT8
;
5054 case TdsEnums
.SQLFLT4
:
5055 Debug
.Assert(length
== 4, "invalid length for SqlSingle type!");
5057 if (unencryptedBytes
.Length
!= 4) {
5061 singleValue
= BitConverter
.ToSingle(unencryptedBytes
, 0);
5062 value.Single
= singleValue
;
5065 case TdsEnums
.SQLFLT8
:
5067 if (unencryptedBytes
.Length
!= 8) {
5071 doubleValue
= BitConverter
.ToDouble(unencryptedBytes
, 0);
5072 value.Double
= doubleValue
;
5075 // We normalize to allow conversion across data types. SMALLMONEY is serialized into a MONEY.
5076 case TdsEnums
.SQLMONEYN
:
5077 case TdsEnums
.SQLMONEY4
:
5078 case TdsEnums
.SQLMONEY
:
5083 if (unencryptedBytes
.Length
!= 8) {
5087 mid
= BitConverter
.ToInt32(unencryptedBytes
, 0);
5088 lo
= BitConverter
.ToUInt32(unencryptedBytes
, 4);
5090 long l
= (((long)mid
) << 0x20) + ((long)lo
);
5091 value.SetToMoney(l
);
5095 case TdsEnums
.SQLDATETIMN
:
5097 goto case TdsEnums
.SQLDATETIM4
;
5100 goto case TdsEnums
.SQLDATETIME
;
5103 case TdsEnums
.SQLDATETIM4
:
5104 ushort daypartShort
, timepartShort
;
5105 if (unencryptedBytes
.Length
!= 4) {
5109 daypartShort
= (UInt16
)((unencryptedBytes
[1] << 8) + unencryptedBytes
[0]);
5110 timepartShort
= (UInt16
)((unencryptedBytes
[3] << 8) + unencryptedBytes
[2]);
5111 value.SetToDateTime(daypartShort
, timepartShort
* SqlDateTime
.SQLTicksPerMinute
);
5114 case TdsEnums
.SQLDATETIME
:
5117 if (unencryptedBytes
.Length
!= 8) {
5121 daypart
= BitConverter
.ToInt32(unencryptedBytes
, 0);
5122 timepart
= BitConverter
.ToUInt32(unencryptedBytes
, 4);
5123 value.SetToDateTime(daypart
, (int)timepart
);
5126 case TdsEnums
.SQLUNIQUEID
:
5128 Debug
.Assert(length
== 16, "invalid length for SqlGuid type!");
5129 value.SqlGuid
= new SqlGuid(unencryptedBytes
, true); // doesn't copy the byte array
5133 case TdsEnums
.SQLBINARY
:
5134 case TdsEnums
.SQLBIGBINARY
:
5135 case TdsEnums
.SQLBIGVARBINARY
:
5136 case TdsEnums
.SQLVARBINARY
:
5137 case TdsEnums
.SQLIMAGE
:
5139 // Note: Better not come here with plp data!!
5140 Debug
.Assert(length
<= TdsEnums
.MAXSIZE
, "Plp data decryption attempted");
5142 // If this is a fixed length type, pad with zeros to get to the fixed length size.
5143 if (tdsType
== TdsEnums
.SQLBINARY
|| tdsType
== TdsEnums
.SQLBIGBINARY
) {
5144 byte[] bytes
= new byte[md
.baseTI
.length
];
5145 Buffer
.BlockCopy(unencryptedBytes
, 0, bytes
, 0, unencryptedBytes
.Length
);
5146 unencryptedBytes
= bytes
;
5149 value.SqlBinary
= new SqlBinary(unencryptedBytes
, true); // doesn't copy the byte array
5153 case TdsEnums
.SQLDECIMALN
:
5154 case TdsEnums
.SQLNUMERICN
:
5155 // Check the sign from the first byte.
5157 byteValue
= unencryptedBytes
[index
++];
5158 bool fPositive
= (1 == byteValue
);
5160 // Now read the 4 next integers which contain the actual value.
5161 length
= checked((int)length
-1);
5162 int[] bits
= new int[4];
5163 int decLength
= length
>>2;
5164 for (int i
= 0; i
< decLength
; i
++) {
5165 // up to 16 bytes of data following the sign byte
5166 bits
[i
] = BitConverter
.ToInt32(unencryptedBytes
, index
);
5169 value.SetToDecimal (md
.baseTI
.precision
, md
.baseTI
.scale
, fPositive
, bits
);
5172 case TdsEnums
.SQLCHAR
:
5173 case TdsEnums
.SQLBIGCHAR
:
5174 case TdsEnums
.SQLVARCHAR
:
5175 case TdsEnums
.SQLBIGVARCHAR
:
5176 case TdsEnums
.SQLTEXT
:
5178 System
.Text
.Encoding encoding
= md
.baseTI
.encoding
;
5180 if (null == encoding
) {
5181 encoding
= _defaultEncoding
;
5184 if (null == encoding
) {
5185 ThrowUnsupportedCollationEncountered(stateObj
);
5188 string strValue
= encoding
.GetString(unencryptedBytes
, 0, length
);
5190 // If this is a fixed length type, pad with spaces to get to the fixed length size.
5191 if (tdsType
== TdsEnums
.SQLCHAR
|| tdsType
== TdsEnums
.SQLBIGCHAR
) {
5192 strValue
= strValue
.PadRight(md
.baseTI
.length
);
5195 value.SetToString(strValue
);
5199 case TdsEnums
.SQLNCHAR
:
5200 case TdsEnums
.SQLNVARCHAR
:
5201 case TdsEnums
.SQLNTEXT
:
5203 string strValue
= System
.Text
.Encoding
.Unicode
.GetString(unencryptedBytes
, 0, length
);
5205 // If this is a fixed length type, pad with spaces to get to the fixed length size.
5206 if (tdsType
== TdsEnums
.SQLNCHAR
) {
5207 strValue
= strValue
.PadRight(md
.baseTI
.length
/ ADP
.CharSize
);
5210 value.SetToString(strValue
);
5214 case TdsEnums
.SQLDATE
:
5215 Debug
.Assert(length
== 3, "invalid length for date type!");
5216 value.SetToDate(unencryptedBytes
);
5219 case TdsEnums
.SQLTIME
:
5220 // We normalize to maximum precision to allow conversion across different precisions.
5221 Debug
.Assert(length
== 5, "invalid length for time type!");
5222 value.SetToTime(unencryptedBytes
, length
, TdsEnums
.MAX_TIME_SCALE
, denormalizedScale
);
5225 case TdsEnums
.SQLDATETIME2
:
5226 // We normalize to maximum precision to allow conversion across different precisions.
5227 Debug
.Assert(length
== 8, "invalid length for datetime2 type!");
5228 value.SetToDateTime2(unencryptedBytes
, length
, TdsEnums
.MAX_TIME_SCALE
, denormalizedScale
);
5231 case TdsEnums
.SQLDATETIMEOFFSET
:
5232 // We normalize to maximum precision to allow conversion across different precisions.
5233 Debug
.Assert(length
== 10, "invalid length for datetimeoffset type!");
5234 value.SetToDateTimeOffset(unencryptedBytes
, length
, TdsEnums
.MAX_TIME_SCALE
, denormalizedScale
);
5238 MetaType metaType
= md
.baseTI
.metaType
;
5240 // If we don't have a metatype already, construct one to get the proper type name.
5241 if (metaType
== null) {
5242 metaType
= MetaType
.GetSqlDataType(tdsType
, userType
:0, length
:length
);
5245 throw SQL
.UnsupportedDatatypeEncryption(metaType
.TypeName
);
5251 internal bool TryReadSqlValue(SqlBuffer
value,
5254 TdsParserStateObject stateObj
,
5255 SqlCommandColumnEncryptionSetting columnEncryptionOverride
,
5256 string columnName
) {
5257 bool isPlp
= md
.metaType
.IsPlp
;
5258 byte tdsType
= md
.tdsType
;
5260 Debug
.Assert(isPlp
|| !IsNull(md
.metaType
, (ulong)length
), "null value should not get here!");
5262 // We must read the column value completely, no matter what length is passed in
5263 length
= Int32
.MaxValue
;
5266 //DEVNOTE: When modifying the following routines (for deserialization) please pay attention to
5267 // deserialization code in DecryptWithKey () method and modify it accordingly.
5269 case TdsEnums
.SQLDECIMALN
:
5270 case TdsEnums
.SQLNUMERICN
:
5271 if (!TryReadSqlDecimal(value, length
, md
.precision
, md
.scale
, stateObj
)) {
5276 case TdsEnums
.SQLUDT
:
5277 case TdsEnums
.SQLBINARY
:
5278 case TdsEnums
.SQLBIGBINARY
:
5279 case TdsEnums
.SQLBIGVARBINARY
:
5280 case TdsEnums
.SQLVARBINARY
:
5281 case TdsEnums
.SQLIMAGE
:
5284 // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
5286 // If we are given -1 for length, then we read the entire value,
5287 // otherwise only the requested amount, usually first chunk.
5289 if (!stateObj
.TryReadPlpBytes(ref b
, 0, length
, out ignored
)) {
5294 //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
5295 b
= new byte[length
];
5296 if (!stateObj
.TryReadByteArray(b
, 0, length
)) {
5302 && ((columnEncryptionOverride
== SqlCommandColumnEncryptionSetting
.Enabled
5303 || columnEncryptionOverride
== SqlCommandColumnEncryptionSetting
.ResultSetOnly
)
5304 || (columnEncryptionOverride
== SqlCommandColumnEncryptionSetting
.UseConnectionSetting
5305 && _connHandler
!= null && _connHandler
.ConnectionOptions
!= null
5306 && _connHandler
.ConnectionOptions
.ColumnEncryptionSetting
== SqlConnectionColumnEncryptionSetting
.Enabled
))) {
5308 // CipherInfo is present, decrypt and read
5309 byte[] unencryptedBytes
= SqlSecurityUtility
.DecryptWithKey(b
, md
.cipherMD
, _connHandler
.ConnectionOptions
.DataSource
);
5311 if (unencryptedBytes
!= null) {
5312 DeserializeUnencryptedValue(value, unencryptedBytes
, md
, stateObj
, md
.NormalizationRuleVersion
);
5315 catch (Exception e
) {
5316 throw SQL
.ColumnDecryptionFailed(columnName
, null, e
);
5320 value.SqlBinary
= new SqlBinary(b
, true); // doesn't copy the byte array
5324 case TdsEnums
.SQLCHAR
:
5325 case TdsEnums
.SQLBIGCHAR
:
5326 case TdsEnums
.SQLVARCHAR
:
5327 case TdsEnums
.SQLBIGVARCHAR
:
5328 case TdsEnums
.SQLTEXT
:
5329 case TdsEnums
.SQLNCHAR
:
5330 case TdsEnums
.SQLNVARCHAR
:
5331 case TdsEnums
.SQLNTEXT
:
5332 if (!TryReadSqlStringValue(value, tdsType
, length
, md
.encoding
, isPlp
, stateObj
)) {
5337 case TdsEnums
.SQLXMLTYPE
:
5338 // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader.
5339 SqlCachedBuffer sqlBuf
;
5340 if (!SqlCachedBuffer
.TryCreate(md
, this, stateObj
, out sqlBuf
)) {
5344 value.SqlCachedBuffer
= sqlBuf
;
5347 case TdsEnums
.SQLDATE
:
5348 case TdsEnums
.SQLTIME
:
5349 case TdsEnums
.SQLDATETIME2
:
5350 case TdsEnums
.SQLDATETIMEOFFSET
:
5351 if (!TryReadSqlDateTime(value, tdsType
, length
, md
.scale
, stateObj
)) {
5357 Debug
.Assert(!isPlp
, "ReadSqlValue calling ReadSqlValueInternal with plp data");
5358 if (!TryReadSqlValueInternal(value, tdsType
, length
, stateObj
)) {
5364 Debug
.Assert((stateObj
._longlen
== 0) && (stateObj
._longlenleft
== 0), "ReadSqlValue did not read plp field completely, longlen =" + stateObj
._longlen
.ToString((IFormatProvider
)null) + ",longlenleft=" + stateObj
._longlenleft
.ToString((IFormatProvider
)null));
5368 private bool TryReadSqlDateTime(SqlBuffer
value, byte tdsType
, int length
, byte scale
, TdsParserStateObject stateObj
) {
5369 byte[] datetimeBuffer
= new byte[length
];
5371 if (!stateObj
.TryReadByteArray(datetimeBuffer
, 0, length
)) {
5376 case TdsEnums
.SQLDATE
:
5377 Debug
.Assert(length
== 3, "invalid length for date type!");
5378 value.SetToDate(datetimeBuffer
);
5381 case TdsEnums
.SQLTIME
:
5382 Debug
.Assert(3 <= length
&& length
<= 5, "invalid length for time type!");
5383 value.SetToTime(datetimeBuffer
, length
, scale
, scale
);
5386 case TdsEnums
.SQLDATETIME2
:
5387 Debug
.Assert(6 <= length
&& length
<= 8, "invalid length for datetime2 type!");
5388 value.SetToDateTime2(datetimeBuffer
, length
, scale
, scale
);
5391 case TdsEnums
.SQLDATETIMEOFFSET
:
5392 Debug
.Assert(8 <= length
&& length
<= 10, "invalid length for datetimeoffset type!");
5393 value.SetToDateTimeOffset(datetimeBuffer
, length
, scale
, scale
);
5397 Debug
.Assert(false, "ReadSqlDateTime is called with the wrong tdsType");
5404 internal bool TryReadSqlValueInternal(SqlBuffer
value, byte tdsType
, int length
, TdsParserStateObject stateObj
) {
5406 case TdsEnums
.SQLBIT
:
5407 case TdsEnums
.SQLBITN
:
5408 Debug
.Assert(length
== 1, "invalid length for SqlBoolean type!");
5410 if (!stateObj
.TryReadByte(out byteValue
)) {
5413 value.Boolean
= (byteValue
!= 0);
5416 case TdsEnums
.SQLINTN
:
5418 goto case TdsEnums
.SQLINT1
;
5420 else if (length
== 2) {
5421 goto case TdsEnums
.SQLINT2
;
5423 else if (length
== 4) {
5424 goto case TdsEnums
.SQLINT4
;
5427 goto case TdsEnums
.SQLINT8
;
5430 case TdsEnums
.SQLINT1
:
5431 Debug
.Assert(length
== 1, "invalid length for SqlByte type!");
5432 if (!stateObj
.TryReadByte(out byteValue
)) {
5435 value.Byte
= byteValue
;
5438 case TdsEnums
.SQLINT2
:
5439 Debug
.Assert(length
== 2, "invalid length for SqlInt16 type!");
5441 if (!stateObj
.TryReadInt16(out shortValue
)) {
5444 value.Int16
= shortValue
;
5447 case TdsEnums
.SQLINT4
:
5448 Debug
.Assert(length
== 4, "invalid length for SqlInt32 type!");
5450 if (!stateObj
.TryReadInt32(out intValue
)) {
5453 value.Int32
= intValue
;
5456 case TdsEnums
.SQLINT8
:
5457 Debug
.Assert(length
== 8, "invalid length for SqlInt64 type!");
5459 if (!stateObj
.TryReadInt64(out longValue
)) {
5462 value.Int64
= longValue
;
5465 case TdsEnums
.SQLFLTN
:
5467 goto case TdsEnums
.SQLFLT4
;
5470 goto case TdsEnums
.SQLFLT8
;
5473 case TdsEnums
.SQLFLT4
:
5474 Debug
.Assert(length
== 4, "invalid length for SqlSingle type!");
5476 if (!stateObj
.TryReadSingle(out singleValue
)) {
5479 value.Single
= singleValue
;
5482 case TdsEnums
.SQLFLT8
:
5483 Debug
.Assert(length
== 8, "invalid length for SqlDouble type!");
5485 if (!stateObj
.TryReadDouble(out doubleValue
)) {
5488 value.Double
= doubleValue
;
5491 case TdsEnums
.SQLMONEYN
:
5493 goto case TdsEnums
.SQLMONEY4
;
5496 goto case TdsEnums
.SQLMONEY
;
5499 case TdsEnums
.SQLMONEY
:
5504 if (!stateObj
.TryReadInt32(out mid
)) {
5507 if (!stateObj
.TryReadUInt32(out lo
)) {
5511 long l
= (((long)mid
) << 0x20) + ((long)lo
);
5513 value.SetToMoney(l
);
5517 case TdsEnums
.SQLMONEY4
:
5518 if (!stateObj
.TryReadInt32(out intValue
)) {
5521 value.SetToMoney(intValue
);
5524 case TdsEnums
.SQLDATETIMN
:
5526 goto case TdsEnums
.SQLDATETIM4
;
5529 goto case TdsEnums
.SQLDATETIME
;
5532 case TdsEnums
.SQLDATETIM4
:
5533 ushort daypartShort
, timepartShort
;
5534 if (!stateObj
.TryReadUInt16(out daypartShort
)) {
5537 if (!stateObj
.TryReadUInt16(out timepartShort
)) {
5540 value.SetToDateTime(daypartShort
, timepartShort
* SqlDateTime
.SQLTicksPerMinute
);
5543 case TdsEnums
.SQLDATETIME
:
5546 if (!stateObj
.TryReadInt32(out daypart
)) {
5549 if (!stateObj
.TryReadUInt32(out timepart
)) {
5552 value.SetToDateTime(daypart
, (int)timepart
);
5555 case TdsEnums
.SQLUNIQUEID
:
5557 Debug
.Assert(length
== 16, "invalid length for SqlGuid type!");
5559 byte[] b
= new byte[length
];
5561 if (!stateObj
.TryReadByteArray(b
, 0, length
)) {
5564 value.SqlGuid
= new SqlGuid(b
, true); // doesn't copy the byte array
5568 case TdsEnums
.SQLBINARY
:
5569 case TdsEnums
.SQLBIGBINARY
:
5570 case TdsEnums
.SQLBIGVARBINARY
:
5571 case TdsEnums
.SQLVARBINARY
:
5572 case TdsEnums
.SQLIMAGE
:
5574 // Note: Better not come here with plp data!!
5575 Debug
.Assert(length
<= TdsEnums
.MAXSIZE
);
5576 byte[] b
= new byte[length
];
5577 if (!stateObj
.TryReadByteArray(b
, 0, length
)) {
5580 value.SqlBinary
= new SqlBinary(b
, true); // doesn't copy the byte array
5585 case TdsEnums
.SQLVARIANT
:
5586 if (!TryReadSqlVariant(value, length
, stateObj
)) {
5592 Debug
.Assert(false, "Unknown SqlType!" + tdsType
.ToString(CultureInfo
.InvariantCulture
));
5600 // Read in a SQLVariant
5602 // SQLVariant looks like:
5607 // BYTE[] Properties
5610 internal bool TryReadSqlVariant(SqlBuffer
value, int lenTotal
, TdsParserStateObject stateObj
) {
5611 Debug
.Assert(_isShiloh
== true, "Shouldn't be dealing with sql_variaint in pre-SQL2000 server!");
5612 // get the SQLVariant type
5614 if (!stateObj
.TryReadByte(out type
)) {
5617 ushort lenMax
= 0; // maximum lenData of value inside variant
5621 if (!stateObj
.TryReadByte(out cbPropsActual
)) {
5624 MetaType mt
= MetaType
.GetSqlDataType(type
, 0 /*no user datatype*/, 0 /* no lenData, non-nullable type */);
5625 byte cbPropsExpected
= mt
.PropBytes
;
5627 int lenConsumed
= TdsEnums
.SQLVARIANT_SIZE
+ cbPropsActual
; // type, count of propBytes, and actual propBytes
5628 int lenData
= lenTotal
- lenConsumed
; // length of actual data
5630 // read known properties and skip unknown properties
5631 Debug
.Assert(cbPropsActual
>= cbPropsExpected
, "cbPropsActual is less that cbPropsExpected!");
5634 // now read the value
5637 case TdsEnums
.SQLBIT
:
5638 case TdsEnums
.SQLINT1
:
5639 case TdsEnums
.SQLINT2
:
5640 case TdsEnums
.SQLINT4
:
5641 case TdsEnums
.SQLINT8
:
5642 case TdsEnums
.SQLFLT4
:
5643 case TdsEnums
.SQLFLT8
:
5644 case TdsEnums
.SQLMONEY
:
5645 case TdsEnums
.SQLMONEY4
:
5646 case TdsEnums
.SQLDATETIME
:
5647 case TdsEnums
.SQLDATETIM4
:
5648 case TdsEnums
.SQLUNIQUEID
:
5649 if (!TryReadSqlValueInternal(value, type
, lenData
, stateObj
)) {
5654 case TdsEnums
.SQLDECIMALN
:
5655 case TdsEnums
.SQLNUMERICN
:
5657 Debug
.Assert(cbPropsExpected
== 2, "SqlVariant: invalid PropBytes for decimal/numeric type!");
5660 if (!stateObj
.TryReadByte(out precision
)) {
5664 if (!stateObj
.TryReadByte(out scale
)) {
5668 // skip over unknown properties
5669 if (cbPropsActual
> cbPropsExpected
) {
5670 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5675 if (!TryReadSqlDecimal(value, TdsEnums
.MAX_NUMERIC_LEN
, precision
, scale
, stateObj
)) {
5681 case TdsEnums
.SQLBIGBINARY
:
5682 case TdsEnums
.SQLBIGVARBINARY
:
5683 //Debug.Assert(TdsEnums.VARNULL == lenData, "SqlVariant: data length for Binary indicates null?");
5684 Debug
.Assert(cbPropsExpected
== 2, "SqlVariant: invalid PropBytes for binary type!");
5686 if (!stateObj
.TryReadUInt16(out lenMax
)) {
5689 Debug
.Assert(lenMax
!= TdsEnums
.SQL_USHORTVARMAXLEN
, "bigvarbinary(max) in a sqlvariant");
5691 // skip over unknown properties
5692 if (cbPropsActual
> cbPropsExpected
) {
5693 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5698 goto case TdsEnums
.SQLBIT
;
5700 case TdsEnums
.SQLBIGCHAR
:
5701 case TdsEnums
.SQLBIGVARCHAR
:
5702 case TdsEnums
.SQLNCHAR
:
5703 case TdsEnums
.SQLNVARCHAR
:
5705 Debug
.Assert(cbPropsExpected
== 7, "SqlVariant: invalid PropBytes for character type!");
5707 SqlCollation collation
;
5708 if (!TryProcessCollation(stateObj
, out collation
)) {
5712 if (!stateObj
.TryReadUInt16(out lenMax
)) {
5715 Debug
.Assert(lenMax
!= TdsEnums
.SQL_USHORTVARMAXLEN
, "bigvarchar(max) or nvarchar(max) in a sqlvariant");
5717 // skip over unknown properties
5718 if (cbPropsActual
> cbPropsExpected
) {
5719 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5724 Encoding encoding
= Encoding
.GetEncoding(GetCodePage(collation
, stateObj
));
5725 if (!TryReadSqlStringValue(value, type
, lenData
, encoding
, false, stateObj
)) {
5730 case TdsEnums
.SQLDATE
:
5731 if (!TryReadSqlDateTime(value, type
, lenData
, 0, stateObj
)) {
5736 case TdsEnums
.SQLTIME
:
5737 case TdsEnums
.SQLDATETIME2
:
5738 case TdsEnums
.SQLDATETIMEOFFSET
:
5740 Debug
.Assert(cbPropsExpected
== 1, "SqlVariant: invalid PropBytes for time/datetime2/datetimeoffset type!");
5743 if (!stateObj
.TryReadByte(out scale
)) {
5747 // skip over unknown properties
5748 if (cbPropsActual
> cbPropsExpected
) {
5749 if (!stateObj
.TrySkipBytes(cbPropsActual
- cbPropsExpected
)) {
5754 if (!TryReadSqlDateTime(value, type
, lenData
, scale
, stateObj
)) {
5761 Debug
.Assert(false, "Unknown tds type in SqlVariant!" + type
.ToString(CultureInfo
.InvariantCulture
));
5769 // Translates a com+ object -> SqlVariant
5770 // when the type is ambiguous, we always convert to the bigger type
5771 // note that we also write out the maxlen and actuallen members (4 bytes each)
5772 // in addition to the SQLVariant structure
5774 internal Task
WriteSqlVariantValue(object value, int length
, int offset
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
5775 Debug
.Assert(_isShiloh
== true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
5777 // handle null values
5778 if (ADP
.IsNull(value)) {
5779 WriteInt(TdsEnums
.FIXEDNULL
, stateObj
); //maxlen
5780 WriteInt(TdsEnums
.FIXEDNULL
, stateObj
); //actuallen
5784 MetaType mt
= MetaType
.GetMetaTypeFromValue(value);
5786 // Special case data type correction for SqlMoney inside a SqlVariant.
5787 if ((TdsEnums
.SQLNUMERICN
== mt
.TDSType
) && (8 == length
)) {
5788 // The caller will coerce all SqlTypes to native CLR types, which means SqlMoney will
5789 // coerce to decimal/SQLNUMERICN (via SqlMoney.Value call). In the case where the original
5790 // value was SqlMoney the caller will also pass in the metadata length for the SqlMoney type
5791 // which is 8 bytes. To honor the intent of the caller here we coerce this special case
5792 // input back to SqlMoney from decimal/SQLNUMERICN.
5793 mt
= MetaType
.GetMetaTypeFromValue(new SqlMoney((decimal)value));
5796 if (mt
.IsAnsiType
) {
5797 length
= GetEncodingCharLength((string)value, length
, 0, _defaultEncoding
);
5800 // max and actual len are equal to
5801 // SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
5802 WriteInt(TdsEnums
.SQLVARIANT_SIZE
+ mt
.PropBytes
+ length
, stateObj
); // maxLen
5803 WriteInt(TdsEnums
.SQLVARIANT_SIZE
+ mt
.PropBytes
+ length
, stateObj
); // actualLen
5805 // write the SQLVariant header (type and cbPropBytes)
5806 stateObj
.WriteByte(mt
.TDSType
);
5807 stateObj
.WriteByte(mt
.PropBytes
);
5809 // now write the actual PropBytes and data
5810 switch (mt
.TDSType
) {
5811 case TdsEnums
.SQLFLT4
:
5812 WriteFloat((Single
)value, stateObj
);
5815 case TdsEnums
.SQLFLT8
:
5816 WriteDouble((Double
)value, stateObj
);
5819 case TdsEnums
.SQLINT8
:
5820 WriteLong((Int64
)value, stateObj
);
5823 case TdsEnums
.SQLINT4
:
5824 WriteInt((Int32
)value, stateObj
);
5827 case TdsEnums
.SQLINT2
:
5828 WriteShort((Int16
)value, stateObj
);
5831 case TdsEnums
.SQLINT1
:
5832 stateObj
.WriteByte((byte)value);
5835 case TdsEnums
.SQLBIT
:
5836 if ((bool)value == true)
5837 stateObj
.WriteByte(1);
5839 stateObj
.WriteByte(0);
5843 case TdsEnums
.SQLBIGVARBINARY
:
5845 byte[] b
= (byte[])value;
5847 WriteShort(length
, stateObj
); // propbytes: varlen
5848 return stateObj
.WriteByteArray(b
, length
, offset
, canAccumulate
);
5851 case TdsEnums
.SQLBIGVARCHAR
:
5853 string s
= (string)value;
5855 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
5856 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
5857 WriteShort(length
, stateObj
); // propbyte: varlen
5858 return WriteEncodingChar(s
, _defaultEncoding
, stateObj
, canAccumulate
);
5861 case TdsEnums
.SQLUNIQUEID
:
5863 System
.Guid guid
= (System
.Guid
)value;
5864 byte[] b
= guid
.ToByteArray();
5866 Debug
.Assert((length
== b
.Length
) && (length
== 16), "Invalid length for guid type in com+ object");
5867 stateObj
.WriteByteArray(b
, length
, 0);
5871 case TdsEnums
.SQLNVARCHAR
:
5873 string s
= (string)value;
5875 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
5876 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
5877 WriteShort(length
, stateObj
); // propbyte: varlen
5879 // string takes cchar, not cbyte so convert
5881 return WriteString(s
, length
, offset
, stateObj
, canAccumulate
);
5884 case TdsEnums
.SQLDATETIME
:
5886 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, 8);
5888 WriteInt(dt
.days
, stateObj
);
5889 WriteInt(dt
.time
, stateObj
);
5893 case TdsEnums
.SQLMONEY
:
5895 WriteCurrency((Decimal
)value, 8, stateObj
);
5899 case TdsEnums
.SQLNUMERICN
:
5901 stateObj
.WriteByte(mt
.Precision
); //propbytes: precision
5902 stateObj
.WriteByte((byte)((Decimal
.GetBits((Decimal
)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
5903 WriteDecimal((Decimal
)value, stateObj
);
5907 case TdsEnums
.SQLTIME
:
5908 stateObj
.WriteByte(mt
.Scale
); //propbytes: scale
5909 WriteTime((TimeSpan
)value, mt
.Scale
, length
, stateObj
);
5912 case TdsEnums
.SQLDATETIMEOFFSET
:
5913 stateObj
.WriteByte(mt
.Scale
); //propbytes: scale
5914 WriteDateTimeOffset((DateTimeOffset
)value, mt
.Scale
, length
, stateObj
);
5918 Debug
.Assert(false, "unknown tds type for sqlvariant!");
5921 // return point for accumulated writes, note: non-accumulated writes returned from their case statements
5925 // todo: since we now know the difference between SqlWriteVariantValue and SqlWriteRowDataVariant we should consider
5926 // combining these tow methods.
5929 // Translates a com+ object -> SqlVariant
5930 // when the type is ambiguous, we always convert to the bigger type
5931 // note that we also write out the maxlen and actuallen members (4 bytes each)
5932 // in addition to the SQLVariant structure
5934 // Devnote: DataRows are preceeded by Metadata. The Metadata includes the MaxLen value.
5935 // Therefore the sql_variant value must not include the MaxLength. This is the major difference
5936 // between this method and WriteSqlVariantValue above.
5938 internal Task
WriteSqlVariantDataRowValue(object value, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
5939 Debug
.Assert(_isShiloh
== true, "Shouldn't be dealing with sql_variant in pre-SQL2000 server!");
5941 // handle null values
5942 if ((null == value) || (DBNull
.Value
== value)) {
5943 WriteInt(TdsEnums
.FIXEDNULL
, stateObj
);
5947 MetaType metatype
= MetaType
.GetMetaTypeFromValue(value);
5950 if (metatype
.IsAnsiType
) {
5951 length
= GetEncodingCharLength((string)value, length
, 0, _defaultEncoding
);
5954 switch (metatype
.TDSType
) {
5955 case TdsEnums
.SQLFLT4
:
5956 WriteSqlVariantHeader(6, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5957 WriteFloat((Single
)value, stateObj
);
5960 case TdsEnums
.SQLFLT8
:
5961 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5962 WriteDouble((Double
)value, stateObj
);
5965 case TdsEnums
.SQLINT8
:
5966 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5967 WriteLong((Int64
)value, stateObj
);
5970 case TdsEnums
.SQLINT4
:
5971 WriteSqlVariantHeader(6, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5972 WriteInt((Int32
)value, stateObj
);
5975 case TdsEnums
.SQLINT2
:
5976 WriteSqlVariantHeader(4, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5977 WriteShort((Int16
)value, stateObj
);
5980 case TdsEnums
.SQLINT1
:
5981 WriteSqlVariantHeader(3, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5982 stateObj
.WriteByte((byte)value);
5985 case TdsEnums
.SQLBIT
:
5986 WriteSqlVariantHeader(3, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
5987 if ((bool)value == true)
5988 stateObj
.WriteByte(1);
5990 stateObj
.WriteByte(0);
5994 case TdsEnums
.SQLBIGVARBINARY
:
5996 byte[] b
= (byte[])value;
5999 WriteSqlVariantHeader(4 + length
, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6000 WriteShort(length
, stateObj
); // propbytes: varlen
6001 return stateObj
.WriteByteArray(b
, length
, 0, canAccumulate
);
6004 case TdsEnums
.SQLBIGVARCHAR
:
6006 string s
= (string)value;
6009 WriteSqlVariantHeader(9 + length
, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6010 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
6011 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
6012 WriteShort(length
, stateObj
);
6013 return WriteEncodingChar(s
, _defaultEncoding
, stateObj
, canAccumulate
);
6016 case TdsEnums
.SQLUNIQUEID
:
6018 System
.Guid guid
= (System
.Guid
)value;
6019 byte[] b
= guid
.ToByteArray();
6022 Debug
.Assert(length
== 16, "Invalid length for guid type in com+ object");
6023 WriteSqlVariantHeader(18, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6024 stateObj
.WriteByteArray(b
, length
, 0);
6028 case TdsEnums
.SQLNVARCHAR
:
6030 string s
= (string)value;
6032 length
= s
.Length
* 2;
6033 WriteSqlVariantHeader(9 + length
, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6034 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); // propbytes: collation.Info
6035 stateObj
.WriteByte(_defaultCollation
.sortId
); // propbytes: collation.SortId
6036 WriteShort(length
, stateObj
); // propbyte: varlen
6038 // string takes cchar, not cbyte so convert
6040 return WriteString(s
, length
, 0, stateObj
, canAccumulate
);
6043 case TdsEnums
.SQLDATETIME
:
6045 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, 8);
6047 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6048 WriteInt(dt
.days
, stateObj
);
6049 WriteInt(dt
.time
, stateObj
);
6053 case TdsEnums
.SQLMONEY
:
6055 WriteSqlVariantHeader(10, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6056 WriteCurrency((Decimal
)value, 8, stateObj
);
6060 case TdsEnums
.SQLNUMERICN
:
6062 WriteSqlVariantHeader(21, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6063 stateObj
.WriteByte(metatype
.Precision
); //propbytes: precision
6064 stateObj
.WriteByte((byte)((Decimal
.GetBits((Decimal
)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
6065 WriteDecimal((Decimal
)value, stateObj
);
6069 case TdsEnums
.SQLTIME
:
6070 WriteSqlVariantHeader(8, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6071 stateObj
.WriteByte(metatype
.Scale
); //propbytes: scale
6072 WriteTime((TimeSpan
)value, metatype
.Scale
, 5, stateObj
);
6075 case TdsEnums
.SQLDATETIMEOFFSET
:
6076 WriteSqlVariantHeader(13, metatype
.TDSType
, metatype
.PropBytes
, stateObj
);
6077 stateObj
.WriteByte(metatype
.Scale
); //propbytes: scale
6078 WriteDateTimeOffset((DateTimeOffset
)value, metatype
.Scale
, 10, stateObj
);
6082 Debug
.Assert(false, "unknown tds type for sqlvariant!");
6085 // return point for accumualated writes, note: non-accumulated writes returned from their case statements
6089 internal void WriteSqlVariantHeader(int length
, byte tdstype
, byte propbytes
, TdsParserStateObject stateObj
) {
6090 WriteInt(length
, stateObj
);
6091 stateObj
.WriteByte(tdstype
);
6092 stateObj
.WriteByte(propbytes
);
6095 internal void WriteSqlVariantDateTime2(DateTime
value, TdsParserStateObject stateObj
)
6097 MSS
.SmiMetaData dateTime2MetaData
= MSS
.SmiMetaData
.DefaultDateTime2
;
6098 // NOTE: 3 bytes added here to support additional header information for variant - internal type, scale prop, scale
6099 WriteSqlVariantHeader((int)(dateTime2MetaData
.MaxLength
+ 3), TdsEnums
.SQLDATETIME2
, 1 /* one scale prop */, stateObj
);
6100 stateObj
.WriteByte(dateTime2MetaData
.Scale
); //scale property
6101 WriteDateTime2(value, dateTime2MetaData
.Scale
, (int)(dateTime2MetaData
.MaxLength
), stateObj
);
6104 internal void WriteSqlVariantDate(DateTime
value, TdsParserStateObject stateObj
)
6106 MSS
.SmiMetaData dateMetaData
= MSS
.SmiMetaData
.DefaultDate
;
6107 // NOTE: 2 bytes added here to support additional header information for variant - internal type, scale prop (ignoring scale here)
6108 WriteSqlVariantHeader((int)(dateMetaData
.MaxLength
+ 2), TdsEnums
.SQLDATE
, 0 /* one scale prop */, stateObj
);
6109 WriteDate(value, stateObj
);
6112 private byte[] SerializeSqlMoney(SqlMoney
value, int length
, TdsParserStateObject stateObj
) {
6113 return SerializeCurrency(value.Value
, length
, stateObj
);
6116 private void WriteSqlMoney(SqlMoney
value, int length
, TdsParserStateObject stateObj
) {
6118 int[] bits
= Decimal
.GetBits(value.Value
);
6120 // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6121 bool isNeg
= (0 != (bits
[3] & unchecked((int)0x80000000)));
6122 long l
= ((long)(uint)bits
[1]) << 0x20 | (uint)bits
[0];
6128 Decimal decimalValue
= value.Value
;
6130 // validate the value can be represented as a small money
6131 if (decimalValue
< TdsEnums
.SQL_SMALL_MONEY_MIN
|| decimalValue
> TdsEnums
.SQL_SMALL_MONEY_MAX
) {
6132 throw SQL
.MoneyOverflow(decimalValue
.ToString(CultureInfo
.InvariantCulture
));
6135 WriteInt((int)l
, stateObj
);
6138 WriteInt((int)(l
>> 0x20), stateObj
);
6139 WriteInt((int)l
, stateObj
);
6143 private byte[] SerializeCurrency(Decimal
value, int length
, TdsParserStateObject stateObj
) {
6144 SqlMoney m
= new SqlMoney(value);
6145 int[] bits
= Decimal
.GetBits(m
.Value
);
6147 // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6148 bool isNeg
= (0 != (bits
[3] & unchecked((int)0x80000000)));
6149 long l
= ((long)(uint)bits
[1]) << 0x20 | (uint)bits
[0];
6155 // validate the value can be represented as a small money
6156 if (value < TdsEnums
.SQL_SMALL_MONEY_MIN
|| value > TdsEnums
.SQL_SMALL_MONEY_MAX
) {
6157 throw SQL
.MoneyOverflow(value.ToString(CultureInfo
.InvariantCulture
));
6160 // We normalize to allow conversion across data types. SMALLMONEY is serialized into a MONEY.
6164 Debug
.Assert (8 == length
, "invalid length in SerializeCurrency");
6165 if (null == stateObj
._bLongBytes
) {
6166 stateObj
._bLongBytes
= new byte[8];
6169 byte[] bytes
= stateObj
._bLongBytes
;
6172 byte[] bytesPart
= SerializeInt((int)(l
>> 0x20), stateObj
);
6173 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6176 bytesPart
= SerializeInt((int)l
, stateObj
);
6177 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6182 private void WriteCurrency(Decimal
value, int length
, TdsParserStateObject stateObj
) {
6183 SqlMoney m
= new SqlMoney(value);
6184 int[] bits
= Decimal
.GetBits(m
.Value
);
6186 // this decimal should be scaled by 10000 (regardless of what the incoming decimal was scaled by)
6187 bool isNeg
= (0 != (bits
[3] & unchecked((int)0x80000000)));
6188 long l
= ((long)(uint)bits
[1]) << 0x20 | (uint)bits
[0];
6194 // validate the value can be represented as a small money
6195 if (value < TdsEnums
.SQL_SMALL_MONEY_MIN
|| value > TdsEnums
.SQL_SMALL_MONEY_MAX
) {
6196 throw SQL
.MoneyOverflow(value.ToString(CultureInfo
.InvariantCulture
));
6199 WriteInt((int)l
, stateObj
);
6202 WriteInt((int)(l
>> 0x20), stateObj
);
6203 WriteInt((int)l
, stateObj
);
6207 private byte[] SerializeDate(DateTime
value) {
6208 long days
= value.Subtract(DateTime
.MinValue
).Days
;
6209 return SerializePartialLong(days
, 3);
6212 private void WriteDate(DateTime
value, TdsParserStateObject stateObj
) {
6213 long days
= value.Subtract(DateTime
.MinValue
).Days
;
6214 WritePartialLong(days
, 3, stateObj
);
6217 private byte[] SerializeTime(TimeSpan
value, byte scale
, int length
) {
6218 if (0 > value.Ticks
|| value.Ticks
>= TimeSpan
.TicksPerDay
) {
6219 throw SQL
.TimeOverflow(value.ToString());
6222 long time
= value.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
];
6224 // We normalize to maximum precision to allow conversion across different precisions.
6225 time
= time
* TdsEnums
.TICKS_FROM_SCALE
[scale
];
6226 length
= TdsEnums
.MAX_TIME_LENGTH
;
6228 return SerializePartialLong(time
, length
);
6231 private void WriteTime(TimeSpan
value, byte scale
, int length
, TdsParserStateObject stateObj
) {
6232 if (0 > value.Ticks
|| value.Ticks
>= TimeSpan
.TicksPerDay
) {
6233 throw SQL
.TimeOverflow(value.ToString());
6235 long time
= value.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
];
6236 WritePartialLong(time
, length
, stateObj
);
6239 private byte[] SerializeDateTime2(DateTime
value, byte scale
, int length
) {
6240 long time
= value.TimeOfDay
.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
6242 // We normalize to maximum precision to allow conversion across different precisions.
6243 time
= time
* TdsEnums
.TICKS_FROM_SCALE
[scale
];
6244 length
= TdsEnums
.MAX_DATETIME2_LENGTH
;
6246 byte[] bytes
= new byte[length
];
6250 bytesPart
= SerializePartialLong(time
, length
- 3);
6251 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, length
- 3);
6252 current
+= length
- 3;
6254 bytesPart
= SerializeDate(value);
6255 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 3);
6260 private void WriteDateTime2(DateTime
value, byte scale
, int length
, TdsParserStateObject stateObj
) {
6261 long time
= value.TimeOfDay
.Ticks
/ TdsEnums
.TICKS_FROM_SCALE
[scale
]; // DateTime.TimeOfDay always returns a valid TimeSpan for Time
6262 WritePartialLong(time
, length
- 3, stateObj
);
6263 WriteDate(value, stateObj
);
6266 private byte[] SerializeDateTimeOffset(DateTimeOffset
value, byte scale
, int length
) {
6270 bytesPart
= SerializeDateTime2(value.UtcDateTime
, scale
, length
- 2);
6272 // We need to allocate the array after we have received the length of the serialized value
6273 // since it might be higher due to normalization.
6274 length
= bytesPart
.Length
+ 2;
6275 byte[] bytes
= new byte[length
];
6277 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, length
- 2);
6278 current
+= length
- 2;
6280 Int16 offset
= (Int16
)value.Offset
.TotalMinutes
;
6281 bytes
[current
++] = (byte)(offset
& 0xff);
6282 bytes
[current
++] = (byte)((offset
>> 8) & 0xff);
6287 private void WriteDateTimeOffset(DateTimeOffset
value, byte scale
, int length
, TdsParserStateObject stateObj
) {
6288 WriteDateTime2(value.UtcDateTime
, scale
, length
- 2, stateObj
);
6289 Int16 offset
= (Int16
)value.Offset
.TotalMinutes
;
6290 stateObj
.WriteByte((byte)(offset
& 0xff));
6291 stateObj
.WriteByte((byte)((offset
>> 8) & 0xff));
6294 private bool TryReadSqlDecimal(SqlBuffer
value, int length
, byte precision
, byte scale
, TdsParserStateObject stateObj
) {
6296 if (!stateObj
.TryReadByte(out byteValue
)) {
6299 bool fPositive
= (1 == byteValue
);
6301 length
= checked((int)length
-1);
6304 if (!TryReadDecimalBits(length
, stateObj
, out bits
)) {
6308 value.SetToDecimal(precision
, scale
, fPositive
, bits
);
6312 // @devnote: length should be size of decimal without the sign
6313 // @devnote: sign should have already been read off the wire
6314 private bool TryReadDecimalBits(int length
, TdsParserStateObject stateObj
, out int[] bits
) {
6315 bits
= stateObj
._decimalBits
; // used alloc'd array if we have one already
6321 for (i
= 0; i
< bits
.Length
; i
++)
6325 Debug
.Assert((length
> 0) &&
6326 (length
<= TdsEnums
.MAX_NUMERIC_LEN
- 1) &&
6327 (length
% 4 == 0), "decimal should have 4, 8, 12, or 16 bytes of data");
6329 int decLength
= length
>> 2;
6331 for (i
= 0; i
< decLength
; i
++) {
6332 // up to 16 bytes of data following the sign byte
6333 if (!stateObj
.TryReadInt32(out bits
[i
])) {
6341 static internal SqlDecimal
AdjustSqlDecimalScale(SqlDecimal d
, int newScale
) {
6342 if (d
.Scale
!= newScale
) {
6343 return SqlDecimal
.AdjustScale(d
, newScale
- d
.Scale
, false /* Don't round, truncate. MDAC 69229 */);
6349 static internal decimal AdjustDecimalScale(decimal value, int newScale
) {
6350 int oldScale
= (Decimal
.GetBits(value)[3] & 0x00ff0000) >> 0x10;
6352 if (newScale
!= oldScale
) {
6353 SqlDecimal num
= new SqlDecimal(value);
6355 num
= SqlDecimal
.AdjustScale(num
, newScale
- oldScale
, false /* Don't round, truncate. MDAC 69229 */);
6362 internal byte[] SerializeSqlDecimal(SqlDecimal d
, TdsParserStateObject stateObj
) {
6363 if (null == stateObj
._bDecimalBytes
) {
6364 stateObj
._bDecimalBytes
= new byte[17];
6367 byte[] bytes
= stateObj
._bDecimalBytes
;
6372 bytes
[current
++] = 1;
6374 bytes
[current
++] = 0;
6376 byte[] bytesPart
= SerializeUnsignedInt(d
.m_data1
, stateObj
);
6377 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6379 bytesPart
= SerializeUnsignedInt(d
.m_data2
, stateObj
);
6380 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6382 bytesPart
= SerializeUnsignedInt(d
.m_data3
, stateObj
);
6383 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6385 bytesPart
= SerializeUnsignedInt(d
.m_data4
, stateObj
);
6386 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6391 internal void WriteSqlDecimal(SqlDecimal d
, TdsParserStateObject stateObj
) {
6394 stateObj
.WriteByte(1);
6396 stateObj
.WriteByte(0);
6398 WriteUnsignedInt(d
.m_data1
, stateObj
);
6399 WriteUnsignedInt(d
.m_data2
, stateObj
);
6400 WriteUnsignedInt(d
.m_data3
, stateObj
);
6401 WriteUnsignedInt(d
.m_data4
, stateObj
);
6404 private byte[] SerializeDecimal(decimal value, TdsParserStateObject stateObj
) {
6405 int[] decimalBits
= Decimal
.GetBits(value);
6406 if (null == stateObj
._bDecimalBytes
) {
6407 stateObj
._bDecimalBytes
= new byte[17];
6410 byte[] bytes
= stateObj
._bDecimalBytes
;
6414 Returns a binary representation of a Decimal. The return value is an integer
6415 array with four elements. Elements 0, 1, and 2 contain the low, middle, and
6416 high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
6417 the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
6418 unused; bits 16-23 contain a value between 0 and 28, indicating the power of
6419 10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
6420 30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
6421 meaning positive and 1 meaning negative.
6423 SQLDECIMAL/SQLNUMERIC has a byte stream of:
6425 BYTE sign; // 1 if positive, 0 if negative
6429 For TDS 7.0 and above, there are always 17 bytes of data
6432 // write the sign (note that COM and SQL are opposite)
6433 if (0x80000000 == (decimalBits
[3] & 0x80000000))
6434 bytes
[current
++] = 0;
6436 bytes
[current
++] = 1;
6438 byte[] bytesPart
= SerializeInt(decimalBits
[0], stateObj
);
6439 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6441 bytesPart
= SerializeInt(decimalBits
[1], stateObj
);
6442 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6444 bytesPart
= SerializeInt(decimalBits
[2], stateObj
);
6445 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6447 bytesPart
= SerializeInt(0, stateObj
);
6448 Buffer
.BlockCopy(bytesPart
, 0, bytes
, current
, 4);
6453 private void WriteDecimal(decimal value, TdsParserStateObject stateObj
) {
6454 stateObj
._decimalBits
= Decimal
.GetBits(value);
6455 Debug
.Assert(null != stateObj
._decimalBits
, "decimalBits should be filled in at TdsExecuteRPC time");
6458 Returns a binary representation of a Decimal. The return value is an integer
6459 array with four elements. Elements 0, 1, and 2 contain the low, middle, and
6460 high 32 bits of the 96-bit integer part of the Decimal. Element 3 contains
6461 the scale factor and sign of the Decimal: bits 0-15 (the lower word) are
6462 unused; bits 16-23 contain a value between 0 and 28, indicating the power of
6463 10 to divide the 96-bit integer part by to produce the Decimal value; bits 24-
6464 30 are unused; and finally bit 31 indicates the sign of the Decimal value, 0
6465 meaning positive and 1 meaning negative.
6467 SQLDECIMAL/SQLNUMERIC has a byte stream of:
6469 BYTE sign; // 1 if positive, 0 if negative
6473 For TDS 7.0 and above, there are always 17 bytes of data
6476 // write the sign (note that COM and SQL are opposite)
6477 if (0x80000000 == (stateObj
._decimalBits
[3] & 0x80000000))
6478 stateObj
.WriteByte(0);
6480 stateObj
.WriteByte(1);
6482 WriteInt(stateObj
._decimalBits
[0], stateObj
);
6483 WriteInt(stateObj
._decimalBits
[1], stateObj
);
6484 WriteInt(stateObj
._decimalBits
[2], stateObj
);
6485 WriteInt(0, stateObj
);
6488 private void WriteIdentifier(string s
, TdsParserStateObject stateObj
) {
6490 stateObj
.WriteByte(checked((byte)s
.Length
));
6491 WriteString(s
, stateObj
);
6494 stateObj
.WriteByte((byte)0);
6498 private void WriteIdentifierWithShortLength(string s
, TdsParserStateObject stateObj
) {
6500 WriteShort(checked((short)s
.Length
), stateObj
);
6501 WriteString(s
, stateObj
);
6504 WriteShort(0, stateObj
);
6508 private Task
WriteString(string s
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6509 return WriteString(s
, s
.Length
, 0, stateObj
, canAccumulate
);
6512 internal byte[] SerializeCharArray(char[] carr
, int length
, int offset
) {
6513 int cBytes
= ADP
.CharSize
* length
;
6514 byte[] bytes
= new byte[cBytes
];
6516 CopyCharsToBytes(carr
, offset
, bytes
, 0, length
);
6520 internal Task
WriteCharArray(char[] carr
, int length
, int offset
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6521 int cBytes
= ADP
.CharSize
* length
;
6523 // Perf shortcut: If it fits, write directly to the outBuff
6524 if(cBytes
< (stateObj
._outBuff
.Length
- stateObj
._outBytesUsed
)) {
6525 CopyCharsToBytes(carr
, offset
, stateObj
._outBuff
, stateObj
._outBytesUsed
, length
);
6526 stateObj
._outBytesUsed
+= cBytes
;
6530 if (stateObj
._bTmp
== null || stateObj
._bTmp
.Length
< cBytes
) {
6531 stateObj
._bTmp
= new byte[cBytes
];
6534 CopyCharsToBytes(carr
, offset
, stateObj
._bTmp
, 0, length
);
6535 return stateObj
.WriteByteArray(stateObj
._bTmp
, cBytes
, 0, canAccumulate
);
6539 internal byte[] SerializeString(string s
, int length
, int offset
) {
6540 int cBytes
= ADP
.CharSize
* length
;
6541 byte[] bytes
= new byte[cBytes
];
6543 CopyStringToBytes(s
, offset
, bytes
, 0, length
);
6547 internal Task
WriteString(string s
, int length
, int offset
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6548 int cBytes
= ADP
.CharSize
* length
;
6550 // Perf shortcut: If it fits, write directly to the outBuff
6551 if(cBytes
< (stateObj
._outBuff
.Length
- stateObj
._outBytesUsed
)) {
6552 CopyStringToBytes(s
, offset
, stateObj
._outBuff
, stateObj
._outBytesUsed
, length
);
6553 stateObj
._outBytesUsed
+= cBytes
;
6557 if (stateObj
._bTmp
== null || stateObj
._bTmp
.Length
< cBytes
) {
6558 stateObj
._bTmp
= new byte[cBytes
];
6561 CopyStringToBytes(s
, offset
, stateObj
._bTmp
, 0, length
);
6562 return stateObj
.WriteByteArray(stateObj
._bTmp
, cBytes
, 0, canAccumulate
);
6567 private unsafe static void CopyCharsToBytes(char[] source
, int sourceOffset
, byte[] dest
, int destOffset
, int charLength
) {
6568 // DEVNOTE: BE EXTREMELY CAREFULL in this method. Since it pins the buffers and copies the memory
6569 // directly, it bypasses all of the CLR's usual array-bounds checking. For maintainability, the checks
6570 // here should NOT be removed just because some other code will check them ahead of time.
6571 if (charLength
< 0) {
6572 throw ADP
.InvalidDataLength(charLength
);
6575 if (checked(sourceOffset
+ charLength
) > source
.Length
|| sourceOffset
< 0) {
6576 throw ADP
.IndexOutOfRange(sourceOffset
);
6579 // charLength >= 0 & checked conversion implies byteLength >= 0
6580 int byteLength
= checked(charLength
* ADP
.CharSize
);
6582 if (checked(destOffset
+ byteLength
) > dest
.Length
|| destOffset
< 0) {
6583 throw ADP
.IndexOutOfRange(destOffset
);
6586 fixed (char* sourcePtr
= source
) {
6587 char* srcPtr
= sourcePtr
; // Can't increment the target of a Fixed statement
6588 srcPtr
+= sourceOffset
; // char* increments by sizeof(char)
6589 fixed (byte* destinationPtr
= dest
) {
6590 byte* destPtr
= destinationPtr
;
6591 destPtr
+= destOffset
;
6592 NativeOledbWrapper
.MemoryCopy((IntPtr
)destPtr
, (IntPtr
)srcPtr
, byteLength
);
6597 private unsafe static void CopyStringToBytes(string source
, int sourceOffset
, byte[] dest
, int destOffset
, int charLength
) {
6598 // DEVNOTE: BE EXTREMELY CAREFULL in this method. Since it pins the buffers and copies the memory
6599 // directly, it bypasses all of the CLR's usual array-bounds checking. For maintainability, the checks
6600 // here should NOT be removed just because some other code will check them ahead of time.
6601 if (charLength
< 0) {
6602 throw ADP
.InvalidDataLength(charLength
);
6605 if (checked(sourceOffset
+ charLength
) > source
.Length
|| sourceOffset
< 0) {
6606 throw ADP
.IndexOutOfRange(sourceOffset
);
6609 // charLength >= 0 & checked conversion implies byteLength >= 0
6610 int byteLength
= checked(charLength
* ADP
.CharSize
);
6612 if (checked(destOffset
+ byteLength
) > dest
.Length
|| destOffset
< 0) {
6613 throw ADP
.IndexOutOfRange(destOffset
);
6616 fixed (char* sourcePtr
= source
) {
6617 char* srcPtr
= sourcePtr
; // Can't increment the target of a Fixed statement
6618 srcPtr
+= sourceOffset
; // char* increments by sizeof(char)
6619 fixed (byte* destinationPtr
= dest
) {
6620 byte* destPtr
= destinationPtr
;
6621 destPtr
+= destOffset
;
6622 NativeOledbWrapper
.MemoryCopy((IntPtr
)destPtr
, (IntPtr
)srcPtr
, byteLength
);
6627 private Task
WriteEncodingChar(string s
, Encoding encoding
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6628 return WriteEncodingChar(s
, s
.Length
, 0, encoding
, stateObj
, canAccumulate
);
6631 private byte[] SerializeEncodingChar(string s
, int numChars
, int offset
, Encoding encoding
) {
6633 byte[] byteData
= null;
6635 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6636 // 7.0 has no support for multiple code pages in data - single code page support only
6637 if (encoding
== null)
6638 encoding
= _defaultEncoding
;
6640 charData
= s
.ToCharArray(offset
, numChars
);
6642 byteData
= new byte[encoding
.GetByteCount(charData
, 0, charData
.Length
)];
6643 encoding
.GetBytes(charData
, 0, charData
.Length
, byteData
, 0);
6648 private Task
WriteEncodingChar(string s
, int numChars
, int offset
, Encoding encoding
, TdsParserStateObject stateObj
, bool canAccumulate
= true) {
6652 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6653 // 7.0 has no support for multiple code pages in data - single code page support only
6654 if (encoding
== null)
6655 encoding
= _defaultEncoding
;
6657 charData
= s
.ToCharArray(offset
, numChars
);
6659 // Optimization: if the entire string fits in the current buffer, then copy it directly
6660 int bytesLeft
= stateObj
._outBuff
.Length
- stateObj
._outBytesUsed
;
6661 if ((numChars
<= bytesLeft
) && (encoding
.GetMaxByteCount(charData
.Length
) <= bytesLeft
)) {
6662 int bytesWritten
= encoding
.GetBytes(charData
, 0, charData
.Length
, stateObj
._outBuff
, stateObj
._outBytesUsed
);
6663 stateObj
._outBytesUsed
+= bytesWritten
;
6667 byteData
= encoding
.GetBytes(charData
, 0, numChars
);
6668 Debug
.Assert(byteData
!= null, "no data from encoding");
6669 return stateObj
.WriteByteArray(byteData
, byteData
.Length
, 0, canAccumulate
);
6673 internal int GetEncodingCharLength(string value, int numChars
, int charOffset
, Encoding encoding
) {
6676 if (value == null || value == ADP
.StrEmpty
) {
6680 // if hitting 7.0 server, encoding will be null in metadata for columns or return values since
6681 // 7.0 has no support for multiple code pages in data - single code page support only
6682 if (encoding
== null) {
6683 if (null == _defaultEncoding
) {
6684 ThrowUnsupportedCollationEncountered(null);
6687 encoding
= _defaultEncoding
;
6690 char[] charData
= value.ToCharArray(charOffset
, numChars
);
6692 return encoding
.GetByteCount(charData
, 0, numChars
);
6696 // Returns the data stream length of the data identified by tds type or SqlMetaData returns
6697 // Returns either the total size or the size of the first chunk for partially length prefixed types.
6699 internal bool TryGetDataLength(SqlMetaDataPriv colmeta
, TdsParserStateObject stateObj
, out ulong length
) {
6700 // Handle Yukon specific tokens
6701 if (_isYukon
&& colmeta
.metaType
.IsPlp
) {
6702 Debug
.Assert(colmeta
.tdsType
== TdsEnums
.SQLXMLTYPE
||
6703 colmeta
.tdsType
== TdsEnums
.SQLBIGVARCHAR
||
6704 colmeta
.tdsType
== TdsEnums
.SQLBIGVARBINARY
||
6705 colmeta
.tdsType
== TdsEnums
.SQLNVARCHAR
||
6706 // Large UDTs is WinFS-only
6707 colmeta
.tdsType
== TdsEnums
.SQLUDT
,
6708 "GetDataLength:Invalid streaming datatype");
6709 return stateObj
.TryReadPlpLength(true, out length
);
6713 if (!TryGetTokenLength(colmeta
.tdsType
, stateObj
, out intLength
)) {
6717 length
= (ulong)intLength
;
6723 // returns the token length of the token or tds type
6724 // Returns -1 for partially length prefixed (plp) types for metadata info.
6725 // DOES NOT handle plp data streams correctly!!!
6726 // Plp data streams length information should be obtained from GetDataLength
6728 internal bool TryGetTokenLength(byte token
, TdsParserStateObject stateObj
, out int tokenLength
) {
6729 Debug
.Assert(token
!= 0, "0 length token!");
6731 switch (token
) { // rules about SQLLenMask no longer apply to new tokens (as of 7.4)
6732 case TdsEnums
.SQLFEATUREEXTACK
:
6735 case TdsEnums
.SQLSESSIONSTATE
:
6736 case TdsEnums
.SQLFEDAUTHINFO
:
6737 return stateObj
.TryReadInt32(out tokenLength
);
6740 if (_isYukon
) { // Handle Yukon specific exceptions
6741 if (token
== TdsEnums
.SQLUDT
) { // special case for UDTs
6742 tokenLength
= -1; // Should we return -1 or not call GetTokenLength for UDTs?
6745 else if (token
== TdsEnums
.SQLRETURNVALUE
) {
6746 tokenLength
= -1; // In Yukon, the RETURNVALUE token stream no longer has length
6749 else if (token
== TdsEnums
.SQLXMLTYPE
) {
6751 if (!stateObj
.TryReadUInt16(out value)) {
6755 tokenLength
= (int)value;
6756 Debug
.Assert(tokenLength
== TdsEnums
.SQL_USHORTVARMAXLEN
, "Invalid token stream for xml datatype");
6761 switch (token
& TdsEnums
.SQLLenMask
) {
6762 case TdsEnums
.SQLFixedLen
:
6763 tokenLength
= ((0x01 << ((token
& 0x0c) >> 2))) & 0xff;
6765 case TdsEnums
.SQLZeroLen
:
6768 case TdsEnums
.SQLVarLen
:
6769 case TdsEnums
.SQLVarCnt
:
6770 if (0 != (token
& 0x80)) {
6772 if (!stateObj
.TryReadUInt16(out value)) {
6776 tokenLength
= value;
6779 else if (0 == (token
& 0x0c)) {
6781 if (!stateObj
.TryReadInt32(out tokenLength
)) {
6788 if (!stateObj
.TryReadByte(out value)) {
6792 tokenLength
= value;
6796 Debug
.Assert(false, "Unknown token length!");
6802 private void ProcessAttention(TdsParserStateObject stateObj
) {
6803 if (_state
== TdsParserState
.Closed
|| _state
== TdsParserState
.Broken
){
6806 Debug
.Assert(stateObj
._attentionSent
, "invalid attempt to ProcessAttention, attentionSent == false!");
6808 // Attention processing scenarios:
6809 // 1) EOM packet with header ST_AACK bit plus DONE with status DONE_ATTN
6810 // 2) Packet without ST_AACK header bit but has DONE with status DONE_ATTN
6811 // 3) Secondary timeout occurs while reading, break connection
6813 // Since errors can occur and we need to cancel prior to throwing those errors, we
6814 // cache away error state and then process TDS for the attention. We restore those
6815 // errors after processing.
6816 stateObj
.StoreErrorAndWarningForAttention();
6819 // Call run loop to process looking for attention ack.
6820 Run(RunBehavior
.Attention
, null, null, null, stateObj
);
6822 catch (Exception e
) {
6824 if (!ADP
.IsCatchableExceptionType(e
)) {
6828 // If an exception occurs - break the connection.
6829 // Attention error will not be thrown in this case by Run(), but other failures may.
6830 ADP
.TraceExceptionWithoutRethrow(e
);
6831 _state
= TdsParserState
.Broken
;
6832 _connHandler
.BreakConnection();
6837 stateObj
.RestoreErrorAndWarningAfterAttention();
6839 Debug
.Assert(!stateObj
._attentionSent
, "Invalid attentionSent state at end of ProcessAttention");
6842 static private int StateValueLength(int dataLen
) {
6843 return dataLen
< 0xFF ? (dataLen
+ 1) : (dataLen
+ 5);
6846 internal int WriteSessionRecoveryFeatureRequest(SessionData reconnectData
, bool write
/* if false just calculates the length */) {
6849 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_SRECOVERY
);
6851 if (reconnectData
== null) {
6853 WriteInt(0, _physicalStateObj
);
6858 Debug
.Assert(reconnectData
._unrecoverableStatesCount
== 0, "Unrecoverable state count should be 0");
6859 int initialLength
= 0; // sizeof(DWORD) - length itself
6860 initialLength
+= 1 + 2 * TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._initialDatabase
);
6861 initialLength
+= 1 + 2 * TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._initialLanguage
);
6862 initialLength
+= (reconnectData
._initialCollation
== null) ? 1 : 6;
6863 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6864 if (reconnectData
._initialState
[i
] != null) {
6865 initialLength
+= 1 /* StateId*/ + StateValueLength(reconnectData
._initialState
[i
].Length
);
6868 int currentLength
= 0; // sizeof(DWORD) - length itself
6869 currentLength
+= 1 + 2 * (reconnectData
._initialDatabase
== reconnectData
._database
? 0 : TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._database
));
6870 currentLength
+= 1 + 2 * (reconnectData
._initialLanguage
== reconnectData
._language
? 0 : TdsParserStaticMethods
.NullAwareStringLength(reconnectData
._language
));
6871 currentLength
+= (reconnectData
._collation
!= null && !SqlCollation
.AreSame(reconnectData
._collation
, reconnectData
._initialCollation
)) ? 6 : 1;
6872 bool[] writeState
= new bool[SessionData
._maxNumberOfSessionStates
];
6873 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6874 if (reconnectData
._delta
[i
] != null) {
6875 Debug
.Assert(reconnectData
._delta
[i
]._recoverable
, "State should be recoverable");
6876 writeState
[i
] = true;
6877 if (reconnectData
._initialState
[i
] != null && reconnectData
._initialState
[i
].Length
== reconnectData
._delta
[i
]._dataLength
) {
6878 writeState
[i
] = false;
6879 for (int j
= 0; j
< reconnectData
._delta
[i
]._dataLength
; j
++) {
6880 if (reconnectData
._initialState
[i
][j
] != reconnectData
._delta
[i
]._data
[j
]) {
6881 writeState
[i
] = true;
6886 if (writeState
[i
]) {
6887 currentLength
+= 1 /* StateId*/ + StateValueLength(reconnectData
._delta
[i
]._dataLength
);
6892 WriteInt(8 + initialLength
+ currentLength
, _physicalStateObj
); // length of data w/o total length (initil+current+2*sizeof(DWORD))
6893 WriteInt(initialLength
, _physicalStateObj
);
6894 WriteIdentifier(reconnectData
._initialDatabase
, _physicalStateObj
);
6895 WriteCollation(reconnectData
._initialCollation
, _physicalStateObj
);
6896 WriteIdentifier(reconnectData
._initialLanguage
, _physicalStateObj
);
6897 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6898 if (reconnectData
._initialState
[i
] != null) {
6899 _physicalStateObj
.WriteByte((byte)i
);
6900 if (reconnectData
._initialState
[i
].Length
< 0xFF) {
6901 _physicalStateObj
.WriteByte((byte)reconnectData
._initialState
[i
].Length
);
6904 _physicalStateObj
.WriteByte(0xFF);
6905 WriteInt(reconnectData
._initialState
[i
].Length
, _physicalStateObj
);
6907 _physicalStateObj
.WriteByteArray(reconnectData
._initialState
[i
], reconnectData
._initialState
[i
].Length
, 0);
6910 WriteInt(currentLength
, _physicalStateObj
);
6911 WriteIdentifier(reconnectData
._database
!= reconnectData
._initialDatabase
? reconnectData
._database
: null, _physicalStateObj
);
6912 WriteCollation(SqlCollation
.AreSame(reconnectData
._initialCollation
, reconnectData
._collation
) ? null : reconnectData
._collation
, _physicalStateObj
);
6913 WriteIdentifier(reconnectData
._language
!= reconnectData
._initialLanguage
? reconnectData
._language
: null, _physicalStateObj
);
6914 for (int i
= 0; i
< SessionData
._maxNumberOfSessionStates
; i
++) {
6915 if (writeState
[i
]) {
6916 _physicalStateObj
.WriteByte((byte)i
);
6917 if (reconnectData
._delta
[i
]._dataLength
< 0xFF) {
6918 _physicalStateObj
.WriteByte((byte)reconnectData
._delta
[i
]._dataLength
);
6921 _physicalStateObj
.WriteByte(0xFF);
6922 WriteInt(reconnectData
._delta
[i
]._dataLength
, _physicalStateObj
);
6924 _physicalStateObj
.WriteByteArray(reconnectData
._delta
[i
]._data
, reconnectData
._delta
[i
]._dataLength
, 0);
6928 len
+= initialLength
+ currentLength
+ 12 /* length fields (initial, current, total) */;
6933 internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionData fedAuthFeatureData
,
6934 bool write
/* if false just calculates the length */) {
6935 Debug
.Assert(fedAuthFeatureData
.libraryType
== TdsEnums
.FedAuthLibrary
.ADAL
|| fedAuthFeatureData
.libraryType
== TdsEnums
.FedAuthLibrary
.SecurityToken
,
6936 "only fed auth library type ADAL and Security Token are supported in writing feature request");
6941 // set dataLen and totalLen
6942 switch (fedAuthFeatureData
.libraryType
) {
6943 case TdsEnums
.FedAuthLibrary
.ADAL
:
6944 dataLen
= 2; // length of feature data = 1 byte for library and echo + 1 byte for workflow
6946 case TdsEnums
.FedAuthLibrary
.SecurityToken
:
6947 Debug
.Assert(fedAuthFeatureData
.accessToken
!= null, "AccessToken should not be null.");
6948 dataLen
= 1 + sizeof(int) + fedAuthFeatureData
.accessToken
.Length
; // length of feature data = 1 byte for library and echo, security token length and sizeof(int) for token lengh itself
6951 Debug
.Assert(false, "Unrecognized library type for fedauth feature extension request");
6955 totalLen
= dataLen
+ 5; // length of feature id (1 byte), data length field (4 bytes), and feature data (dataLen)
6959 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_FEDAUTH
);
6962 byte options
= 0x00;
6964 // set upper 7 bits of options to indicate fed auth library type
6965 switch (fedAuthFeatureData
.libraryType
) {
6966 case TdsEnums
.FedAuthLibrary
.ADAL
:
6967 Debug
.Assert(_connHandler
._federatedAuthenticationInfoRequested
== true, "_federatedAuthenticationInfoRequested field should be true");
6968 options
|= TdsEnums
.FEDAUTHLIB_ADAL
<< 1;
6970 case TdsEnums
.FedAuthLibrary
.SecurityToken
:
6971 Debug
.Assert(_connHandler
._federatedAuthenticationRequested
== true, "_federatedAuthenticationRequested field should be true");
6972 options
|= TdsEnums
.FEDAUTHLIB_SECURITYTOKEN
<< 1;
6975 Debug
.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request");
6979 options
|= (byte)(fedAuthFeatureData
.fedAuthRequiredPreLoginResponse
== true ? 0x01 : 0x00);
6981 // write dataLen and options
6982 WriteInt(dataLen
, _physicalStateObj
);
6983 _physicalStateObj
.WriteByte(options
);
6985 // write workflow for FedAuthLibrary.ADAL
6986 // write accessToken for FedAuthLibrary.SecurityToken
6987 switch (fedAuthFeatureData
.libraryType
) {
6988 case TdsEnums
.FedAuthLibrary
.ADAL
:
6989 byte workflow
= 0x00;
6990 switch (fedAuthFeatureData
.authentication
) {
6991 case SqlAuthenticationMethod
.ActiveDirectoryPassword
:
6992 workflow
= TdsEnums
.ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD
;
6994 case SqlAuthenticationMethod
.ActiveDirectoryIntegrated
:
6995 workflow
= TdsEnums
.ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED
;
6998 Debug
.Assert(false, "Unrecognized Authentication type for fedauth ADAL request");
7002 _physicalStateObj
.WriteByte(workflow
);
7004 case TdsEnums
.FedAuthLibrary
.SecurityToken
:
7005 WriteInt(fedAuthFeatureData
.accessToken
.Length
, _physicalStateObj
);
7006 _physicalStateObj
.WriteByteArray(fedAuthFeatureData
.accessToken
, fedAuthFeatureData
.accessToken
.Length
, 0);
7009 Debug
.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request");
7016 internal int WriteTceFeatureRequest (bool write
/* if false just calculates the length */) {
7017 int len
= 6; // (1byte = featureID, 4bytes = featureData length, 1 bytes = Version
7020 // Write Feature ID, legth of the version# field and TCE Version#
7021 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_TCE
);
7022 WriteInt (1, _physicalStateObj
);
7023 _physicalStateObj
.WriteByte(TdsEnums
.MAX_SUPPORTED_TCE_VERSION
);
7026 return len
; // size of data written
7029 internal int WriteGlobalTransactionsFeatureRequest(bool write
/* if false just calculates the length */) {
7030 int len
= 5; // 1byte = featureID, 4bytes = featureData length
7034 _physicalStateObj
.WriteByte(TdsEnums
.FEATUREEXT_GLOBALTRANSACTIONS
);
7035 WriteInt(0, _physicalStateObj
); // we don't send any data
7041 internal void TdsLogin(SqlLogin rec
,
7042 TdsEnums
.FeatureExtension requestedFeatures
,
7043 SessionData recoverySessionData
,
7044 FederatedAuthenticationFeatureExtensionData
? fedAuthFeatureExtensionData
) {
7045 _physicalStateObj
.SetTimeoutSeconds(rec
.timeout
);
7047 Debug
.Assert(recoverySessionData
== null || (requestedFeatures
& TdsEnums
.FeatureExtension
.SessionRecovery
) != 0, "Recovery session data without session recovery feature request");
7048 Debug
.Assert(TdsEnums
.MAXLEN_HOSTNAME
>=rec
.hostName
.Length
, "_workstationId.Length exceeds the max length for this value");
7050 Debug
.Assert(!(rec
.useSSPI
&& _connHandler
._fedAuthRequired
), "Cannot use SSPI when server has responded 0x01 for FedAuthRequired PreLogin Option.");
7051 Debug
.Assert(!rec
.useSSPI
|| (requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) == 0, "Cannot use both SSPI and FedAuth");
7052 Debug
.Assert(fedAuthFeatureExtensionData
== null || (requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) != 0, "fedAuthFeatureExtensionData provided without fed auth feature request");
7053 Debug
.Assert(fedAuthFeatureExtensionData
!= null || (requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) == 0, "Fed Auth feature requested without specifying fedAuthFeatureExtensionData.");
7055 Debug
.Assert(rec
.userName
== null || (rec
.userName
!= null && TdsEnums
.MAXLEN_USERNAME
>= rec
.userName
.Length
), "_userID.Length exceeds the max length for this value");
7056 Debug
.Assert(rec
.credential
== null || (rec
.credential
!= null && TdsEnums
.MAXLEN_USERNAME
>= rec
.credential
.UserId
.Length
), "_credential.UserId.Length exceeds the max length for this value");
7058 Debug
.Assert(rec
.password
== null || (rec
.password
!= null && TdsEnums
.MAXLEN_PASSWORD
>=rec
.password
.Length
), "_password.Length exceeds the max length for this value");
7059 Debug
.Assert(rec
.credential
== null || (rec
.credential
!= null && TdsEnums
.MAXLEN_PASSWORD
>= rec
.credential
.Password
.Length
), "_credential.Password.Length exceeds the max length for this value");
7061 Debug
.Assert(rec
.credential
!= null || rec
.userName
!= null || rec
.password
!= null, "cannot mix the new secure password system and the connection string based password");
7062 Debug
.Assert(rec
.newSecurePassword
!= null || rec
.newPassword
!= null, "cannot have both new secure change password and string based change password");
7063 Debug
.Assert(TdsEnums
.MAXLEN_APPNAME
>=rec
.applicationName
.Length
, "_applicationName.Length exceeds the max length for this value");
7064 Debug
.Assert(TdsEnums
.MAXLEN_SERVERNAME
>=rec
.serverName
.Length
, "_dataSource.Length exceeds the max length for this value");
7065 Debug
.Assert(TdsEnums
.MAXLEN_LANGUAGE
>=rec
.language
.Length
, "_currentLanguage .Length exceeds the max length for this value");
7066 Debug
.Assert(TdsEnums
.MAXLEN_DATABASE
>=rec
.database
.Length
, "_initialCatalog.Length exceeds the max length for this value");
7067 Debug
.Assert(TdsEnums
.MAXLEN_ATTACHDBFILE
>=rec
.attachDBFilename
.Length
, "_attachDBFileName.Length exceeds the max length for this value");
7069 Debug
.Assert(_connHandler
!= null, "SqlConnectionInternalTds handler can not be null at this point.");
7070 _connHandler
.TimeoutErrorInternal
.EndPhase(SqlConnectionTimeoutErrorPhase
.LoginBegin
);
7071 _connHandler
.TimeoutErrorInternal
.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase
.ProcessConnectionAuth
);
7073 // get the password up front to use in sspi logic below
7074 byte[] encryptedPassword
= null;
7075 byte[] encryptedChangePassword
= null;
7076 int encryptedPasswordLengthInBytes
;
7077 int encryptedChangePasswordLengthInBytes
;
7078 bool useFeatureExt
= (requestedFeatures
!= TdsEnums
.FeatureExtension
.None
);
7082 if (rec
.credential
!= null) {
7083 userName
= rec
.credential
.UserId
;
7084 encryptedPasswordLengthInBytes
= rec
.credential
.Password
.Length
* 2;
7087 userName
= rec
.userName
;
7088 encryptedPassword
= TdsParserStaticMethods
.EncryptPassword(rec
.password
);
7089 encryptedPasswordLengthInBytes
= encryptedPassword
.Length
; // password in clear text is already encrypted and its length is in byte
7092 if (rec
.newSecurePassword
!= null) {
7093 encryptedChangePasswordLengthInBytes
= rec
.newSecurePassword
.Length
* 2;
7096 encryptedChangePassword
= TdsParserStaticMethods
.EncryptPassword(rec
.newPassword
);
7097 encryptedChangePasswordLengthInBytes
= encryptedChangePassword
.Length
;
7101 // set the message type
7102 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_LOGIN7
;
7105 int length
= TdsEnums
.YUKON_LOG_REC_FIXED_LEN
;
7107 string clientInterfaceName
= TdsEnums
.SQL_PROVIDER_NAME
;
7108 Debug
.Assert(TdsEnums
.MAXLEN_CLIENTINTERFACE
>= clientInterfaceName
.Length
, "cchCltIntName can specify at most 128 unicode characters. See Tds spec");
7110 // add up variable-len portions (multiply by 2 for byte len of char strings)
7113 length
+= (rec
.hostName
.Length
+ rec
.applicationName
.Length
+
7114 rec
.serverName
.Length
+ clientInterfaceName
.Length
+
7115 rec
.language
.Length
+ rec
.database
.Length
+
7116 rec
.attachDBFilename
.Length
) * 2;
7117 if (useFeatureExt
) {
7122 // allocate memory for SSPI variables
7123 byte[] outSSPIBuff
= null;
7124 UInt32 outSSPILength
= 0;
7126 // only add lengths of password and username if not using SSPI or requesting federated authentication info
7127 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7129 length
+= (userName
.Length
* 2) + encryptedPasswordLengthInBytes
7130 + encryptedChangePasswordLengthInBytes
;
7135 // now allocate proper length of buffer, and set length
7136 outSSPIBuff
= new byte[s_maxSSPILength
];
7137 outSSPILength
= s_maxSSPILength
;
7139 // Call helper function for SSPI data and actual length.
7140 // Since we don't have SSPI data from the server, send null for the
7141 // byte[] buffer and 0 for the int length.
7142 Debug
.Assert(SniContext
.Snix_Login
==_physicalStateObj
.SniContext
, String
.Format((IFormatProvider
)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj
.SniContext
));
7143 _physicalStateObj
.SniContext
= SniContext
.Snix_LoginSspi
;
7144 SSPIData(null, 0, outSSPIBuff
, ref outSSPILength
);
7145 if (outSSPILength
> Int32
.MaxValue
) {
7146 throw SQL
.InvalidSSPIPacketSize(); // SqlBu 332503
7148 _physicalStateObj
.SniContext
=SniContext
.Snix_Login
;
7151 length
+= (Int32
)outSSPILength
;
7156 int feOffset
= length
;
7158 if (useFeatureExt
) {
7160 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.SessionRecovery
) != 0) {
7161 length
+= WriteSessionRecoveryFeatureRequest(recoverySessionData
, false);
7163 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) != 0) {
7164 Debug
.Assert(fedAuthFeatureExtensionData
!= null, "fedAuthFeatureExtensionData should not null.");
7165 length
+= WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData
.Value
, write
:false);
7167 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.Tce
) != 0) {
7168 length
+= WriteTceFeatureRequest (false);
7170 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.GlobalTransactions
) != 0) {
7171 length
+= WriteGlobalTransactionsFeatureRequest(false);
7173 length
++; // for terminator
7178 WriteInt(length
, _physicalStateObj
);
7179 if (recoverySessionData
== null) {
7180 WriteInt((TdsEnums
.DENALI_MAJOR
<< 24) | (TdsEnums
.DENALI_INCREMENT
<< 16) | TdsEnums
.DENALI_MINOR
, _physicalStateObj
);
7183 WriteUnsignedInt(recoverySessionData
._tdsVersion
, _physicalStateObj
);
7185 WriteInt(rec
.packetSize
, _physicalStateObj
);
7186 WriteInt(TdsEnums
.CLIENT_PROG_VER
, _physicalStateObj
);
7187 WriteInt(TdsParserStaticMethods
.GetCurrentProcessIdForTdsLoginOnly(), _physicalStateObj
); //MDAC 84718
7188 WriteInt(0, _physicalStateObj
); // connectionID is unused
7190 // Log7Flags (DWORD)
7194 Current snapshot from TDS spec with the offsets added:
7195 0) fByteOrder:1, // byte order of numeric data types on client
7196 1) fCharSet:1, // character set on client
7197 2) fFloat:2, // Type of floating point on client
7198 4) fDumpLoad:1, // Dump/Load and BCP enable
7199 5) fUseDb:1, // USE notification
7200 6) fDatabase:1, // Initial database fatal flag
7201 7) fSetLang:1, // SET LANGUAGE notification
7202 8) fLanguage:1, // Initial language fatal flag
7203 9) fODBC:1, // Set if client is ODBC driver
7204 10) fTranBoundary:1, // Transaction boundary notification
7205 11) fDelegatedSec:1, // Security with delegation is available
7206 12) fUserType:3, // Type of user
7207 15) fIntegratedSecurity:1, // Set if client is using integrated security
7208 16) fSQLType:4, // Type of SQL sent from client
7209 20) fOLEDB:1, // Set if client is OLEDB driver
7210 21) fSpare1:3, // first bit used for read-only intent, rest unused
7211 24) fResetPassword:1, // set if client wants to reset password
7212 25) fNoNBCAndSparse:1, // set if client does not support NBC and Sparse column
7213 26) fUserInstance:1, // This connection wants to connect to a SQL "user instance"
7214 27) fUnknownCollationHandling:1, // This connection can handle unknown collation correctly.
7215 28) fExtension:1 // Extensions are used
7220 log7Flags
|= TdsEnums
.USE_DB_ON
<< 5;
7221 log7Flags
|= TdsEnums
.INIT_DB_FATAL
<< 6;
7222 log7Flags
|= TdsEnums
.SET_LANG_ON
<< 7;
7225 log7Flags
|= TdsEnums
.INIT_LANG_FATAL
<< 8;
7226 log7Flags
|= TdsEnums
.ODBC_ON
<< 9;
7227 if (rec
.useReplication
) {
7228 log7Flags
|= TdsEnums
.REPL_ON
<< 12;
7231 log7Flags
|= TdsEnums
.SSPI_ON
<< 15;
7235 if (rec
.readOnlyIntent
) {
7236 log7Flags
|= TdsEnums
.READONLY_INTENT_ON
<< 21; // read-only intent flag is a first bit of fSpare1
7240 if (!ADP
.IsEmpty(rec
.newPassword
) || (rec
.newSecurePassword
!= null && rec
.newSecurePassword
.Length
!= 0)) {
7241 log7Flags
|= 1 << 24;
7243 if (rec
.userInstance
) {
7244 log7Flags
|= 1 << 26;
7247 if (useFeatureExt
) {
7248 log7Flags
|= 1 << 28;
7251 WriteInt(log7Flags
, _physicalStateObj
);
7252 if (Bid
.AdvancedOn
) {
7253 Bid
.Trace("<sc.TdsParser.TdsLogin|ADV> %d#, TDS Login7 flags = %d:\n", ObjectID
, log7Flags
);
7256 WriteInt(0, _physicalStateObj
); // ClientTimeZone is not used
7257 WriteInt(0, _physicalStateObj
); // LCID is unused by server
7259 // Start writing offset and length of variable length portions
7260 int offset
= TdsEnums
.YUKON_LOG_REC_FIXED_LEN
;
7262 // write offset/length pairs
7264 // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record
7265 WriteShort(offset
, _physicalStateObj
); // host name offset
7266 WriteShort(rec
.hostName
.Length
, _physicalStateObj
);
7267 offset
+= rec
.hostName
.Length
* 2;
7269 // Only send user/password over if not fSSPI or fed auth ADAL... If both user/password and SSPI are in login
7270 // rec, only SSPI is used. Confirmed same bahavior as in luxor.
7271 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7272 WriteShort(offset
, _physicalStateObj
); // userName offset
7273 WriteShort(userName
.Length
, _physicalStateObj
);
7274 offset
+= userName
.Length
* 2;
7276 // the encrypted password is a byte array - so length computations different than strings
7277 WriteShort(offset
, _physicalStateObj
); // password offset
7278 WriteShort(encryptedPasswordLengthInBytes
/ 2, _physicalStateObj
);
7279 offset
+= encryptedPasswordLengthInBytes
;
7282 // case where user/password data is not used, send over zeros
7283 WriteShort(0, _physicalStateObj
); // userName offset
7284 WriteShort(0, _physicalStateObj
);
7285 WriteShort(0, _physicalStateObj
); // password offset
7286 WriteShort(0, _physicalStateObj
);
7289 WriteShort(offset
, _physicalStateObj
); // app name offset
7290 WriteShort(rec
.applicationName
.Length
, _physicalStateObj
);
7291 offset
+= rec
.applicationName
.Length
* 2;
7293 WriteShort(offset
, _physicalStateObj
); // server name offset
7294 WriteShort(rec
.serverName
.Length
, _physicalStateObj
);
7295 offset
+= rec
.serverName
.Length
* 2;
7297 WriteShort(offset
, _physicalStateObj
);
7298 if (useFeatureExt
) {
7299 WriteShort(4, _physicalStateObj
); // length of ibFeatgureExtLong (which is a DWORD)
7303 WriteShort(0, _physicalStateObj
); // unused (was remote password ?)
7306 WriteShort(offset
, _physicalStateObj
); // client interface name offset
7307 WriteShort(clientInterfaceName
.Length
, _physicalStateObj
);
7308 offset
+= clientInterfaceName
.Length
* 2;
7310 WriteShort(offset
, _physicalStateObj
); // language name offset
7311 WriteShort(rec
.language
.Length
, _physicalStateObj
);
7312 offset
+= rec
.language
.Length
* 2;
7314 WriteShort(offset
, _physicalStateObj
); // database name offset
7315 WriteShort(rec
.database
.Length
, _physicalStateObj
);
7316 offset
+= rec
.database
.Length
* 2;
7320 if (null == s_nicAddress
)
7321 s_nicAddress
= TdsParserStaticMethods
.GetNetworkPhysicalAddressForTdsLoginOnly();
7323 _physicalStateObj
.WriteByteArray(s_nicAddress
, s_nicAddress
.Length
, 0);
7325 WriteShort(offset
, _physicalStateObj
); // ibSSPI offset
7327 WriteShort((int)outSSPILength
, _physicalStateObj
);
7328 offset
+= (int)outSSPILength
;
7331 WriteShort(0, _physicalStateObj
);
7334 WriteShort(offset
, _physicalStateObj
); // DB filename offset
7335 WriteShort(rec
.attachDBFilename
.Length
, _physicalStateObj
);
7336 offset
+= rec
.attachDBFilename
.Length
* 2;
7338 WriteShort(offset
, _physicalStateObj
); // reset password offset
7339 WriteShort(encryptedChangePasswordLengthInBytes
/ 2, _physicalStateObj
);
7341 WriteInt(0, _physicalStateObj
); // reserved for chSSPI
7343 // write variable length portion
7344 WriteString(rec
.hostName
, _physicalStateObj
);
7346 // if we are using SSPI or fed auth ADAL, do not send over username/password, since we will use SSPI instead
7347 // same behavior as Luxor
7348 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7349 WriteString(userName
, _physicalStateObj
);
7351 // Cache offset in packet for tracing.
7352 _physicalStateObj
._tracePasswordOffset
= _physicalStateObj
._outBytesUsed
;
7353 _physicalStateObj
._tracePasswordLength
= encryptedPasswordLengthInBytes
;
7355 if (rec
.credential
!= null) {
7356 _physicalStateObj
.WriteSecureString(rec
.credential
.Password
);
7359 _physicalStateObj
.WriteByteArray(encryptedPassword
, encryptedPasswordLengthInBytes
, 0);
7363 WriteString(rec
.applicationName
, _physicalStateObj
);
7364 WriteString(rec
.serverName
, _physicalStateObj
);
7366 // write ibFeatureExtLong
7367 if (useFeatureExt
) {
7368 WriteInt(feOffset
, _physicalStateObj
);
7371 WriteString(clientInterfaceName
, _physicalStateObj
);
7372 WriteString(rec
.language
, _physicalStateObj
);
7373 WriteString(rec
.database
, _physicalStateObj
);
7375 // send over SSPI data if we are using SSPI
7377 _physicalStateObj
.WriteByteArray(outSSPIBuff
, (int)outSSPILength
, 0);
7379 WriteString(rec
.attachDBFilename
, _physicalStateObj
);
7380 if (!rec
.useSSPI
&& !(_connHandler
._federatedAuthenticationInfoRequested
|| _connHandler
._federatedAuthenticationRequested
)) {
7381 // Cache offset in packet for tracing.
7382 _physicalStateObj
._traceChangePasswordOffset
= _physicalStateObj
._outBytesUsed
;
7383 _physicalStateObj
._traceChangePasswordLength
= encryptedChangePasswordLengthInBytes
;
7384 if (rec
.newSecurePassword
!= null) {
7385 _physicalStateObj
.WriteSecureString(rec
.newSecurePassword
);
7388 _physicalStateObj
.WriteByteArray(encryptedChangePassword
, encryptedChangePasswordLengthInBytes
, 0);
7392 if (useFeatureExt
) {
7393 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.SessionRecovery
) != 0) {
7394 WriteSessionRecoveryFeatureRequest(recoverySessionData
, true);
7396 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.FedAuth
) != 0) {
7397 Bid
.Trace("<sc.TdsParser.TdsLogin|SEC> Sending federated authentication feature request\n");
7398 Debug
.Assert(fedAuthFeatureExtensionData
!= null, "fedAuthFeatureExtensionData should not null.");
7399 WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData
.Value
, write
: true);
7401 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.Tce
) != 0) {
7402 WriteTceFeatureRequest (true);
7404 if ((requestedFeatures
& TdsEnums
.FeatureExtension
.GlobalTransactions
) != 0) {
7405 WriteGlobalTransactionsFeatureRequest(true);
7407 _physicalStateObj
.WriteByte(0xFF); // terminator
7410 catch (Exception e
) {
7412 if (ADP
.IsCatchableExceptionType(e
)) {
7413 // be sure to wipe out our buffer if we started sending stuff
7414 _physicalStateObj
._outputPacketNumber
= 1; // end of message - reset to 1 - per ramas
7415 _physicalStateObj
.ResetBuffer();
7421 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7422 _physicalStateObj
.ResetSecurePasswordsInfomation(); // Password information is needed only from Login process; done with writing login packet and should clear information
7423 _physicalStateObj
._pendingData
= true;
7424 _physicalStateObj
._messageStatus
= 0;
7428 /// Send the access token to the server.
7430 /// <param name="fedAuthToken">Type encapuslating a Federated Authentication access token.</param>
7431 internal void SendFedAuthToken(SqlFedAuthToken fedAuthToken
) {
7432 Debug
.Assert(fedAuthToken
!= null, "fedAuthToken cannot be null");
7433 Debug
.Assert(fedAuthToken
.accessToken
!= null, "fedAuthToken.accessToken cannot be null");
7436 Bid
.Trace("<sc.TdsParser.SendFedAuthToken|SEC> Sending federated authentication token\n");
7438 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_FEDAUTH
;
7440 byte[] accessToken
= fedAuthToken
.accessToken
;
7442 // Send total length (length of token plus 4 bytes for the token length field)
7443 // If we were sending a nonce, this would include that length as well
7444 WriteUnsignedInt((uint)accessToken
.Length
+ sizeof(uint), _physicalStateObj
);
7446 // Send length of token
7447 WriteUnsignedInt((uint)accessToken
.Length
, _physicalStateObj
);
7449 // Send federated authentication access token
7450 _physicalStateObj
.WriteByteArray(accessToken
, accessToken
.Length
, 0);
7452 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7453 _physicalStateObj
._pendingData
= true;
7454 _physicalStateObj
._messageStatus
= 0;
7456 _connHandler
._federatedAuthenticationRequested
= true;
7459 private void SSPIData(byte[] receivedBuff
, UInt32 receivedLength
, byte[] sendBuff
, ref UInt32 sendLength
) {
7460 SNISSPIData(receivedBuff
, receivedLength
, sendBuff
, ref sendLength
);
7463 private void SNISSPIData(byte[] receivedBuff
, UInt32 receivedLength
, byte[] sendBuff
, ref UInt32 sendLength
)
7465 if (receivedBuff
== null)
7467 // we do not have SSPI data coming from server, so send over 0's for pointer and length
7470 // we need to respond to the server's message with SSPI data
7471 if(0 != SNINativeMethodWrapper
.SNISecGenClientContext(_physicalStateObj
.Handle
, receivedBuff
, receivedLength
, sendBuff
, ref sendLength
, _sniSpnBuffer
))
7473 SSPIError(SQLMessage
.SSPIGenerateError(), TdsEnums
.GEN_CLIENT_CONTEXT
);
7478 private void ProcessSSPI(int receivedLength
) {
7479 SniContext outerContext
=_physicalStateObj
.SniContext
;
7480 _physicalStateObj
.SniContext
= SniContext
.Snix_ProcessSspi
;
7481 // allocate received buffer based on length from SSPI message
7482 byte[] receivedBuff
= new byte[receivedLength
];
7484 // read SSPI data received from server
7485 Debug
.Assert(_physicalStateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
7486 bool result
= _physicalStateObj
.TryReadByteArray(receivedBuff
, 0, receivedLength
);
7487 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
7489 // allocate send buffer and initialize length
7490 byte[] sendBuff
= new byte[s_maxSSPILength
];
7491 UInt32 sendLength
= s_maxSSPILength
;
7493 // make call for SSPI data
7494 SSPIData(receivedBuff
, (UInt32
)receivedLength
, sendBuff
, ref sendLength
);
7496 // DO NOT SEND LENGTH - TDS DOC INCORRECT! JUST SEND SSPI DATA!
7497 _physicalStateObj
.WriteByteArray(sendBuff
, (int)sendLength
, 0);
7499 // set message type so server knows its a SSPI response
7500 _physicalStateObj
._outputMessageType
= TdsEnums
.MT_SSPI
;
7503 _physicalStateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7504 _physicalStateObj
.SniContext
=outerContext
;
7507 private void SSPIError(string error
, string procedure
) {
7508 Debug
.Assert(!ADP
.IsEmpty(procedure
), "TdsParser.SSPIError called with an empty or null procedure string");
7509 Debug
.Assert(!ADP
.IsEmpty(error
), "TdsParser.SSPIError called with an empty or null error string");
7511 _physicalStateObj
.AddError(new SqlError(0, (byte)0x00, (byte)TdsEnums
.MIN_ERROR_CLASS
, _server
, error
, procedure
, 0));
7512 ThrowExceptionAndWarning(_physicalStateObj
);
7515 private void LoadSSPILibrary() {
7516 // Outer check so we don't acquire lock once once it's loaded.
7517 if (!s_fSSPILoaded
) {
7518 lock (s_tdsParserLock
) {
7519 // re-check inside lock
7520 if (!s_fSSPILoaded
) {
7521 // use local for ref param to defer setting s_maxSSPILength until we know the call succeeded.
7522 UInt32 maxLength
= 0;
7523 if (0 != SNINativeMethodWrapper
.SNISecInitPackage(ref maxLength
))
7524 SSPIError(SQLMessage
.SSPIInitializeError(), TdsEnums
.INIT_SSPI_PACKAGE
);
7526 s_maxSSPILength
= maxLength
;
7527 s_fSSPILoaded
= true;
7533 if (s_maxSSPILength
> Int32
.MaxValue
) {
7534 throw SQL
.InvalidSSPIPacketSize(); // SqlBu 332503
7538 private void LoadADALLibrary() {
7539 // Outer check so we don't acquire lock once once it's loaded.
7540 if (!s_fADALLoaded
) {
7541 lock (s_tdsParserLock
) {
7542 // re-check inside lock
7543 if (!s_fADALLoaded
) {
7544 int result
= ADALNativeWrapper
.ADALInitialize();
7547 s_fADALLoaded
= true;
7550 s_fADALLoaded
= false;
7552 SqlAuthenticationMethod authentication
= SqlAuthenticationMethod
.NotSpecified
;
7554 if (_connHandler
.ConnectionOptions
!= null)
7556 authentication
= _connHandler
.ConnectionOptions
.Authentication
;
7558 // Only the below connection string options should have ended up calling this function.
7559 Debug
.Assert(authentication
== SqlAuthenticationMethod
.ActiveDirectoryIntegrated
|| authentication
== SqlAuthenticationMethod
.ActiveDirectoryPassword
);
7562 _physicalStateObj
.AddError(new SqlError(0, (byte)0x00, (byte)TdsEnums
.MIN_ERROR_CLASS
, _server
, Res
.GetString(Res
.SQL_ADALInitializeError
, authentication
.ToString("G"), result
.ToString("X")), TdsEnums
.INIT_ADAL_PACKAGE
, 0));
7564 ThrowExceptionAndWarning(_physicalStateObj
);
7571 internal byte[] GetDTCAddress(int timeout
, TdsParserStateObject stateObj
) {
7572 // If this fails, the server will return a server error - Sameet Agarwal confirmed.
7573 // Success: DTCAddress returned. Failure: SqlError returned.
7575 byte[] dtcAddr
= null;
7577 using (SqlDataReader dtcReader
= TdsExecuteTransactionManagerRequest(
7579 TdsEnums
.TransactionManagerRequestType
.GetDTCAddress
,
7581 TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
,
7582 timeout
, null, stateObj
, true)) {
7584 Debug
.Assert(SniContext
.Snix_Read
==stateObj
.SniContext
, String
.Format((IFormatProvider
)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj
.SniContext
));
7585 if (null != dtcReader
&& dtcReader
.Read()) {
7586 Debug
.Assert(dtcReader
.GetName(0) == "TM Address", "TdsParser: GetDTCAddress did not return 'TM Address'");
7588 // DTCAddress is of variable size, and does not have a maximum. So we call GetBytes
7589 // to get the length of the dtcAddress, then allocate a byte array of that length,
7590 // then call GetBytes again on that byte[] with the length
7591 long dtcLength
= dtcReader
.GetBytes(0, 0, null, 0, 0);
7594 if (dtcLength
<= Int32
.MaxValue
) {
7595 int cb
= (int)dtcLength
;
7597 dtcAddr
= new byte[cb
];
7598 dtcReader
.GetBytes(0, 0, dtcAddr
, 0, cb
);
7602 Debug
.Assert(false, "unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes");
7603 // if we hit this case we'll just return a null address so that the user
7604 // will get a transcaction enlistment error in the upper layers
7612 // Propagate the dtc cookie to the server, enlisting the connection.
7613 internal void PropagateDistributedTransaction(byte[] buffer
, int timeout
, TdsParserStateObject stateObj
) {
7614 // if this fails, the server will return a server error - Sameet Agarwal confirmed
7615 // Success: server will return done token. Failure: SqlError returned.
7617 TdsExecuteTransactionManagerRequest(buffer
,
7618 TdsEnums
.TransactionManagerRequestType
.Propagate
, null,
7619 TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
, timeout
, null, stateObj
, true);
7622 internal SqlDataReader
TdsExecuteTransactionManagerRequest(
7624 TdsEnums
.TransactionManagerRequestType request
,
7625 string transactionName
,
7626 TdsEnums
.TransactionManagerIsolationLevel isoLevel
,
7628 SqlInternalTransaction transaction
,
7629 TdsParserStateObject stateObj
,
7630 bool isDelegateControlRequest
) {
7632 Debug
.Assert(this == stateObj
.Parser
, "different parsers");
7634 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
) {
7638 // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7639 // delegated transactions often happen while there is an open result
7640 // set, so we need to handle them by using a different MARS session,
7641 // otherwise we'll write on the physical state objects while someone
7642 // else is using it. When we don't have MARS enabled, we need to
7643 // lock the physical state object to syncronize it's use at least
7644 // until we increment the open results count. Once it's been
7645 // incremented the delegated transaction requests will fail, so they
7646 // won't stomp on anything.
7649 Debug
.Assert(!_connHandler
.ThreadHasParserLockForClose
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken");
7650 bool callerHasConnectionLock
= _connHandler
.ThreadHasParserLockForClose
; // If the thread already claims to have the parser lock, then we will let the caller handle releasing it
7651 if (!callerHasConnectionLock
) {
7652 _connHandler
._parserLock
.Wait(canReleaseFromAnyThread
:false);
7653 _connHandler
.ThreadHasParserLockForClose
= true;
7655 // Capture _asyncWrite (after taking lock) to restore it afterwards
7656 bool hadAsyncWrites
= _asyncWrite
;
7658 // Temprarily disable async writes
7659 _asyncWrite
= false;
7661 // This validation step MUST be done after locking the connection to guarantee we don't
7662 // accidentally execute after the transaction has completed on a different thread.
7663 if (!isDelegateControlRequest
) {
7664 _connHandler
.CheckEnlistedTransactionBinding();
7667 stateObj
._outputMessageType
= TdsEnums
.MT_TRANS
; // set message type
7668 stateObj
.SetTimeoutSeconds(timeout
);
7670 stateObj
.SniContext
= SniContext
.Snix_Execute
;
7673 const int marsHeaderSize
= 18; // 4 + 2 + 8 + 4
7674 const int totalHeaderLength
= 22; // 4 + 4 + 2 + 8 + 4
7675 Debug
.Assert(stateObj
._outBytesUsed
== stateObj
._outputHeaderLen
, "Output bytes written before total header length");
7676 // Write total header length
7677 WriteInt(totalHeaderLength
, stateObj
);
7678 // Write mars header length
7679 WriteInt(marsHeaderSize
, stateObj
);
7680 WriteMarsHeaderData(stateObj
, _currentTransaction
);
7683 WriteShort((short)request
, stateObj
); // write TransactionManager Request type
7685 bool returnReader
= false;
7688 case TdsEnums
.TransactionManagerRequestType
.GetDTCAddress
:
7689 WriteShort(0, stateObj
);
7691 returnReader
= true;
7693 case TdsEnums
.TransactionManagerRequestType
.Propagate
:
7694 if (null != buffer
) {
7695 WriteShort(buffer
.Length
, stateObj
);
7696 stateObj
.WriteByteArray(buffer
, buffer
.Length
, 0);
7699 WriteShort(0, stateObj
);
7702 case TdsEnums
.TransactionManagerRequestType
.Begin
:
7703 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for BeginTransaction!");
7704 Debug
.Assert(null != transaction
, "Should have specified an internalTransaction when doing a BeginTransaction request!");
7706 // Only assign the passed in transaction if it is not equal to the current transaction.
7707 // And, if it is not equal, the current actually should be null. Anything else
7708 // is a unexpected state. The concern here is mainly for the mixed use of
7709 // T-SQL and API transactions. See SQL BU DT 345300 for full details and repro.
7712 // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
7713 // passed in on BeginTransaction API call.
7714 // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
7715 // passed in but equivalent to _currentTransaction.
7717 // #1 will occur on standard BeginTransactionAPI call. #2 should only occur if
7718 // t-sql transaction started followed by a call to SqlConnection.BeginTransaction.
7719 // Any other state is unknown.
7720 if (_currentTransaction
!= transaction
) {
7721 Debug
.Assert(_currentTransaction
== null || true == _fResetConnection
, "We should not have a current Tx at this point");
7722 PendingTransaction
= transaction
;
7725 stateObj
.WriteByte((byte)isoLevel
);
7727 stateObj
.WriteByte((byte)(transactionName
.Length
* 2)); // Write number of bytes (unicode string).
7728 WriteString(transactionName
, stateObj
);
7730 case TdsEnums
.TransactionManagerRequestType
.Promote
:
7731 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for PromoteTransaction!");
7732 // No payload - except current transaction in header
7733 // Promote returns a DTC cookie. However, the transaction cookie we use for the
7734 // connection does not change after a promote.
7736 case TdsEnums
.TransactionManagerRequestType
.Commit
:
7737 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for CommitTransaction!");
7739 Debug
.Assert(transactionName
.Length
== 0, "Should not have a transaction name on Commit");
7740 stateObj
.WriteByte((byte)0); // No xact name
7742 stateObj
.WriteByte(0); // No flags
7744 Debug
.Assert(isoLevel
== TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
, "Should not have isolation level other than unspecified on Commit!");
7745 // WriteByte((byte) 0, stateObj); // IsolationLevel
7746 // WriteByte((byte) 0, stateObj); // No begin xact name
7748 case TdsEnums
.TransactionManagerRequestType
.Rollback
:
7749 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for RollbackTransaction!");
7751 stateObj
.WriteByte((byte)(transactionName
.Length
* 2)); // Write number of bytes (unicode string).
7752 WriteString(transactionName
, stateObj
);
7754 stateObj
.WriteByte(0); // No flags
7756 Debug
.Assert(isoLevel
== TdsEnums
.TransactionManagerIsolationLevel
.Unspecified
, "Should not have isolation level other than unspecified on Commit!");
7757 // WriteByte((byte) 0, stateObj); // IsolationLevel
7758 // WriteByte((byte) 0, stateObj); // No begin xact name
7760 case TdsEnums
.TransactionManagerRequestType
.Save
:
7761 Debug
.Assert(IsYukonOrNewer
, "Should not be calling TdsExecuteTransactionManagerRequest on pre-Yukon clients for SaveTransaction!");
7763 stateObj
.WriteByte((byte)(transactionName
.Length
* 2)); // Write number of bytes (unicode string).
7764 WriteString(transactionName
, stateObj
);
7767 Debug
.Assert(false, "Unexpected TransactionManagerRequest");
7771 Task writeTask
= stateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
7772 Debug
.Assert(writeTask
== null, "Writes should not pend when writing sync");
7773 stateObj
._pendingData
= true;
7774 stateObj
._messageStatus
= 0;
7776 SqlDataReader dtcReader
= null;
7777 stateObj
.SniContext
= SniContext
.Snix_Read
;
7779 dtcReader
= new SqlDataReader(null, CommandBehavior
.Default
);
7780 Debug
.Assert(this == stateObj
.Parser
, "different parser");
7782 // Remove the current owner of stateObj - otherwise we will hit asserts
7783 stateObj
.Owner
= null;
7785 dtcReader
.Bind(stateObj
);
7787 // force consumption of metadata
7788 _SqlMetaDataSet metaData
= dtcReader
.MetaData
;
7791 Run(RunBehavior
.UntilDone
, null, null, null, stateObj
);
7794 // If the retained ID is no longer valid (because we are enlisting in null or a new transaction) then it should be cleared
7795 if (((request
== TdsEnums
.TransactionManagerRequestType
.Begin
) || (request
== TdsEnums
.TransactionManagerRequestType
.Propagate
)) && ((transaction
== null) || (transaction
.TransactionId
!= _retainedTransactionId
))) {
7796 _retainedTransactionId
= SqlInternalTransaction
.NullTransactionId
;
7801 catch (Exception e
) {
7803 if (!ADP
.IsCatchableExceptionType(e
)) {
7807 FailureCleanup(stateObj
, e
);
7812 // SQLHotfix 50000518
7813 // make sure we don't leave temporary fields set when leaving this function
7814 _pendingTransaction
= null;
7816 _asyncWrite
= hadAsyncWrites
;
7818 if (!callerHasConnectionLock
) {
7819 _connHandler
.ThreadHasParserLockForClose
= false;
7820 _connHandler
._parserLock
.Release();
7825 internal void FailureCleanup(TdsParserStateObject stateObj
, Exception e
) {
7826 int old_outputPacketNumber
= stateObj
._outputPacketNumber
;
7829 Bid
.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception caught on ExecuteXXX: '%ls' \n", e
.ToString());
7832 if (stateObj
.HasOpenResult
) { // SQL BU DT 383773 - need to decrement openResultCount if operation failed.
7833 stateObj
.DecrementOpenResultCount();
7836 // be sure to wipe out our buffer if we started sending stuff
7837 stateObj
.ResetBuffer();
7838 stateObj
._outputPacketNumber
= 1; // end of message - reset to 1 - per ramas
7840 if (old_outputPacketNumber
!= 1 && _state
== TdsParserState
.OpenLoggedIn
) {
7841 Debug
.Assert(_connHandler
._parserLock
.ThreadMayHaveLock(), "Should not be calling into FailureCleanup without first taking the parser lock");
7843 bool originalThreadHasParserLock
= _connHandler
.ThreadHasParserLockForClose
;
7845 // Dev11 Bug 385286 : ExecuteNonQueryAsync hangs when trying to write a parameter which generates ArgumentException and while handling that exception the server disconnects the connection
7846 // Need to set this to true such that if we have an error sending\processing the attention, we won't deadlock ourselves
7847 _connHandler
.ThreadHasParserLockForClose
= true;
7849 // If _outputPacketNumber prior to ResetBuffer was not equal to 1, a packet was already
7850 // sent to the server and so we need to send an attention and process the attention ack.
7851 stateObj
.SendAttention();
7852 ProcessAttention(stateObj
);
7855 // Reset the ThreadHasParserLock value incase our caller expects it to be set\not set
7856 _connHandler
.ThreadHasParserLockForClose
= originalThreadHasParserLock
;
7860 Bid
.Trace("<sc.TdsParser.FailureCleanup|ERR> Exception rethrown. \n");
7863 internal Task
TdsExecuteSQLBatch(string text
, int timeout
, SqlNotificationRequest notificationRequest
, TdsParserStateObject stateObj
, bool sync
, bool callerHasConnectionLock
= false) {
7864 if (TdsParserState
.Broken
== State
|| TdsParserState
.Closed
== State
) {
7868 if (stateObj
.BcpLock
) {
7869 throw SQL
.ConnectionLockedForBcpEvent();
7872 // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7873 // delegated transactions often happen while there is an open result
7874 // set, so we need to handle them by using a different MARS session,
7875 // otherwise we'll write on the physical state objects while someone
7876 // else is using it. When we don't have MARS enabled, we need to
7877 // lock the physical state object to syncronize it's use at least
7878 // until we increment the open results count. Once it's been
7879 // incremented the delegated transaction requests will fail, so they
7880 // won't stomp on anything.
7882 // Only need to take the lock if neither the thread nor the caller claims to already have it
7883 bool needToTakeParserLock
= (!callerHasConnectionLock
) && (!_connHandler
.ThreadHasParserLockForClose
);
7884 Debug
.Assert(!_connHandler
.ThreadHasParserLockForClose
|| sync
, "Thread shouldn't claim to have the parser lock if we are doing async writes"); // Since we have the possibility of pending with async writes, make sure the thread doesn't claim to already have the lock
7885 Debug
.Assert(needToTakeParserLock
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Thread or caller claims to have connection lock, but lock is not taken");
7887 bool releaseConnectionLock
= false;
7888 if (needToTakeParserLock
) {
7889 _connHandler
._parserLock
.Wait(canReleaseFromAnyThread
: !sync
);
7890 releaseConnectionLock
= true;
7893 // Switch the writing mode
7894 // NOTE: We are not turning off async writes when we complete since SqlBulkCopy uses this method and expects _asyncWrite to not change
7895 _asyncWrite
= !sync
;
7898 // Check that the connection is still alive
7899 if ((_state
== TdsParserState
.Closed
) || (_state
== TdsParserState
.Broken
)) {
7900 throw ADP
.ClosedConnectionError();
7903 // This validation step MUST be done after locking the connection to guarantee we don't
7904 // accidentally execute after the transaction has completed on a different thread.
7905 _connHandler
.CheckEnlistedTransactionBinding();
7907 stateObj
.SetTimeoutSeconds(timeout
);
7908 if ((!_fMARS
) && (_physicalStateObj
.HasOpenResult
))
7910 Bid
.Trace("<sc.TdsParser.TdsExecuteSQLBatch|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID
);
7912 stateObj
.SniContext
= SniContext
.Snix_Execute
;
7916 WriteRPCBatchHeaders(stateObj
, notificationRequest
);
7919 stateObj
._outputMessageType
= TdsEnums
.MT_SQL
;
7921 WriteString(text
, text
.Length
, 0, stateObj
);
7923 Task executeTask
= stateObj
.ExecuteFlush();
7924 if (executeTask
== null) {
7925 stateObj
.SniContext
= SniContext
.Snix_Read
;
7928 Debug
.Assert(!sync
, "Should not have gotten a Task when writing in sync mode");
7930 // Need to wait for flush - continuation will unlock the connection
7931 bool taskReleaseConnectionLock
= releaseConnectionLock
;
7932 releaseConnectionLock
= false;
7933 return executeTask
.ContinueWith(t
=> {
7934 Debug
.Assert(!t
.IsCanceled
, "Task should not be canceled");
7937 FailureCleanup(stateObj
, t
.Exception
.InnerException
);
7938 throw t
.Exception
.InnerException
;
7941 stateObj
.SniContext
= SniContext
.Snix_Read
;
7945 if (taskReleaseConnectionLock
) {
7946 _connHandler
._parserLock
.Release();
7949 }, TaskScheduler
.Default
);
7955 catch (Exception e
) {
7956 //Debug.Assert(_state == TdsParserState.Broken, "Caught exception in TdsExecuteSQLBatch but connection was not broken!");
7958 if (!ADP
.IsCatchableExceptionType(e
)) {
7962 FailureCleanup(stateObj
, e
);
7967 if (releaseConnectionLock
) {
7968 _connHandler
._parserLock
.Release();
7973 internal Task
TdsExecuteRPC(SqlCommand cmd
, _SqlRPC
[] rpcArray
, int timeout
, bool inSchema
, SqlNotificationRequest notificationRequest
, TdsParserStateObject stateObj
, bool isCommandProc
, bool sync
= true,
7974 TaskCompletionSource
<object> completion
= null, int startRpc
= 0, int startParam
= 0) {
7975 bool firstCall
= (completion
== null);
7976 bool releaseConnectionLock
= false;
7978 Debug
.Assert(cmd
!= null, @"cmd cannot be null inside TdsExecuteRPC");
7979 Debug
.Assert(!firstCall
|| startRpc
== 0, "startRpc is not 0 on first call");
7980 Debug
.Assert(!firstCall
|| startParam
== 0, "startParam is not 0 on first call");
7981 Debug
.Assert(!firstCall
|| !_connHandler
.ThreadHasParserLockForClose
, "Thread should not already have connection lock");
7982 Debug
.Assert(firstCall
|| _connHandler
._parserLock
.ThreadMayHaveLock(), "Connection lock not taken after the first call");
7984 _SqlRPC rpcext
= null;
7987 // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
7988 // delegated transactions often happen while there is an open result
7989 // set, so we need to handle them by using a different MARS session,
7990 // otherwise we'll write on the physical state objects while someone
7991 // else is using it. When we don't have MARS enabled, we need to
7992 // lock the physical state object to syncronize it's use at least
7993 // until we increment the open results count. Once it's been
7994 // incremented the delegated transaction requests will fail, so they
7995 // won't stomp on anything.
7999 _connHandler
._parserLock
.Wait(canReleaseFromAnyThread
:!sync
);
8000 releaseConnectionLock
= true;
8003 // Ensure that connection is alive
8004 if ((TdsParserState
.Broken
== State
) || (TdsParserState
.Closed
== State
)) {
8005 throw ADP
.ClosedConnectionError();
8008 // This validation step MUST be done after locking the connection to guarantee we don't
8009 // accidentally execute after the transaction has completed on a different thread.
8011 _asyncWrite
= !sync
;
8013 _connHandler
.CheckEnlistedTransactionBinding();
8015 stateObj
.SetTimeoutSeconds(timeout
);
8016 if ((!_fMARS
) && (_physicalStateObj
.HasOpenResult
))
8018 Bid
.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID
);
8020 stateObj
.SniContext
= SniContext
.Snix_Execute
;
8024 WriteRPCBatchHeaders(stateObj
, notificationRequest
);
8027 stateObj
._outputMessageType
= TdsEnums
.MT_RPC
;
8030 for (int ii
= startRpc
; ii
< rpcArray
.Length
; ii
++) {
8031 rpcext
= rpcArray
[ii
];
8033 if (startParam
== 0 || ii
> startRpc
) {
8034 if (rpcext
.ProcID
!= 0 && _isShiloh
) {
8035 // Perf optimization for Shiloh and later,
8036 Debug
.Assert(rpcext
.ProcID
< 255, "rpcExec:ProcID can't be larger than 255");
8037 WriteShort(0xffff, stateObj
);
8038 WriteShort((short)(rpcext
.ProcID
), stateObj
);
8041 Debug
.Assert(!ADP
.IsEmpty(rpcext
.rpcName
), "must have an RPC name");
8042 tempLen
= rpcext
.rpcName
.Length
;
8043 WriteShort(tempLen
, stateObj
);
8044 WriteString(rpcext
.rpcName
, tempLen
, 0, stateObj
);
8048 WriteShort((short)rpcext
.options
, stateObj
);
8051 // Stream out parameters
8052 SqlParameter
[] parameters
= rpcext
.parameters
;
8054 for (int i
= (ii
== startRpc
) ? startParam
: 0; i
< parameters
.Length
; i
++) {
8055 // Debug.WriteLine("i: " + i.ToString(CultureInfo.InvariantCulture));
8056 // parameters can be unnamed
8057 SqlParameter param
= parameters
[i
];
8058 // Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters.
8060 break; // End of parameters for this execute
8062 // Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
8063 if (param
.ForceColumnEncryption
&&
8064 !(cmd
.ColumnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.Enabled
||
8065 (cmd
.ColumnEncryptionSetting
== SqlCommandColumnEncryptionSetting
.UseConnectionSetting
&& cmd
.Connection
.IsColumnEncryptionSettingEnabled
))) {
8066 throw SQL
.ParamInvalidForceColumnEncryptionSetting(param
.ParameterName
, rpcext
.GetCommandTextOrRpcName());
8069 // Check if the applications wants to force column encryption to avoid sending sensitive data to server
8070 if (param
.ForceColumnEncryption
&& param
.CipherMetadata
== null
8071 && (param
.Direction
== ParameterDirection
.Input
|| param
.Direction
== ParameterDirection
.InputOutput
)) {
8072 // Application wants a parameter to be encrypted before sending it to server, however server doesnt think this parameter needs encryption.
8073 throw SQL
.ParamUnExpectedEncryptionMetadata(param
.ParameterName
, rpcext
.GetCommandTextOrRpcName());
8076 // Validate parameters are not variable length without size and with null value. MDAC 66522
8077 param
.Validate(i
, isCommandProc
);
8079 // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
8080 MetaType mt
= param
.InternalMetaType
;
8082 if (mt
.IsNewKatmaiType
) {
8083 WriteSmiParameter(param
, i
, 0 != (rpcext
.paramoptions
[i
] & TdsEnums
.RPC_PARAM_DEFAULT
), stateObj
);
8087 if ((!_isShiloh
&& !mt
.Is70Supported
) ||
8088 (!_isYukon
&& !mt
.Is80Supported
) ||
8089 (!_isKatmai
&& !mt
.Is90Supported
)) {
8090 throw ADP
.VersionDoesNotSupportDataType(mt
.TypeName
);
8092 object value = null;
8094 bool isSqlVal
= false;
8095 bool isDataFeed
= false;
8096 // if we have an output param, set the value to null so we do not send it across to the server
8097 if (param
.Direction
== ParameterDirection
.Output
) {
8098 isSqlVal
= param
.ParamaterIsSqlType
; // We have to forward the TYPE info, we need to know what type we are returning. Once we null the paramater we will no longer be able to distinguish what type were seeing.
8100 param
.ParamaterIsSqlType
= isSqlVal
;
8103 value = param
.GetCoercedValue();
8104 isNull
= param
.IsNull
;
8106 isSqlVal
= param
.CoercedValueIsSqlType
;
8107 isDataFeed
= param
.CoercedValueIsDataFeed
;
8111 WriteParameterName(param
.ParameterNameFixed
, stateObj
);
8113 // Write parameter status
8114 stateObj
.WriteByte(rpcext
.paramoptions
[i
]);
8116 // MaxLen field is only written out for non-fixed length data types
8117 // use the greater of the two sizes for maxLen
8119 int size
= mt
.IsSizeInCharacters
? param
.GetParameterSize() * 2 : param
.GetParameterSize();
8121 //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
8122 if (mt
.TDSType
!= TdsEnums
.SQLUDT
)
8123 // getting the actualSize is expensive, cache here and use below
8124 actualSize
= param
.GetActualSize();
8126 actualSize
= 0; //get this later
8131 // scale and precision are only relevant for numeric and decimal types
8132 // adjust the actual value scale and precision to match the user specified
8133 if (mt
.SqlDbType
== SqlDbType
.Decimal
) {
8134 precision
= param
.GetActualPrecision();
8135 scale
= param
.GetActualScale();
8137 if (precision
> TdsEnums
.MAX_NUMERIC_PRECISION
) {
8138 throw SQL
.PrecisionValueOutOfRange(precision
);
8141 // bug 49512, make sure the value matches the scale the user enters
8144 value = AdjustSqlDecimalScale((SqlDecimal
)value, scale
);
8146 // If Precision is specified, verify value precision vs param precision
8147 if (precision
!= 0) {
8148 if (precision
< ((SqlDecimal
)value).Precision
) {
8149 throw ADP
.ParameterValueOutOfRange((SqlDecimal
)value);
8154 value = AdjustDecimalScale((Decimal
)value, scale
);
8156 SqlDecimal sqlValue
= new SqlDecimal((Decimal
)value);
8158 // If Precision is specified, verify value precision vs param precision
8159 if (precision
!= 0) {
8160 if (precision
< sqlValue
.Precision
) {
8161 throw ADP
.ParameterValueOutOfRange((Decimal
)value);
8168 bool isParameterEncrypted
= 0 != (rpcext
.paramoptions
[i
] & TdsEnums
.RPC_PARAM_ENCRYPTED
);
8170 // Additional information we need to send over wire to the server when writing encrypted parameters.
8171 SqlColumnEncryptionInputParameterInfo encryptedParameterInfoToWrite
= null;
8173 // If the parameter is encrypted, we need to encrypt the value.
8174 if (isParameterEncrypted
) {
8175 Debug
.Assert(mt
.TDSType
!= TdsEnums
.SQLVARIANT
&&
8176 mt
.TDSType
!= TdsEnums
.SQLUDT
&&
8177 mt
.TDSType
!= TdsEnums
.SQLXMLTYPE
&&
8178 mt
.TDSType
!= TdsEnums
.SQLIMAGE
&&
8179 mt
.TDSType
!= TdsEnums
.SQLTEXT
&&
8180 mt
.TDSType
!= TdsEnums
.SQLNTEXT
, "Type unsupported for encryption");
8182 byte[] serializedValue
= null;
8183 byte[] encryptedValue
= null;
8188 serializedValue
= SerializeUnencryptedSqlValue(value, mt
, actualSize
, param
.Offset
, param
.NormalizationRuleVersion
, stateObj
);
8191 // for codePageEncoded types, WriteValue simply expects the number of characters
8192 // For plp types, we also need the encoded byte size
8193 serializedValue
= SerializeUnencryptedValue(value, mt
, param
.GetActualScale(), actualSize
, param
.Offset
, isDataFeed
, param
.NormalizationRuleVersion
, stateObj
);
8196 Debug
.Assert(serializedValue
!= null, "serializedValue should not be null in TdsExecuteRPC.");
8197 encryptedValue
= SqlSecurityUtility
.EncryptWithKey(serializedValue
, param
.CipherMetadata
, _connHandler
.ConnectionOptions
.DataSource
);
8199 catch (Exception e
) {
8200 throw SQL
.ParamEncryptionFailed(param
.ParameterName
, null, e
);
8203 Debug
.Assert(encryptedValue
!= null && encryptedValue
.Length
> 0,
8204 "encryptedValue should not be null or empty in TdsExecuteRPC.");
8207 encryptedValue
= null;
8210 // Change the datatype to varbinary(max).
8211 // Since we don't know the size of the encrypted parameter on the server side, always set to (max).
8213 mt
= MetaType
.MetaMaxVarBinary
;
8215 actualSize
= (encryptedValue
== null) ? 0 : encryptedValue
.Length
;
8217 encryptedParameterInfoToWrite
= new SqlColumnEncryptionInputParameterInfo(param
.GetMetadataForTypeInfo(),
8218 param
.CipherMetadata
);
8220 // Set the value to the encrypted value and mark isSqlVal as false for VARBINARY encrypted value.
8221 value = encryptedValue
;
8225 Debug
.Assert(isParameterEncrypted
== (encryptedParameterInfoToWrite
!= null),
8226 "encryptedParameterInfoToWrite can be not null if and only if isParameterEncrypted is true.");
8228 Debug
.Assert(!isSqlVal
|| !isParameterEncrypted
, "isParameterEncrypted can be true only if isSqlVal is false.");
8231 // fixup the types by using the NullableType property of the MetaType class
8233 // following rules should be followed based on feedback from the M-SQL team
8234 // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
8235 // 2) always use nullable types (ex: instead of SQLINT use SQLINTN)
8236 // 3) DECIMALN should always be sent as NUMERICN
8238 stateObj
.WriteByte(mt
.NullableType
);
8240 // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
8241 if (mt
.TDSType
== TdsEnums
.SQLVARIANT
) {
8242 // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant
8243 // param.GetActualSize is not used
8244 WriteSqlVariantValue(isSqlVal
? MetaType
.GetComValueFromSqlVariant(value) : value, param
.GetActualSize(), param
.Offset
, stateObj
);
8248 int codePageByteSize
= 0;
8251 if (mt
.IsAnsiType
) {
8252 // Avoid the following code block if ANSI but unfilled LazyMat blob
8253 if ((!isNull
) && (!isDataFeed
)) {
8257 if (value is SqlString
) {
8258 s
= ((SqlString
)value).Value
;
8261 Debug
.Assert(value is SqlChars
, "Unknown value for Ansi datatype");
8262 s
= new String(((SqlChars
)value).Value
);
8269 codePageByteSize
= GetEncodingCharLength(s
, actualSize
, param
.Offset
, _defaultEncoding
);
8273 WriteShort(TdsEnums
.SQL_USHORTVARMAXLEN
, stateObj
);
8276 maxsize
= (size
> codePageByteSize
) ? size
: codePageByteSize
;
8278 // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
8285 WriteParameterVarLen(mt
, maxsize
, false/*IsNull*/, stateObj
);
8289 // If type timestamp - treat as fixed type and always send over timestamp length, which is 8.
8290 // For fixed types, we either send null or fixed length for type length. We want to match that
8291 // behavior for timestamps. However, in the case of null, we still must send 8 because if we
8292 // send null we will not receive a output val. You can send null for fixed types and still
8293 // receive a output value, but not for variable types. So, always send 8 for timestamp because
8294 // while the user sees it as a fixed type, we are actually representing it as a bigbinary which
8296 if (mt
.SqlDbType
== SqlDbType
.Timestamp
) {
8297 WriteParameterVarLen(mt
, TdsEnums
.TEXT_TIME_STAMP_LEN
, false, stateObj
);
8299 else if (mt
.SqlDbType
== SqlDbType
.Udt
) {
8300 byte[] udtVal
= null;
8301 Microsoft
.SqlServer
.Server
.Format format
= Microsoft
.SqlServer
.Server
.Format
.Native
;
8303 Debug
.Assert(_isYukon
, "Invalid DataType UDT for non-Yukon or later server!");
8306 udtVal
= _connHandler
.Connection
.GetBytes(value, out format
, out maxsize
);
8308 Debug
.Assert(null != udtVal
, "GetBytes returned null instance. Make sure that it always returns non-null value");
8309 size
= udtVal
.Length
;
8311 //it may be legitimate, but we dont support it yet
8312 if (size
< 0 || (size
>= UInt16
.MaxValue
&& maxsize
!= -1))
8313 throw new IndexOutOfRangeException();
8316 //if this is NULL value, write special null value
8317 byte[] lenBytes
= BitConverter
.GetBytes((Int64
)size
);
8319 if (ADP
.IsEmpty(param
.UdtTypeName
))
8320 throw SQL
.MustSetUdtTypeNameForUdtParams();
8322 // Split the input name. TypeName is returned as single 3 part name during DeriveParameters.
8323 // NOTE: ParseUdtTypeName throws if format is incorrect
8324 String
[] names
= SqlParameter
.ParseTypeName(param
.UdtTypeName
, true /* is UdtTypeName */);
8325 if (!ADP
.IsEmpty(names
[0]) && TdsEnums
.MAX_SERVERNAME
< names
[0].Length
) {
8326 throw ADP
.ArgumentOutOfRange("names");
8328 if (!ADP
.IsEmpty(names
[1]) && TdsEnums
.MAX_SERVERNAME
< names
[names
.Length
- 2].Length
) {
8329 throw ADP
.ArgumentOutOfRange("names");
8331 if (TdsEnums
.MAX_SERVERNAME
< names
[2].Length
) {
8332 throw ADP
.ArgumentOutOfRange("names");
8335 WriteUDTMetaData(value, names
[0], names
[1], names
[2], stateObj
);
8339 WriteUnsignedLong((ulong)udtVal
.Length
, stateObj
); // PLP length
8340 if (udtVal
.Length
> 0) { // Only write chunk length if its value is greater than 0
8341 WriteInt(udtVal
.Length
, stateObj
); // Chunk length
8342 stateObj
.WriteByteArray(udtVal
, udtVal
.Length
, 0); // Value
8344 WriteInt(0, stateObj
); // Terminator
8347 WriteUnsignedLong(TdsEnums
.SQL_PLP_NULL
, stateObj
); // PLP Null.
8349 continue; // End of UDT - continue to next parameter.
8352 else if (mt
.IsPlp
) {
8353 if (mt
.SqlDbType
!= SqlDbType
.Xml
)
8354 WriteShort(TdsEnums
.SQL_USHORTVARMAXLEN
, stateObj
);
8356 else if ((!mt
.IsVarTime
) && (mt
.SqlDbType
!= SqlDbType
.Date
)) { // Time, Date, DateTime2, DateTimeoffset do not have the size written out
8357 maxsize
= (size
> actualSize
) ? size
: actualSize
;
8358 if (maxsize
== 0 && IsYukonOrNewer
) {
8359 // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
8366 WriteParameterVarLen(mt
, maxsize
, false/*IsNull*/, stateObj
);
8370 // scale and precision are only relevant for numeric and decimal types
8371 if (mt
.SqlDbType
== SqlDbType
.Decimal
) {
8372 if (0 == precision
) {
8374 stateObj
.WriteByte(TdsEnums
.DEFAULT_NUMERIC_PRECISION
);
8376 stateObj
.WriteByte(TdsEnums
.SPHINX_DEFAULT_NUMERIC_PRECISION
);
8379 stateObj
.WriteByte(precision
);
8381 stateObj
.WriteByte(scale
);
8383 else if (mt
.IsVarTime
) {
8384 stateObj
.WriteByte(param
.GetActualScale());
8387 // write out collation or xml metadata
8389 if (_isYukon
&& (mt
.SqlDbType
== SqlDbType
.Xml
)) {
8390 if (((param
.XmlSchemaCollectionDatabase
!= null) && (param
.XmlSchemaCollectionDatabase
!= ADP
.StrEmpty
)) ||
8391 ((param
.XmlSchemaCollectionOwningSchema
!= null) && (param
.XmlSchemaCollectionOwningSchema
!= ADP
.StrEmpty
)) ||
8392 ((param
.XmlSchemaCollectionName
!= null) && (param
.XmlSchemaCollectionName
!= ADP
.StrEmpty
))) {
8393 stateObj
.WriteByte(1); //Schema present flag
8395 if ((param
.XmlSchemaCollectionDatabase
!= null) && (param
.XmlSchemaCollectionDatabase
!= ADP
.StrEmpty
)) {
8396 tempLen
= (param
.XmlSchemaCollectionDatabase
).Length
;
8397 stateObj
.WriteByte((byte)(tempLen
));
8398 WriteString(param
.XmlSchemaCollectionDatabase
, tempLen
, 0, stateObj
);
8401 stateObj
.WriteByte(0); // No dbname
8404 if ((param
.XmlSchemaCollectionOwningSchema
!= null) && (param
.XmlSchemaCollectionOwningSchema
!= ADP
.StrEmpty
)) {
8405 tempLen
= (param
.XmlSchemaCollectionOwningSchema
).Length
;
8406 stateObj
.WriteByte((byte)(tempLen
));
8407 WriteString(param
.XmlSchemaCollectionOwningSchema
, tempLen
, 0, stateObj
);
8410 stateObj
.WriteByte(0); // no xml schema name
8412 if ((param
.XmlSchemaCollectionName
!= null) && (param
.XmlSchemaCollectionName
!= ADP
.StrEmpty
)) {
8413 tempLen
= (param
.XmlSchemaCollectionName
).Length
;
8414 WriteShort((short)(tempLen
), stateObj
);
8415 WriteString(param
.XmlSchemaCollectionName
, tempLen
, 0, stateObj
);
8418 WriteShort(0, stateObj
); // No xml schema collection name
8423 stateObj
.WriteByte(0); // No schema
8426 else if (_isShiloh
&& mt
.IsCharType
) {
8427 // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
8428 SqlCollation outCollation
= (param
.Collation
!= null) ? param
.Collation
: _defaultCollation
;
8429 Debug
.Assert(_defaultCollation
!= null, "_defaultCollation is null!");
8431 WriteUnsignedInt(outCollation
.info
, stateObj
);
8432 stateObj
.WriteByte(outCollation
.sortId
);
8435 if (0 == codePageByteSize
)
8436 WriteParameterVarLen(mt
, actualSize
, isNull
, stateObj
, isDataFeed
);
8438 WriteParameterVarLen(mt
, codePageByteSize
, isNull
, stateObj
, isDataFeed
);
8440 Task writeParamTask
= null;
8441 // write the value now
8444 writeParamTask
= WriteSqlValue(value, mt
, actualSize
, codePageByteSize
, param
.Offset
, stateObj
);
8447 // for codePageEncoded types, WriteValue simply expects the number of characters
8448 // For plp types, we also need the encoded byte size
8449 writeParamTask
= WriteValue(value, mt
, isParameterEncrypted
? (byte)0 : param
.GetActualScale(), actualSize
, codePageByteSize
, isParameterEncrypted
? 0 : param
.Offset
, stateObj
, isParameterEncrypted
? 0 : param
.Size
, isDataFeed
);
8453 // Send encryption metadata for encrypted parameters.
8454 if (isParameterEncrypted
) {
8455 writeParamTask
= WriteEncryptionMetadata(writeParamTask
, encryptedParameterInfoToWrite
, stateObj
);
8459 if (writeParamTask
== null) {
8460 writeParamTask
= stateObj
.WaitForAccumulatedWrites();
8463 if (writeParamTask
!= null) {
8465 if (completion
== null) {
8466 completion
= new TaskCompletionSource
<object>();
8467 task
= completion
.Task
;
8470 AsyncHelper
.ContinueTask(writeParamTask
, completion
,
8471 () => TdsExecuteRPC(cmd
, rpcArray
, timeout
, inSchema
, notificationRequest
, stateObj
, isCommandProc
, sync
, completion
,
8472 startRpc: ii
, startParam
: i
+ 1),
8473 connectionToDoom: _connHandler
,
8474 onFailure: exc
=> TdsExecuteRPC_OnFailure(exc
, stateObj
));
8476 // Take care of releasing the locks
8477 if (releaseConnectionLock
) {
8478 task
.ContinueWith(_
=> {
8479 _connHandler
._parserLock
.Release();
8480 }, TaskScheduler
.Default
);
8481 releaseConnectionLock
= false;
8489 Debug
.Assert(writeParamTask
== null, "Should not have a task when executing sync");
8492 } // parameter for loop
8494 // If this is not the last RPC we are sending, add the batch flag
8495 if (ii
< (rpcArray
.Length
- 1)) {
8497 stateObj
.WriteByte(TdsEnums
.YUKON_RPCBATCHFLAG
);
8501 stateObj
.WriteByte(TdsEnums
.SHILOH_RPCBATCHFLAG
);
8506 Task execFlushTask
= stateObj
.ExecuteFlush();
8507 Debug
.Assert(!sync
|| execFlushTask
== null, "Should not get a task when executing sync");
8508 if (execFlushTask
!= null) {
8511 if (completion
== null) {
8512 completion
= new TaskCompletionSource
<object>();
8513 task
= completion
.Task
;
8516 bool taskReleaseConnectionLock
= releaseConnectionLock
;
8517 execFlushTask
.ContinueWith(tsk
=> ExecuteFlushTaskCallback(tsk
, stateObj
, completion
, taskReleaseConnectionLock
), TaskScheduler
.Default
);
8519 // ExecuteFlushTaskCallback will take care of the locks for us
8520 releaseConnectionLock
= false;
8525 catch (Exception e
) {
8527 if (!ADP
.IsCatchableExceptionType(e
)) {
8531 FailureCleanup(stateObj
, e
);
8535 FinalizeExecuteRPC(stateObj
);
8536 if (completion
!= null) {
8537 completion
.SetResult(null);
8541 catch (Exception e
) {
8542 FinalizeExecuteRPC(stateObj
);
8543 if (completion
!= null) {
8544 completion
.SetException(e
);
8552 Debug
.Assert(firstCall
|| !releaseConnectionLock
, "Shouldn't be releasing locks synchronously after the first call");
8553 if (releaseConnectionLock
) {
8554 _connHandler
._parserLock
.Release();
8559 private void FinalizeExecuteRPC(TdsParserStateObject stateObj
) {
8560 stateObj
.SniContext
= SniContext
.Snix_Read
;
8561 _asyncWrite
= false;
8564 private void TdsExecuteRPC_OnFailure(Exception exc
, TdsParserStateObject stateObj
) {
8565 RuntimeHelpers
.PrepareConstrainedRegions();
8568 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
8570 RuntimeHelpers
.PrepareConstrainedRegions();
8572 tdsReliabilitySection
.Start();
8576 FailureCleanup(stateObj
, exc
);
8580 tdsReliabilitySection
.Stop();
8584 catch (System
.OutOfMemoryException
) {
8585 _connHandler
.DoomThisConnection();
8588 catch (System
.StackOverflowException
) {
8589 _connHandler
.DoomThisConnection();
8592 catch (System
.Threading
.ThreadAbortException
) {
8593 _connHandler
.DoomThisConnection();
8598 private void ExecuteFlushTaskCallback(Task tsk
, TdsParserStateObject stateObj
, TaskCompletionSource
<object> completion
, bool releaseConnectionLock
) {
8600 FinalizeExecuteRPC(stateObj
);
8601 if (tsk
.Exception
!= null) {
8602 Exception exc
= tsk
.Exception
.InnerException
;
8603 RuntimeHelpers
.PrepareConstrainedRegions();
8606 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
8608 RuntimeHelpers
.PrepareConstrainedRegions();
8610 tdsReliabilitySection
.Start();
8614 FailureCleanup(stateObj
, tsk
.Exception
);
8618 tdsReliabilitySection
.Stop();
8622 catch (System
.OutOfMemoryException e
) {
8623 _connHandler
.DoomThisConnection();
8624 completion
.SetException(e
);
8627 catch (System
.StackOverflowException e
) {
8628 _connHandler
.DoomThisConnection();
8629 completion
.SetException(e
);
8632 catch (System
.Threading
.ThreadAbortException e
) {
8633 _connHandler
.DoomThisConnection();
8634 completion
.SetException(e
);
8637 catch (Exception e
) {
8640 completion
.SetException(exc
);
8643 completion
.SetResult(null);
8647 if (releaseConnectionLock
) {
8648 _connHandler
._parserLock
.Release();
8654 private void WriteParameterName(string parameterName
, TdsParserStateObject stateObj
) {
8657 if (!ADP
.IsEmpty(parameterName
)) {
8658 Debug
.Assert(parameterName
.Length
<= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
8659 int tempLen
= parameterName
.Length
& 0xff;
8660 stateObj
.WriteByte((byte)tempLen
);
8661 WriteString(parameterName
, tempLen
, 0, stateObj
);
8664 stateObj
.WriteByte(0);
8668 private static readonly IEnumerable
<MSS
.SqlDataRecord
> __tvpEmptyValue
= new List
<MSS
.SqlDataRecord
>().AsReadOnly();
8669 private void WriteSmiParameter(SqlParameter param
, int paramIndex
, bool sendDefault
, TdsParserStateObject stateObj
) {
8671 // Determine Metadata
8673 ParameterPeekAheadValue peekAhead
;
8674 MSS
.SmiParameterMetaData metaData
= param
.MetaDataForSmi(out peekAhead
);
8677 MetaType mt
= MetaType
.GetMetaTypeFromSqlDbType(metaData
.SqlDbType
, metaData
.IsMultiValued
);
8678 throw ADP
.VersionDoesNotSupportDataType(mt
.TypeName
);
8682 // Determine value to send
8685 MSS
.ExtendedClrTypeCode typeCode
;
8687 // if we have an output or default param, set the value to null so we do not send it across to the server
8689 // Value for TVP default is empty list, not NULL
8690 if (SqlDbType
.Structured
== metaData
.SqlDbType
&& metaData
.IsMultiValued
) {
8691 value = __tvpEmptyValue
;
8692 typeCode
= MSS
.ExtendedClrTypeCode
.IEnumerableOfSqlDataRecord
;
8695 // Need to send null value for default
8697 typeCode
= MSS
.ExtendedClrTypeCode
.DBNull
;
8700 else if (param
.Direction
== ParameterDirection
.Output
) {
8701 bool isCLRType
= param
.ParamaterIsSqlType
; // We have to forward the TYPE info, we need to know what type we are returning. Once we null the paramater we will no longer be able to distinguish what type were seeing.
8704 typeCode
= MSS
.ExtendedClrTypeCode
.DBNull
;
8705 param
.ParamaterIsSqlType
= isCLRType
;
8708 value = param
.GetCoercedValue();
8709 typeCode
= MSS
.MetaDataUtilsSmi
.DetermineExtendedTypeCodeForUseWithSqlDbType(
8710 metaData
.SqlDbType
, metaData
.IsMultiValued
, value, null, MSS
.SmiContextFactory
.KatmaiVersion
);
8713 if (Bid
.AdvancedOn
) {
8714 Bid
.Trace("<sc.TdsParser.WriteSmiParameter|ADV> %d#, Sending parameter '%ls', default flag=%d, metadata:\n", ObjectID
, param
.ParameterName
, sendDefault
?1:0);
8715 Bid
.PutStr(metaData
.TraceString(3));
8720 // Write parameter metadata
8722 WriteSmiParameterMetaData(metaData
, sendDefault
, stateObj
);
8725 // Now write the value
8727 TdsParameterSetter paramSetter
= new TdsParameterSetter(stateObj
, metaData
);
8728 MSS
.ValueUtilsSmi
.SetCompatibleValueV200(
8729 new MSS
.SmiEventSink_Default(), // TDS Errors/events dealt with at lower level for now, just need an object for processing
8731 0, // ordinal. TdsParameterSetter only handles one parameter at a time
8736 0 < param
.Size
? param
.Size
: -1,
8740 // Writes metadata portion of parameter stream from an SmiParameterMetaData object.
8741 private void WriteSmiParameterMetaData(MSS
.SmiParameterMetaData metaData
, bool sendDefault
, TdsParserStateObject stateObj
) {
8744 if (ParameterDirection
.Output
== metaData
.Direction
|| ParameterDirection
.InputOutput
== metaData
.Direction
) {
8745 status
|= TdsEnums
.RPC_PARAM_BYREF
;
8749 status
|= TdsEnums
.RPC_PARAM_DEFAULT
;
8752 // Write everything out
8753 WriteParameterName(metaData
.Name
, stateObj
);
8754 stateObj
.WriteByte(status
);
8755 WriteSmiTypeInfo(metaData
, stateObj
);
8758 // Write a TypeInfo stream
8759 // Devnote: we remap the legacy types (text, ntext, and image) to SQLBIGVARCHAR, SQLNVARCHAR, and SQLBIGVARBINARY
8760 private void WriteSmiTypeInfo(MSS
.SmiExtendedMetaData metaData
, TdsParserStateObject stateObj
) {
8761 switch(metaData
.SqlDbType
) {
8762 case SqlDbType
.BigInt
:
8763 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8764 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8766 case SqlDbType
.Binary
:
8767 stateObj
.WriteByte(TdsEnums
.SQLBIGBINARY
);
8768 WriteUnsignedShort(checked((ushort)metaData
.MaxLength
), stateObj
);
8771 stateObj
.WriteByte(TdsEnums
.SQLBITN
);
8772 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8774 case SqlDbType
.Char
:
8775 stateObj
.WriteByte(TdsEnums
.SQLBIGCHAR
);
8776 WriteUnsignedShort(checked((ushort)(metaData
.MaxLength
)), stateObj
);
8777 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8778 stateObj
.WriteByte(_defaultCollation
.sortId
);
8780 case SqlDbType
.DateTime
:
8781 stateObj
.WriteByte(TdsEnums
.SQLDATETIMN
);
8782 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8784 case SqlDbType
.Decimal
:
8785 stateObj
.WriteByte(TdsEnums
.SQLNUMERICN
);
8786 stateObj
.WriteByte(checked((byte)MetaType
.MetaDecimal
.FixedLength
)); // SmiMetaData's length and actual wire format's length are different
8787 stateObj
.WriteByte(0 == metaData
.Precision
? (byte)1 : metaData
.Precision
);
8788 stateObj
.WriteByte(metaData
.Scale
);
8790 case SqlDbType
.Float
:
8791 stateObj
.WriteByte(TdsEnums
.SQLFLTN
);
8792 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8794 case SqlDbType
.Image
:
8795 stateObj
.WriteByte(TdsEnums
.SQLBIGVARBINARY
);
8796 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8799 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8800 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8802 case SqlDbType
.Money
:
8803 stateObj
.WriteByte(TdsEnums
.SQLMONEYN
);
8804 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8806 case SqlDbType
.NChar
:
8807 stateObj
.WriteByte(TdsEnums
.SQLNCHAR
);
8808 WriteUnsignedShort(checked((ushort)(metaData
.MaxLength
*2)), stateObj
);
8809 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8810 stateObj
.WriteByte(_defaultCollation
.sortId
);
8812 case SqlDbType
.NText
:
8813 stateObj
.WriteByte(TdsEnums
.SQLNVARCHAR
);
8814 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8815 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8816 stateObj
.WriteByte(_defaultCollation
.sortId
);
8818 case SqlDbType
.NVarChar
:
8819 stateObj
.WriteByte(TdsEnums
.SQLNVARCHAR
);
8820 if (MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
== metaData
.MaxLength
) {
8821 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8824 WriteUnsignedShort(checked((ushort)(metaData
.MaxLength
*2)), stateObj
);
8826 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8827 stateObj
.WriteByte(_defaultCollation
.sortId
);
8829 case SqlDbType
.Real
:
8830 stateObj
.WriteByte(TdsEnums
.SQLFLTN
);
8831 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8833 case SqlDbType
.UniqueIdentifier
:
8834 stateObj
.WriteByte(TdsEnums
.SQLUNIQUEID
);
8835 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8837 case SqlDbType
.SmallDateTime
:
8838 stateObj
.WriteByte(TdsEnums
.SQLDATETIMN
);
8839 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8841 case SqlDbType
.SmallInt
:
8842 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8843 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8845 case SqlDbType
.SmallMoney
:
8846 stateObj
.WriteByte(TdsEnums
.SQLMONEYN
);
8847 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8849 case SqlDbType
.Text
:
8850 stateObj
.WriteByte(TdsEnums
.SQLBIGVARCHAR
);
8851 WriteUnsignedShort(unchecked((ushort)MSS
.SmiMetaData
.UnlimitedMaxLengthIndicator
), stateObj
);
8852 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8853 stateObj
.WriteByte(_defaultCollation
.sortId
);
8855 case SqlDbType
.Timestamp
:
8856 stateObj
.WriteByte(TdsEnums
.SQLBIGBINARY
);
8857 WriteShort(checked((int)metaData
.MaxLength
), stateObj
);
8859 case SqlDbType
.TinyInt
:
8860 stateObj
.WriteByte(TdsEnums
.SQLINTN
);
8861 stateObj
.WriteByte(checked((byte)metaData
.MaxLength
));
8863 case SqlDbType
.VarBinary
:
8864 stateObj
.WriteByte(TdsEnums
.SQLBIGVARBINARY
);
8865 WriteUnsignedShort(unchecked((ushort)metaData
.MaxLength
), stateObj
);
8867 case SqlDbType
.VarChar
:
8868 stateObj
.WriteByte(TdsEnums
.SQLBIGVARCHAR
);
8869 WriteUnsignedShort(unchecked((ushort)metaData
.MaxLength
), stateObj
);
8870 WriteUnsignedInt(_defaultCollation
.info
, stateObj
); //
8871 stateObj
.WriteByte(_defaultCollation
.sortId
);
8873 case SqlDbType
.Variant
:
8874 stateObj
.WriteByte(TdsEnums
.SQLVARIANT
);
8875 WriteInt(checked((int)metaData
.MaxLength
), stateObj
);
8878 stateObj
.WriteByte(TdsEnums
.SQLXMLTYPE
);
8879 // Is there a schema
8880 if (ADP
.IsEmpty(metaData
.TypeSpecificNamePart1
) && ADP
.IsEmpty(metaData
.TypeSpecificNamePart2
) &&
8881 ADP
.IsEmpty(metaData
.TypeSpecificNamePart3
)) {
8882 stateObj
.WriteByte(0); // schema not present
8885 stateObj
.WriteByte(1); // schema present
8886 WriteIdentifier(metaData
.TypeSpecificNamePart1
, stateObj
);
8887 WriteIdentifier(metaData
.TypeSpecificNamePart2
, stateObj
);
8888 WriteIdentifierWithShortLength(metaData
.TypeSpecificNamePart3
, stateObj
);
8892 stateObj
.WriteByte(TdsEnums
.SQLUDT
);
8893 WriteIdentifier(metaData
.TypeSpecificNamePart1
, stateObj
);
8894 WriteIdentifier(metaData
.TypeSpecificNamePart2
, stateObj
);
8895 WriteIdentifier(metaData
.TypeSpecificNamePart3
, stateObj
);
8897 case SqlDbType
.Structured
:
8898 if (metaData
.IsMultiValued
) {
8899 WriteTvpTypeInfo(metaData
, stateObj
);
8902 Debug
.Assert(false, "SUDTs not yet supported.");
8905 case SqlDbType
.Date
:
8906 stateObj
.WriteByte(TdsEnums
.SQLDATE
);
8908 case SqlDbType
.Time
:
8909 stateObj
.WriteByte(TdsEnums
.SQLTIME
);
8910 stateObj
.WriteByte(metaData
.Scale
);
8912 case SqlDbType
.DateTime2
:
8913 stateObj
.WriteByte(TdsEnums
.SQLDATETIME2
);
8914 stateObj
.WriteByte(metaData
.Scale
);
8916 case SqlDbType
.DateTimeOffset
:
8917 stateObj
.WriteByte(TdsEnums
.SQLDATETIMEOFFSET
);
8918 stateObj
.WriteByte(metaData
.Scale
);
8921 Debug
.Assert(false, "Unknown SqlDbType should have been caught earlier!");
8926 private void WriteTvpTypeInfo(MSS
.SmiExtendedMetaData metaData
, TdsParserStateObject stateObj
) {
8927 Debug
.Assert(SqlDbType
.Structured
== metaData
.SqlDbType
&& metaData
.IsMultiValued
,
8928 "Invalid metadata for TVPs. Type=" + metaData
.SqlDbType
);
8930 stateObj
.WriteByte((byte)TdsEnums
.SQLTABLE
);
8932 // 3-part name (DB, Schema, TypeName)
8933 WriteIdentifier(metaData
.TypeSpecificNamePart1
, stateObj
);
8934 WriteIdentifier(metaData
.TypeSpecificNamePart2
, stateObj
);
8935 WriteIdentifier(metaData
.TypeSpecificNamePart3
, stateObj
);
8938 if (0 == metaData
.FieldMetaData
.Count
) {
8939 WriteUnsignedShort((ushort)TdsEnums
.TVP_NOMETADATA_TOKEN
, stateObj
);
8943 WriteUnsignedShort(checked((ushort) metaData
.FieldMetaData
.Count
), stateObj
);
8945 // TvpColumnMetaData for each column (look for defaults in this loop
8946 MSS
.SmiDefaultFieldsProperty defaults
= (MSS
.SmiDefaultFieldsProperty
) metaData
.ExtendedProperties
[MSS
.SmiPropertySelector
.DefaultFields
];
8947 for(int i
=0; i
<metaData
.FieldMetaData
.Count
; i
++) {
8948 WriteTvpColumnMetaData(metaData
.FieldMetaData
[i
], defaults
[i
], stateObj
);
8951 // optional OrderUnique metadata
8952 WriteTvpOrderUnique(metaData
, stateObj
);
8956 // END of optional metadata
8957 stateObj
.WriteByte(TdsEnums
.TVP_END_TOKEN
);
8960 // Write a single TvpColumnMetaData stream to the server
8961 private void WriteTvpColumnMetaData(MSS
.SmiExtendedMetaData md
, bool isDefault
, TdsParserStateObject stateObj
) {
8963 if (SqlDbType
.Timestamp
== md
.SqlDbType
) {
8964 WriteUnsignedInt(TdsEnums
.SQLTIMESTAMP
, stateObj
);
8966 WriteUnsignedInt(0, stateObj
);
8970 ushort status
= TdsEnums
.Nullable
;
8972 status
|= TdsEnums
.TVP_DEFAULT_COLUMN
;
8974 WriteUnsignedShort(status
, stateObj
);
8977 WriteSmiTypeInfo(md
, stateObj
);
8980 // per spec, "ColName is never sent to server or client for TVP, it is required within a TVP to be zero length."
8981 WriteIdentifier(null, stateObj
);
8984 // temporary-results structure used only by WriteTvpOrderUnique
8985 // use class to avoid List<T>'s per-struct-instantiated memory costs.
8986 private class TdsOrderUnique
{
8987 internal short ColumnOrdinal
;
8988 internal byte Flags
;
8990 internal TdsOrderUnique(short ordinal
, byte flags
) {
8991 ColumnOrdinal
= ordinal
;
8996 private void WriteTvpOrderUnique(MSS
.SmiExtendedMetaData metaData
, TdsParserStateObject stateObj
) {
8997 // TVP_ORDER_UNIQUE token (uniqueness and sort order)
8999 // Merge order and unique keys into a single token stream
9001 MSS
.SmiOrderProperty orderProperty
= (MSS
.SmiOrderProperty
) metaData
.ExtendedProperties
[MSS
.SmiPropertySelector
.SortOrder
];
9002 MSS
.SmiUniqueKeyProperty uniqueKeyProperty
= (MSS
.SmiUniqueKeyProperty
) metaData
.ExtendedProperties
[MSS
.SmiPropertySelector
.UniqueKey
];
9005 List
<TdsOrderUnique
> columnList
= new List
<TdsOrderUnique
>(metaData
.FieldMetaData
.Count
);
9006 for(int i
=0; i
<metaData
.FieldMetaData
.Count
; i
++) {
9008 // Add appropriate SortOrder flag
9010 MSS
.SmiOrderProperty
.SmiColumnOrder columnOrder
= orderProperty
[i
];
9011 if (SortOrder
.Ascending
== columnOrder
.Order
) {
9012 flags
= TdsEnums
.TVP_ORDERASC_FLAG
;
9014 else if (SortOrder
.Descending
== columnOrder
.Order
) {
9015 flags
= TdsEnums
.TVP_ORDERDESC_FLAG
;
9018 // Add unique key flage if appropriate
9019 if (uniqueKeyProperty
[i
]) {
9020 flags
|= TdsEnums
.TVP_UNIQUE_FLAG
;
9023 // Remember this column if any flags were set
9025 columnList
.Add(new TdsOrderUnique(checked((short)(i
+1)), flags
));
9029 // Write flagged columns to wire...
9030 if (0 < columnList
.Count
) {
9031 stateObj
.WriteByte(TdsEnums
.TVP_ORDER_UNIQUE_TOKEN
);
9032 WriteShort(columnList
.Count
, stateObj
);
9033 foreach(TdsOrderUnique column
in columnList
) {
9034 WriteShort(column
.ColumnOrdinal
, stateObj
);
9035 stateObj
.WriteByte(column
.Flags
);
9040 internal Task
WriteBulkCopyDone(TdsParserStateObject stateObj
) {
9041 // Write DONE packet
9043 if (!(State
== TdsParserState
.OpenNotLoggedIn
|| State
== TdsParserState
.OpenLoggedIn
)) {
9044 throw ADP
.ClosedConnectionError();
9046 stateObj
.WriteByte(TdsEnums
.SQLDONE
);
9047 WriteShort(0, stateObj
);
9048 WriteShort(0, stateObj
);
9049 WriteInt(0, stateObj
);
9051 stateObj
._pendingData
= true;
9052 stateObj
._messageStatus
= 0;
9053 return stateObj
.WritePacket(TdsEnums
.HARDFLUSH
);
9057 /// Loads the column encryptions keys into cache. This will read the master key info,
9058 /// decrypt the CEK and keep it ready for encryption.
9060 /// <returns></returns>
9061 internal void LoadColumnEncryptionKeys (_SqlMetaDataSet metadataCollection
, string serverName
) {
9062 if (_serverSupportsColumnEncryption
&& ShouldEncryptValuesForBulkCopy()) {
9063 for (int col
= 0; col
< metadataCollection
.Length
; col
++) {
9064 if (null != metadataCollection
[col
]) {
9065 _SqlMetaData md
= metadataCollection
[col
];
9066 if (md
.isEncrypted
) {
9067 SqlSecurityUtility
.DecryptSymmetricKey(md
.cipherMD
, serverName
);
9075 /// Writes a single entry of CEK Table into TDS Stream (for bulk copy).
9077 /// <returns></returns>
9078 internal void WriteEncryptionEntries (ref SqlTceCipherInfoTable cekTable
, TdsParserStateObject stateObj
) {
9079 for (int i
=0; i
< cekTable
.Size
; i
++) {
9081 WriteInt(cekTable
[i
].DatabaseId
, stateObj
);
9084 WriteInt(cekTable
[i
].CekId
, stateObj
);
9086 // Write Key Version
9087 WriteInt(cekTable
[i
].CekVersion
, stateObj
);
9089 // Write 8 bytes of key MD Version
9090 Debug
.Assert (8 == cekTable
[i
].CekMdVersion
.Length
);
9091 stateObj
.WriteByteArray (cekTable
[i
].CekMdVersion
, 8, 0);
9093 // We don't really need to send the keys
9094 stateObj
.WriteByte(0x00);
9099 /// Writes a CEK Table (as part of COLMETADATA token) for bulk copy.
9101 /// <returns></returns>
9102 internal void WriteCekTable (_SqlMetaDataSet metadataCollection
, TdsParserStateObject stateObj
) {
9103 if (!_serverSupportsColumnEncryption
) {
9107 // If no cek table is present, send a count of 0 for table size
9108 // Note- Cek table (with 0 entries) will be present if TCE
9109 // was enabled and server supports it!
9110 // OR if encryption was disabled in connection options
9111 if (!metadataCollection
.cekTable
.HasValue
||
9112 !ShouldEncryptValuesForBulkCopy()) {
9113 WriteShort(0x00, stateObj
);
9117 SqlTceCipherInfoTable cekTable
= metadataCollection
.cekTable
.Value
;
9118 ushort count
= (ushort)cekTable
.Size
;
9120 WriteShort(count
, stateObj
);
9122 WriteEncryptionEntries(ref cekTable
, stateObj
);
9126 /// Writes the UserType and TYPE_INFO values for CryptoMetadata (for bulk copy).
9128 /// <returns></returns>
9129 internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv
, TdsParserStateObject stateObj
) {
9130 // Write the UserType (4 byte value)
9131 WriteInt(0x0, stateObj
); //
9133 Debug
.Assert(SqlDbType
.Xml
!= mdPriv
.type
);
9134 Debug
.Assert(SqlDbType
.Udt
!= mdPriv
.type
);
9136 stateObj
.WriteByte(mdPriv
.tdsType
);
9138 switch (mdPriv
.type
) {
9139 case SqlDbType
.Decimal
:
9140 WriteTokenLength(mdPriv
.tdsType
, mdPriv
.length
, stateObj
);
9141 stateObj
.WriteByte(mdPriv
.precision
);
9142 stateObj
.WriteByte(mdPriv
.scale
);
9144 case SqlDbType
.Date
:
9145 // Nothing more to write!
9147 case SqlDbType
.Time
:
9148 case SqlDbType
.DateTime2
:
9149 case SqlDbType
.DateTimeOffset
:
9150 stateObj
.WriteByte(mdPriv
.scale
);
9153 WriteTokenLength(mdPriv
.tdsType
, mdPriv
.length
, stateObj
);
9154 if (mdPriv
.metaType
.IsCharType
&& _isShiloh
) {
9155 WriteUnsignedInt(mdPriv
.collation
.info
, stateObj
);
9156 stateObj
.WriteByte(mdPriv
.collation
.sortId
);
9163 /// Writes the crypto metadata (as part of COLMETADATA token) for encrypted columns.
9165 /// <returns></returns>
9166 internal void WriteCryptoMetadata(_SqlMetaData md
, TdsParserStateObject stateObj
) {
9167 if (!_serverSupportsColumnEncryption
|| // TCE Feature supported
9168 !md
.isEncrypted
|| // Column is not encrypted
9169 !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string
9173 // Write the ordinal
9174 WriteShort (md
.cipherMD
.CekTableOrdinal
, stateObj
);
9176 // Write UserType and TYPEINFO
9177 WriteTceUserTypeAndTypeInfo(md
.baseTI
, stateObj
);
9179 // Write Encryption Algo
9180 stateObj
.WriteByte(md
.cipherMD
.CipherAlgorithmId
);
9182 if (TdsEnums
.CustomCipherAlgorithmId
== md
.cipherMD
.CipherAlgorithmId
) {
9183 // Write the algorithm name
9184 Debug
.Assert (md
.cipherMD
.CipherAlgorithmName
.Length
< 256);
9185 stateObj
.WriteByte((byte)md
.cipherMD
.CipherAlgorithmName
.Length
);
9186 WriteString(md
.cipherMD
.CipherAlgorithmName
, stateObj
);
9189 // Write Encryption Algo Type
9190 stateObj
.WriteByte(md
.cipherMD
.EncryptionType
);
9192 // Write Normalization Version
9193 stateObj
.WriteByte(md
.cipherMD
.NormalizationRuleVersion
);
9196 internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection
, int count
, TdsParserStateObject stateObj
) {
9197 if (!(State
== TdsParserState
.OpenNotLoggedIn
|| State
== TdsParserState
.OpenLoggedIn
)) {
9198 throw ADP
.ClosedConnectionError();
9201 stateObj
.WriteByte(TdsEnums
.SQLCOLMETADATA
);
9202 WriteShort(count
, stateObj
);
9204 // Write CEK table - 0 count
9205 WriteCekTable(metadataCollection
, stateObj
);
9207 for (int i
= 0; i
< metadataCollection
.Length
; i
++) {
9208 if (metadataCollection
[i
] != null) {
9209 _SqlMetaData md
= metadataCollection
[i
];
9211 // read user type - 4 bytes Yukon, 2 backwards
9212 if (IsYukonOrNewer
) {
9213 WriteInt(0x0, stateObj
);
9216 WriteShort(0x0000, stateObj
);
9221 flags
= (UInt16
)(md
.updatability
<< 2);
9222 flags
|= (UInt16
)(md
.isNullable
? (UInt16
)TdsEnums
.Nullable
: (UInt16
)0);
9223 flags
|= (UInt16
)(md
.isIdentity
? (UInt16
)TdsEnums
.Identity
: (UInt16
)0);
9225 // Write the next byte of flags
9226 if (_serverSupportsColumnEncryption
) { // TCE Supported
9227 if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options
9228 flags
|= (UInt16
)(md
.isEncrypted
? (UInt16
)(TdsEnums
.IsEncrypted
<< 8) : (UInt16
)0);
9232 WriteShort(flags
, stateObj
);// write the flags
9235 // for xml WriteTokenLength results in a no-op
9236 // discuss this with blaine ...
9237 // (Microsoft) xml datatype does not have token length in its metadata. So it should be a noop.
9240 case SqlDbType
.Decimal
:
9241 stateObj
.WriteByte(md
.tdsType
);
9242 WriteTokenLength(md
.tdsType
, md
.length
, stateObj
);
9243 stateObj
.WriteByte(md
.precision
);
9244 stateObj
.WriteByte(md
.scale
);
9248 stateObj
.WriteByteArray(s_xmlMetadataSubstituteSequence
, s_xmlMetadataSubstituteSequence
.Length
, 0);
9251 stateObj
.WriteByte(TdsEnums
.SQLBIGVARBINARY
);
9252 WriteTokenLength(TdsEnums
.SQLBIGVARBINARY
, md
.length
, stateObj
);
9254 case SqlDbType
.Date
:
9255 stateObj
.WriteByte(md
.tdsType
);
9257 case SqlDbType
.Time
:
9258 case SqlDbType
.DateTime2
:
9259 case SqlDbType
.DateTimeOffset
:
9260 stateObj
.WriteByte(md
.tdsType
);
9261 stateObj
.WriteByte(md
.scale
);
9264 stateObj
.WriteByte(md
.tdsType
);
9265 WriteTokenLength(md
.tdsType
, md
.length
, stateObj
);
9266 if (md
.metaType
.IsCharType
&& _isShiloh
) {
9267 WriteUnsignedInt(md
.collation
.info
, stateObj
);
9268 stateObj
.WriteByte(md
.collation
.sortId
);
9273 if (md
.metaType
.IsLong
&& !md
.metaType
.IsPlp
) {
9274 WriteShort(md
.tableName
.Length
, stateObj
);
9275 WriteString(md
.tableName
, stateObj
);
9278 WriteCryptoMetadata(md
, stateObj
);
9280 stateObj
.WriteByte((byte)md
.column
.Length
);
9281 WriteString(md
.column
, stateObj
);
9287 /// Determines if a column value should be encrypted when using BulkCopy (based on connectionstring setting).
9289 /// <returns></returns>
9290 internal bool ShouldEncryptValuesForBulkCopy () {
9291 if (null != _connHandler
&&
9292 null != _connHandler
.ConnectionOptions
&&
9293 SqlConnectionColumnEncryptionSetting
.Enabled
== _connHandler
.ConnectionOptions
.ColumnEncryptionSetting
) {
9301 /// Encrypts a column value (for SqlBulkCopy)
9303 /// <returns></returns>
9304 internal object EncryptColumnValue (object value, SqlMetaDataPriv metadata
, string column
, TdsParserStateObject stateObj
, bool isDataFeed
, bool isSqlType
) {
9305 Debug
.Assert (_serverSupportsColumnEncryption
, "Server doesn't support encryption, yet we received encryption metadata");
9306 Debug
.Assert (ShouldEncryptValuesForBulkCopy(), "Encryption attempted when not requested");
9308 if (isDataFeed
) { // can't encrypt a stream column
9309 SQL
.StreamNotSupportOnEncryptedColumn(column
);
9312 int actualLengthInBytes
;
9313 switch(metadata
.baseTI
.metaType
.NullableType
) {
9314 case TdsEnums
.SQLBIGBINARY
:
9315 case TdsEnums
.SQLBIGVARBINARY
:
9316 case TdsEnums
.SQLIMAGE
:
9317 // For some datatypes, engine does truncation before storing the value. (For example, when
9318 // trying to insert a varbinary(7000) into a varbinary(3000) column). Since we encrypt the
9319 // column values, engine has no way to tell the size of the plaintext datatype. Therefore,
9320 // we truncate the values based on target column sizes here before encrypting them. This
9321 // truncation is only needed if we exceed the max column length or if the target column is
9322 // not a blob type (eg. varbinary(max)). The actual work of truncating the column happens
9323 // when we normalize and serialize the data buffers. The serialization routine expects us
9324 // to report the size of data to be copied out (for serialization). If we underreport the
9325 // size, truncation will happen for us!
9326 actualLengthInBytes
= (isSqlType
) ? ((SqlBinary
)value).Length
: ((byte[])value).Length
;
9327 if (metadata
.baseTI
.length
> 0 &&
9328 actualLengthInBytes
> metadata
.baseTI
.length
) { // see comments agove
9329 actualLengthInBytes
= metadata
.baseTI
.length
;
9333 case TdsEnums
.SQLUNIQUEID
:
9334 actualLengthInBytes
= GUID_SIZE
; // that's a constant for guid
9336 case TdsEnums
.SQLBIGCHAR
:
9337 case TdsEnums
.SQLBIGVARCHAR
:
9338 case TdsEnums
.SQLTEXT
:
9339 if (null == _defaultEncoding
)
9341 ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
9344 string stringValue
= (isSqlType
) ? ((SqlString
)value).Value
: (string)value;
9345 actualLengthInBytes
= _defaultEncoding
.GetByteCount(stringValue
);
9347 // If the string length is > max length, then use the max length (see comments above)
9348 if (metadata
.baseTI
.length
> 0 &&
9349 actualLengthInBytes
> metadata
.baseTI
.length
) {
9350 actualLengthInBytes
= metadata
.baseTI
.length
; // this ensure truncation!
9354 case TdsEnums
.SQLNCHAR
:
9355 case TdsEnums
.SQLNVARCHAR
:
9356 case TdsEnums
.SQLNTEXT
:
9357 actualLengthInBytes
= ((isSqlType
) ? ((SqlString
)value).Value
.Length
: ((string)value).Length
) * 2;
9359 if (metadata
.baseTI
.length
> 0 &&
9360 actualLengthInBytes
> metadata
.baseTI
.length
) { // see comments above
9361 actualLengthInBytes
= metadata
.baseTI
.length
;
9367 actualLengthInBytes
= metadata
.baseTI
.length
;
9371 byte[] serializedValue
;
9374 serializedValue
= SerializeUnencryptedSqlValue (value,
9375 metadata
.baseTI
.metaType
,
9376 actualLengthInBytes
,
9378 normalizationVersion: metadata
.cipherMD
.NormalizationRuleVersion
,
9379 stateObj: stateObj
);
9382 serializedValue
= SerializeUnencryptedValue (value,
9383 metadata
.baseTI
.metaType
,
9384 metadata
.baseTI
.scale
,
9385 actualLengthInBytes
,
9387 isDataFeed: isDataFeed
,
9388 normalizationVersion: metadata
.cipherMD
.NormalizationRuleVersion
,
9389 stateObj: stateObj
);
9392 Debug
.Assert(serializedValue
!= null, "serializedValue should not be null in TdsExecuteRPC.");
9393 return SqlSecurityUtility
.EncryptWithKey(
9396 _connHandler
.ConnectionOptions
.DataSource
);
9399 internal Task
WriteBulkCopyValue(object value, SqlMetaDataPriv metadata
, TdsParserStateObject stateObj
, bool isSqlType
, bool isDataFeed
, bool isNull
) {
9400 Debug
.Assert(!isSqlType
|| value is INullable
, "isSqlType is true, but value can not be type cast to an INullable");
9401 Debug
.Assert(!isDataFeed ^
value is DataFeed
, "Incorrect value for isDataFeed");
9403 Encoding saveEncoding
= _defaultEncoding
;
9404 SqlCollation saveCollation
= _defaultCollation
;
9405 int saveCodePage
= _defaultCodePage
;
9406 int saveLCID
= _defaultLCID
;
9407 Task resultTask
= null;
9408 Task internalWriteTask
= null;
9410 if (!(State
== TdsParserState
.OpenNotLoggedIn
|| State
== TdsParserState
.OpenLoggedIn
)) {
9411 throw ADP
.ClosedConnectionError();
9414 if (metadata
.encoding
!= null) {
9415 _defaultEncoding
= metadata
.encoding
;
9417 if (metadata
.collation
!= null) {
9418 _defaultCollation
= metadata
.collation
;
9419 _defaultLCID
= _defaultCollation
.LCID
;
9421 _defaultCodePage
= metadata
.codePage
;
9423 MetaType metatype
= metadata
.metaType
;
9425 int ccbStringBytes
= 0;
9428 // For UDT, remember we treat as binary even though it is a PLP
9429 if (metatype
.IsPlp
&& (metatype
.NullableType
!= TdsEnums
.SQLUDT
|| metatype
.IsLong
)) {
9430 WriteLong(unchecked((long)TdsEnums
.SQL_PLP_NULL
), stateObj
);
9432 else if (!metatype
.IsFixed
&& !metatype
.IsLong
&& !metatype
.IsVarTime
) {
9433 WriteShort(TdsEnums
.VARNULL
, stateObj
);
9436 stateObj
.WriteByte(TdsEnums
.FIXEDNULL
);
9442 switch (metatype
.NullableType
) {
9443 case TdsEnums
.SQLBIGBINARY
:
9444 case TdsEnums
.SQLBIGVARBINARY
:
9445 case TdsEnums
.SQLIMAGE
:
9446 case TdsEnums
.SQLUDT
:
9447 ccb
= (isSqlType
) ? ((SqlBinary
)value).Length
: ((byte[])value).Length
;
9449 case TdsEnums
.SQLUNIQUEID
:
9450 ccb
= GUID_SIZE
; // that's a constant for guid
9452 case TdsEnums
.SQLBIGCHAR
:
9453 case TdsEnums
.SQLBIGVARCHAR
:
9454 case TdsEnums
.SQLTEXT
:
9455 if (null == _defaultEncoding
) {
9456 ThrowUnsupportedCollationEncountered(null); // stateObject only when reading
9459 string stringValue
= null;
9461 stringValue
= ((SqlString
)value).Value
;
9464 stringValue
= (string)value;
9467 ccb
= stringValue
.Length
;
9468 ccbStringBytes
= _defaultEncoding
.GetByteCount(stringValue
);
9470 case TdsEnums
.SQLNCHAR
:
9471 case TdsEnums
.SQLNVARCHAR
:
9472 case TdsEnums
.SQLNTEXT
:
9473 ccb
= ((isSqlType
) ? ((SqlString
)value).Value
.Length
: ((string)value).Length
) * 2;
9475 case TdsEnums
.SQLXMLTYPE
:
9476 // Value here could be string or XmlReader
9477 if (value is XmlReader
) {
9478 value = MetaType
.GetStringFromXml((XmlReader
)value);
9480 ccb
= ((isSqlType
) ? ((SqlString
)value).Value
.Length
: ((string)value).Length
) * 2;
9484 ccb
= metadata
.length
;
9489 Debug
.Assert(metatype
.IsLong
&&
9490 ((metatype
.SqlDbType
== SqlDbType
.VarBinary
&& value is StreamDataFeed
) ||
9491 ((metatype
.SqlDbType
== SqlDbType
.VarChar
|| metatype
.SqlDbType
== SqlDbType
.NVarChar
) && value is TextDataFeed
) ||
9492 (metatype
.SqlDbType
== SqlDbType
.Xml
&& value is XmlDataFeed
)),
9493 "Stream data feed should only be assigned to VarBinary(max), Text data feed should only be assigned to [N]VarChar(max), Xml data feed should only be assigned to XML(max)");
9497 // Expected the text length in data stream for bulk copy of text, ntext, or image data.
9499 if (metatype
.IsLong
) {
9500 switch (metatype
.SqlDbType
) {
9501 case SqlDbType
.Text
:
9502 case SqlDbType
.NText
:
9503 case SqlDbType
.Image
:
9504 stateObj
.WriteByteArray(s_longDataHeader
, s_longDataHeader
.Length
, 0);
9505 WriteTokenLength(metadata
.tdsType
, ccbStringBytes
== 0 ? ccb
: ccbStringBytes
, stateObj
);
9508 case SqlDbType
.VarChar
:
9509 case SqlDbType
.NVarChar
:
9510 case SqlDbType
.VarBinary
:
9514 WriteUnsignedLong(TdsEnums
.SQL_PLP_UNKNOWNLEN
, stateObj
);
9519 WriteTokenLength(metadata
.tdsType
, ccbStringBytes
== 0 ? ccb
: ccbStringBytes
, stateObj
);
9523 internalWriteTask
= WriteSqlValue(value, metatype
, ccb
, ccbStringBytes
, 0, stateObj
);
9525 else if (metatype
.SqlDbType
!= SqlDbType
.Udt
|| metatype
.IsLong
) {
9526 internalWriteTask
= WriteValue(value, metatype
, metadata
.scale
, ccb
, ccbStringBytes
, 0, stateObj
, metadata
.length
, isDataFeed
);
9527 if ((internalWriteTask
== null) && (_asyncWrite
)) {
9528 internalWriteTask
= stateObj
.WaitForAccumulatedWrites();
9530 Debug
.Assert(_asyncWrite
|| stateObj
.WaitForAccumulatedWrites() == null, "Should not have accumulated writes when writing sync");
9533 WriteShort(ccb
, stateObj
);
9534 internalWriteTask
= stateObj
.WriteByteArray((byte[])value, ccb
, 0);
9538 //In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths.
9539 if (_asyncWrite
&& internalWriteTask
== null && SqlBulkCopy
.SetAlwaysTaskOnWrite
== true) {
9540 internalWriteTask
= Task
.FromResult
<object>(null);
9543 if (internalWriteTask
!= null) { //i.e. the write was async.
9544 resultTask
= WriteBulkCopyValueSetupContinuation(internalWriteTask
, saveEncoding
, saveCollation
, saveCodePage
, saveLCID
);
9548 if (internalWriteTask
== null) {
9549 _defaultEncoding
= saveEncoding
;
9550 _defaultCollation
= saveCollation
;
9551 _defaultCodePage
= saveCodePage
;
9552 _defaultLCID
= saveLCID
;
9558 // This is in its own method to avoid always allocating the lambda in WriteBulkCopyValue
9559 private Task
WriteBulkCopyValueSetupContinuation(Task internalWriteTask
, Encoding saveEncoding
, SqlCollation saveCollation
, int saveCodePage
, int saveLCID
) {
9560 return internalWriteTask
.ContinueWith
<Task
>(t
=> {
9561 _defaultEncoding
= saveEncoding
;
9562 _defaultCollation
= saveCollation
;
9563 _defaultCodePage
= saveCodePage
;
9564 _defaultLCID
= saveLCID
;
9566 }, TaskScheduler
.Default
).Unwrap();
9569 // Write mars header data, not including the mars header length
9570 private void WriteMarsHeaderData(TdsParserStateObject stateObj
, SqlInternalTransaction transaction
) {
9571 // Function to send over additional payload header data for Yukon and beyond only.
9572 Debug
.Assert(_isYukon
, "WriteMarsHeaderData called on a non-Yukon server");
9574 // These are not necessary - can have local started in distributed.
9575 // Debug.Assert(!(null != sqlTransaction && null != distributedTransaction), "Error to have local (api started) and distributed transaction at the same time!");
9576 // Debug.Assert(!(null != _userStartedLocalTransaction && null != distributedTransaction), "Error to have local (started outside of the api) and distributed transaction at the same time!");
9578 // We may need to update the mars header length if mars header is changed in the future
9580 WriteShort(TdsEnums
.HEADERTYPE_MARS
, stateObj
);
9582 if (null != transaction
&& SqlInternalTransaction
.NullTransactionId
!= transaction
.TransactionId
) {
9583 WriteLong(transaction
.TransactionId
, stateObj
);
9584 WriteInt(stateObj
.IncrementAndObtainOpenResultCount(transaction
), stateObj
);
9587 // If no transaction, send over retained transaction descriptor (empty if none retained)
9588 // and always 1 for result count.
9589 WriteLong(_retainedTransactionId
, stateObj
);
9590 WriteInt(stateObj
.IncrementAndObtainOpenResultCount(null), stateObj
);
9594 private int GetNotificationHeaderSize(SqlNotificationRequest notificationRequest
) {
9595 if (null != notificationRequest
) {
9596 string callbackId
= notificationRequest
.UserData
;
9597 string service
= notificationRequest
.Options
;
9598 int timeout
= notificationRequest
.Timeout
;
9600 if (null == callbackId
) {
9601 throw ADP
.ArgumentNull("CallbackId");
9603 else if (UInt16
.MaxValue
< callbackId
.Length
) {
9604 throw ADP
.ArgumentOutOfRange("CallbackId");
9607 if (null == service
) {
9608 throw ADP
.ArgumentNull("Service");
9610 else if (UInt16
.MaxValue
< service
.Length
) {
9611 throw ADP
.ArgumentOutOfRange("Service");
9615 throw ADP
.ArgumentOutOfRange("Timeout");
9618 // Header Length (uint) (included in size) (already written to output buffer)
9619 // Header Type (ushort)
9620 // NotifyID Length (ushort)
9621 // NotifyID UnicodeStream (unicode text)
9622 // SSBDeployment Length (ushort)
9623 // SSBDeployment UnicodeStream (unicode text)
9624 // Timeout (uint) -- optional
9625 // WEBDATA 102263: Don't send timeout value if it is 0
9627 int headerLength
= 4 + 2 + 2 + (callbackId
.Length
* 2) + 2 + (service
.Length
* 2);
9630 return headerLength
;
9637 // Write query notificaiton header data, not including the notificaiton header length
9638 private void WriteQueryNotificationHeaderData(SqlNotificationRequest notificationRequest
, TdsParserStateObject stateObj
) {
9639 Debug
.Assert(_isYukon
, "WriteQueryNotificationHeaderData called on a non-Yukon server");
9641 // We may need to update the notification header length if the header is changed in the future
9643 Debug
.Assert (null != notificationRequest
, "notificaitonRequest is null");
9645 string callbackId
= notificationRequest
.UserData
;
9646 string service
= notificationRequest
.Options
;
9647 int timeout
= notificationRequest
.Timeout
;
9649 // we did verification in GetNotificationHeaderSize, so just assert here.
9650 Debug
.Assert(null != callbackId
, "CallbackId is null");
9651 Debug
.Assert(UInt16
.MaxValue
>= callbackId
.Length
, "CallbackId length is out of range");
9652 Debug
.Assert(null != service
, "Service is null");
9653 Debug
.Assert(UInt16
.MaxValue
>= service
.Length
, "Service length is out of range");
9654 Debug
.Assert(-1 <= timeout
, "Timeout");
9657 Bid
.NotificationsTrace("<sc.TdsParser.WriteQueryNotificationHeader|DEP> NotificationRequest: userData: '%ls', options: '%ls', timeout: '%d'\n", notificationRequest
.UserData
, notificationRequest
.Options
, notificationRequest
.Timeout
);
9659 WriteShort(TdsEnums
.HEADERTYPE_QNOTIFICATION
, stateObj
); // Query notifications Type
9661 WriteShort(callbackId
.Length
* 2, stateObj
); // Length in bytes
9662 WriteString(callbackId
, stateObj
);
9664 WriteShort(service
.Length
* 2, stateObj
); // Length in bytes
9665 WriteString(service
, stateObj
);
9667 WriteInt(timeout
, stateObj
);
9670 // Write the trace header data, not including the trace header length
9671 private void WriteTraceHeaderData(TdsParserStateObject stateObj
) {
9672 Debug
.Assert(this.IncludeTraceHeader
, "WriteTraceHeaderData can only be called on a Denali or higher version server and bid trace with the control bit are on");
9674 // We may need to update the trace header length if trace header is changed in the future
9676 ActivityCorrelator
.ActivityId actId
= ActivityCorrelator
.Current
;
9678 WriteShort(TdsEnums
.HEADERTYPE_TRACE
, stateObj
); // Trace Header Type
9680 stateObj
.WriteByteArray(actId
.Id
.ToByteArray(), GUID_SIZE
, 0); // Id (Guid)
9681 WriteUnsignedInt(actId
.Sequence
, stateObj
); // sequence number
9683 Bid
.Trace("<sc.TdsParser.WriteTraceHeaderData|INFO> ActivityID %ls\n", actId
.ToString());
9686 private void WriteRPCBatchHeaders(TdsParserStateObject stateObj
, SqlNotificationRequest notificationRequest
) {
9687 Debug
.Assert(_isYukon
, "WriteRPCBatchHeaders can only be called on Yukon or higher version servers");
9690 TotalLength - DWORD - including all headers and lengths, including itself
9693 HeaderLength - DWORD - including all header length fields, including itself
9699 int notificationHeaderSize
= GetNotificationHeaderSize(notificationRequest
);
9701 const int marsHeaderSize
= 18; // 4 + 2 + 8 + 4
9703 // Header Length (DWORD)
9704 // Header Type (ushort)
9706 // Trace Data Sequence Number (uint)
9707 const int traceHeaderSize
= 26; // 4 + 2 + GUID_SIZE + sizeof(UInt32);
9709 // TotalLength - DWORD - including all headers and lengths, including itself
9710 int totalHeaderLength
= this.IncludeTraceHeader
? (4 + marsHeaderSize
+ notificationHeaderSize
+ traceHeaderSize
) : (4 + marsHeaderSize
+ notificationHeaderSize
);
9711 Debug
.Assert(stateObj
._outBytesUsed
== stateObj
._outputHeaderLen
, "Output bytes written before total header length");
9712 // Write total header length
9713 WriteInt(totalHeaderLength
, stateObj
);
9715 // Write Mars header length
9716 WriteInt(marsHeaderSize
, stateObj
);
9717 // Write Mars header data
9718 WriteMarsHeaderData(stateObj
, CurrentTransaction
);
9720 if (0 != notificationHeaderSize
) {
9721 // Write Notification header length
9722 WriteInt(notificationHeaderSize
, stateObj
);
9723 // Write notificaiton header data
9724 WriteQueryNotificationHeaderData(notificationRequest
, stateObj
);
9727 if (IncludeTraceHeader
) {
9729 // Write trace header length
9730 WriteInt(traceHeaderSize
, stateObj
);
9731 // Write trace header data
9732 WriteTraceHeaderData(stateObj
);
9738 // Reverse function of GetTokenLength
9740 private void WriteTokenLength(byte token
, int length
, TdsParserStateObject stateObj
) {
9741 int tokenLength
= 0;
9743 Debug
.Assert(token
!= 0, "0 length token!");
9745 // For Plp fields, this should only be used when writing to metadata header.
9746 // For actual data length, WriteDataLength should be used.
9747 // For Xml fields, there is no token length field. For MAX fields it is 0xffff.
9748 if (_isYukon
) { // Handle Yukon specific exceptions
9749 if (TdsEnums
.SQLUDT
== token
) {
9752 else if (token
== TdsEnums
.SQLXMLTYPE
) {
9757 if (tokenLength
== 0) {
9758 switch (token
& TdsEnums
.SQLLenMask
) {
9759 case TdsEnums
.SQLFixedLen
:
9760 Debug
.Assert(length
== 0x01 << ((token
& 0x0c) >> 2), "length does not match encoded length in token");
9764 case TdsEnums
.SQLZeroLen
:
9768 case TdsEnums
.SQLVarLen
:
9769 case TdsEnums
.SQLVarCnt
:
9770 if (0 != (token
& 0x80))
9772 else if (0 == (token
& 0x0c))
9782 Debug
.Assert(false, "Unknown token length!");
9786 switch (tokenLength
) {
9788 stateObj
.WriteByte((byte)length
);
9792 WriteShort(length
, stateObj
);
9796 WriteInt(length
, stateObj
);
9800 // In the metadata case we write 0xffff for partial length prefixed types.
9801 // For actual data length preceding data, WriteDataLength should be used.
9802 WriteShort(TdsEnums
.SQL_USHORTVARMAXLEN
, stateObj
);
9808 // Returns true if BOM byte mark is needed for an XML value
9809 private bool IsBOMNeeded(MetaType type
, object value) {
9810 if (type
.NullableType
== TdsEnums
.SQLXMLTYPE
) {
9811 Type currentType
= value.GetType();
9813 if(currentType
== typeof(SqlString
)) {
9814 if (!((SqlString
)value).IsNull
&& ((((SqlString
)value).Value
).Length
> 0)) {
9815 if ((((SqlString
)value).Value
[0] & 0xff) != 0xff)
9819 else if ((currentType
== typeof(String
)) && (((String
)value).Length
> 0)) {
9820 if ((value != null) && (((String
)value)[0] & 0xff) != 0xff)
9823 else if (currentType
== typeof(SqlXml
)) {
9824 if (!((SqlXml
)value).IsNull
)
9827 else if (currentType
== typeof(XmlDataFeed
)) {
9828 return true; // Values will eventually converted to unicode string here
9834 private Task
GetTerminationTask(Task unterminatedWriteTask
, object value, MetaType type
, int actualLength
, TdsParserStateObject stateObj
, bool isDataFeed
) {
9835 if (type
.IsPlp
&& ((actualLength
> 0) || isDataFeed
)) {
9836 if (unterminatedWriteTask
== null) {
9837 WriteInt(0, stateObj
);
9841 return AsyncHelper
.CreateContinuationTask
<int, TdsParserStateObject
>(unterminatedWriteTask
,
9842 WriteInt
, 0, stateObj
,
9843 connectionToDoom: _connHandler
);
9847 return unterminatedWriteTask
;
9852 private Task
WriteSqlValue(object value, MetaType type
, int actualLength
, int codePageByteSize
, int offset
, TdsParserStateObject stateObj
) {
9853 return GetTerminationTask(
9854 WriteUnterminatedSqlValue(value, type
, actualLength
, codePageByteSize
, offset
, stateObj
),
9855 value, type
, actualLength
, stateObj
, false);
9858 // For MAX types, this method can only write everything in one big chunk. If multiple
9859 // chunk writes needed, please use WritePlpBytes/WritePlpChars
9860 private Task
WriteUnterminatedSqlValue(object value, MetaType type
, int actualLength
, int codePageByteSize
, int offset
, TdsParserStateObject stateObj
) {
9861 Debug
.Assert(((type
.NullableType
== TdsEnums
.SQLXMLTYPE
) ||
9862 (value is INullable
&& !((INullable
)value).IsNull
)),
9863 "unexpected null SqlType!");
9865 // parameters are always sent over as BIG or N types
9866 switch (type
.NullableType
) {
9867 case TdsEnums
.SQLFLTN
:
9868 if (type
.FixedLength
== 4)
9869 WriteFloat(((SqlSingle
)value).Value
, stateObj
);
9871 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
9872 WriteDouble(((SqlDouble
)value).Value
, stateObj
);
9877 case TdsEnums
.SQLBIGBINARY
:
9878 case TdsEnums
.SQLBIGVARBINARY
:
9879 case TdsEnums
.SQLIMAGE
:
9882 WriteInt(actualLength
, stateObj
); // chunk length
9885 if (value is SqlBinary
) {
9886 return stateObj
.WriteByteArray(((SqlBinary
)value).Value
, actualLength
, offset
, canAccumulate
:false);
9889 Debug
.Assert(value is SqlBytes
);
9890 return stateObj
.WriteByteArray(((SqlBytes
)value).Value
, actualLength
, offset
, canAccumulate
:false);
9894 case TdsEnums
.SQLUNIQUEID
:
9896 byte[] b
= ((SqlGuid
)value).ToByteArray();
9898 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
9899 stateObj
.WriteByteArray(b
, actualLength
, 0);
9903 case TdsEnums
.SQLBITN
:
9905 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
9906 if (((SqlBoolean
)value).Value
== true)
9907 stateObj
.WriteByte(1);
9909 stateObj
.WriteByte(0);
9914 case TdsEnums
.SQLINTN
:
9915 if (type
.FixedLength
== 1)
9916 stateObj
.WriteByte(((SqlByte
)value).Value
);
9918 if (type
.FixedLength
== 2)
9919 WriteShort(((SqlInt16
)value).Value
, stateObj
);
9921 if (type
.FixedLength
== 4)
9922 WriteInt(((SqlInt32
)value).Value
, stateObj
);
9924 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
9925 WriteLong(((SqlInt64
)value).Value
, stateObj
);
9930 case TdsEnums
.SQLBIGCHAR
:
9931 case TdsEnums
.SQLBIGVARCHAR
:
9932 case TdsEnums
.SQLTEXT
:
9934 WriteInt(codePageByteSize
, stateObj
); // chunk length
9936 if (value is SqlChars
) {
9937 String sch
= new String(((SqlChars
)value).Value
);
9939 return WriteEncodingChar(sch
, actualLength
, offset
, _defaultEncoding
, stateObj
, canAccumulate
:false);
9942 Debug
.Assert(value is SqlString
);
9943 return WriteEncodingChar(((SqlString
)value).Value
, actualLength
, offset
, _defaultEncoding
, stateObj
, canAccumulate
:false);
9947 case TdsEnums
.SQLNCHAR
:
9948 case TdsEnums
.SQLNVARCHAR
:
9949 case TdsEnums
.SQLNTEXT
:
9950 case TdsEnums
.SQLXMLTYPE
:
9953 if(IsBOMNeeded(type
, value)) {
9954 WriteInt(actualLength
+2, stateObj
); // chunk length
9955 WriteShort(TdsEnums
.XMLUNICODEBOM
, stateObj
);
9957 WriteInt(actualLength
, stateObj
); // chunk length
9961 // convert to cchars instead of cbytes
9962 // Xml type is already converted to string through GetCoercedValue
9963 if (actualLength
!= 0)
9966 if (value is SqlChars
) {
9967 return WriteCharArray(((SqlChars
)value).Value
, actualLength
, offset
, stateObj
, canAccumulate
:false);
9970 Debug
.Assert(value is SqlString
);
9971 return WriteString(((SqlString
)value).Value
, actualLength
, offset
, stateObj
, canAccumulate
:false);
9974 case TdsEnums
.SQLNUMERICN
:
9975 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
9976 WriteSqlDecimal((SqlDecimal
)value, stateObj
);
9979 case TdsEnums
.SQLDATETIMN
:
9980 SqlDateTime dt
= (SqlDateTime
)value;
9982 if (type
.FixedLength
== 4) {
9983 if (0 > dt
.DayTicks
|| dt
.DayTicks
> UInt16
.MaxValue
)
9984 throw SQL
.SmallDateTimeOverflow(dt
.ToString());
9986 WriteShort(dt
.DayTicks
, stateObj
);
9987 WriteShort(dt
.TimeTicks
/ SqlDateTime
.SQLTicksPerMinute
, stateObj
);
9990 WriteInt(dt
.DayTicks
, stateObj
);
9991 WriteInt(dt
.TimeTicks
, stateObj
);
9996 case TdsEnums
.SQLMONEYN
:
9998 WriteSqlMoney((SqlMoney
)value, type
.FixedLength
, stateObj
);
10002 case TdsEnums
.SQLUDT
:
10003 Debug
.Assert(false, "Called WriteSqlValue on UDT param.Should have already been handled");
10004 throw SQL
.UDTUnexpectedResult(value.GetType().AssemblyQualifiedName
);
10007 Debug
.Assert(false, "Unknown TdsType!" + type
.NullableType
.ToString("x2", (IFormatProvider
)null));
10010 // return point for accumualated writes, note: non-accumulated writes returned from their case statements
10014 private class TdsOutputStream
: Stream
{
10016 TdsParserStateObject _stateObj
;
10017 byte[] _preambleToStrip
;
10019 public TdsOutputStream(TdsParser parser
, TdsParserStateObject stateObj
, byte[] preambleToStrip
) {
10021 _stateObj
= stateObj
;
10022 _preambleToStrip
= preambleToStrip
;
10025 public override bool CanRead
{
10026 get { return false; }
10029 public override bool CanSeek
{
10030 get { return false; }
10033 public override bool CanWrite
{
10034 get { return true; }
10037 public override void Flush() {
10041 public override long Length
{
10042 get { throw new NotSupportedException(); }
10045 public override long Position
{
10047 throw new NotSupportedException();
10050 throw new NotSupportedException();
10054 public override int Read(byte[] buffer
, int offset
, int count
) {
10055 throw new NotSupportedException();
10058 public override long Seek(long offset
, SeekOrigin origin
) {
10059 throw new NotSupportedException();
10062 public override void SetLength(long value) {
10063 throw new NotSupportedException();
10066 private void StripPreamble(byte[] buffer
, ref int offset
, ref int count
) {
10067 if (_preambleToStrip
!= null && count
>= _preambleToStrip
.Length
) {
10069 for (int idx
= 0; idx
< _preambleToStrip
.Length
; idx
++) {
10070 if (_preambleToStrip
[idx
] != buffer
[idx
]) {
10071 _preambleToStrip
= null;
10076 offset
+= _preambleToStrip
.Length
;
10077 count
-= _preambleToStrip
.Length
;
10079 _preambleToStrip
= null;
10082 public override void Write(byte[] buffer
, int offset
, int count
) {
10083 Debug
.Assert(!_parser
._asyncWrite
);
10084 ValidateWriteParameters(buffer
, offset
, count
);
10086 StripPreamble(buffer
, ref offset
, ref count
);
10089 _parser
.WriteInt(count
, _stateObj
); // write length of chunk
10090 _stateObj
.WriteByteArray(buffer
, count
, offset
);
10094 public override Task
WriteAsync(byte[] buffer
, int offset
, int count
, CancellationToken cancellationToken
) {
10095 Debug
.Assert(_parser
._asyncWrite
);
10096 ValidateWriteParameters(buffer
, offset
, count
);
10098 StripPreamble(buffer
, ref offset
, ref count
);
10100 RuntimeHelpers
.PrepareConstrainedRegions();
10103 TdsParser
.ReliabilitySection tdsReliabilitySection
= new TdsParser
.ReliabilitySection();
10105 RuntimeHelpers
.PrepareConstrainedRegions();
10107 tdsReliabilitySection
.Start();
10113 _parser
.WriteInt(count
, _stateObj
); // write length of chunk
10114 task
= _stateObj
.WriteByteArray(buffer
, count
, offset
, canAccumulate
: false);
10116 if (task
== null) {
10117 return CompletedTask
;
10125 tdsReliabilitySection
.Stop();
10129 catch (System
.OutOfMemoryException
) {
10130 _parser
._connHandler
.DoomThisConnection();
10133 catch (System
.StackOverflowException
) {
10134 _parser
._connHandler
.DoomThisConnection();
10137 catch (System
.Threading
.ThreadAbortException
) {
10138 _parser
._connHandler
.DoomThisConnection();
10143 internal static void ValidateWriteParameters(byte[] buffer
, int offset
, int count
) {
10144 if (buffer
== null) {
10145 throw ADP
.ArgumentNull(ADP
.ParameterBuffer
);
10148 throw ADP
.ArgumentOutOfRange(ADP
.ParameterOffset
);
10151 throw ADP
.ArgumentOutOfRange(ADP
.ParameterCount
);
10154 if (checked(offset
+ count
) > buffer
.Length
) {
10155 throw ExceptionBuilder
.InvalidOffsetLength();
10158 catch (OverflowException
) {
10159 // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
10160 throw ExceptionBuilder
.InvalidOffsetLength();
10166 private class ConstrainedTextWriter
: TextWriter
{
10171 public ConstrainedTextWriter(TextWriter next
, int size
) {
10177 _size
= int.MaxValue
;
10181 public bool IsComplete
{
10183 return _size
> 0 && _written
>= _size
;
10187 public override Encoding Encoding
{
10188 get { return _next.Encoding; }
10191 public override void Flush() {
10195 public override Task
FlushAsync() {
10196 return _next
.FlushAsync();
10199 public override void Write(char value) {
10200 if (_written
< _size
) {
10201 _next
.Write(value);
10204 Debug
.Assert(_size
< 0 || _written
<= _size
, string.Format("Length of data written exceeds specified length. Written: {0}, specified: {1}", _written
, _size
));
10207 public override void Write(char[] buffer
, int index
, int count
) {
10209 ValidateWriteParameters(buffer
, index
, count
);
10211 Debug
.Assert(_size
>= _written
);
10212 count
= Math
.Min(_size
- _written
, count
);
10214 _next
.Write(buffer
, index
, count
);
10219 public override Task
WriteAsync(char value) {
10221 if (_written
< _size
) {
10223 return _next
.WriteAsync(value);
10226 return CompletedTask
;
10229 public override Task
WriteAsync(char[] buffer
, int index
, int count
) {
10231 ValidateWriteParameters(buffer
, index
, count
);
10233 Debug
.Assert(_size
>= _written
);
10234 count
= Math
.Min(_size
- _written
, count
);
10237 return _next
.WriteAsync(buffer
, index
, count
);
10240 return CompletedTask
;
10243 public override Task
WriteAsync(string value) {
10244 return WriteAsync(value.ToCharArray());
10247 internal static void ValidateWriteParameters(char[] buffer
, int offset
, int count
) {
10248 if (buffer
== null) {
10249 throw ADP
.ArgumentNull(ADP
.ParameterBuffer
);
10252 throw ADP
.ArgumentOutOfRange(ADP
.ParameterOffset
);
10255 throw ADP
.ArgumentOutOfRange(ADP
.ParameterCount
);
10258 if (checked(offset
+ count
) > buffer
.Length
) {
10259 throw ExceptionBuilder
.InvalidOffsetLength();
10262 catch (OverflowException
) {
10263 // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway
10264 throw ExceptionBuilder
.InvalidOffsetLength();
10270 private async Task
WriteXmlFeed(XmlDataFeed feed
, TdsParserStateObject stateObj
, bool needBom
, Encoding encoding
, int size
) {
10271 byte[] preambleToSkip
= null;
10273 preambleToSkip
= encoding
.GetPreamble();
10275 ConstrainedTextWriter writer
= new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj
, preambleToSkip
), encoding
), size
);
10277 XmlWriterSettings writerSettings
= new XmlWriterSettings();
10278 writerSettings
.CloseOutput
= false; // don't close the memory stream
10279 writerSettings
.ConformanceLevel
= ConformanceLevel
.Fragment
;
10281 writerSettings
.Async
= true;
10283 XmlWriter ww
= XmlWriter
.Create(writer
, writerSettings
);
10285 if (feed
._source
.ReadState
== ReadState
.Initial
) {
10286 feed
._source
.Read();
10289 while (!feed
._source
.EOF
&& !writer
.IsComplete
) {
10291 // We are copying nodes from a reader to a writer. This will cause the
10292 // XmlDeclaration to be emitted despite ConformanceLevel.Fragment above.
10293 // Therefore, we filter out the XmlDeclaration while copying.
10294 if (feed
._source
.NodeType
== XmlNodeType
.XmlDeclaration
) {
10295 feed
._source
.Read();
10300 await ww
.WriteNodeAsync(feed
._source
, true).ConfigureAwait(false);
10303 ww
.WriteNode(feed
._source
, true);
10308 await ww
.FlushAsync().ConfigureAwait(false);
10315 private async Task
WriteTextFeed(TextDataFeed feed
, Encoding encoding
, bool needBom
, TdsParserStateObject stateObj
, int size
) {
10316 Debug
.Assert(encoding
== null || !needBom
);
10317 char[] inBuff
= new char[constTextBufferSize
];
10319 encoding
= encoding
?? new UnicodeEncoding(false, false);
10320 ConstrainedTextWriter writer
= new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj
, null), encoding
), size
);
10324 await writer
.WriteAsync((char)TdsEnums
.XMLUNICODEBOM
).ConfigureAwait(false);
10327 writer
.Write((char)TdsEnums
.XMLUNICODEBOM
);
10336 nRead
= await feed
._source
.ReadBlockAsync(inBuff
, 0, constTextBufferSize
).ConfigureAwait(false);
10339 nRead
= feed
._source
.ReadBlock(inBuff
, 0, constTextBufferSize
);
10347 await writer
.WriteAsync(inBuff
, 0, nRead
).ConfigureAwait(false);
10350 writer
.Write(inBuff
, 0, nRead
);
10354 } while (!writer
.IsComplete
);
10357 await writer
.FlushAsync().ConfigureAwait(false);
10364 private async Task
WriteStreamFeed(StreamDataFeed feed
, TdsParserStateObject stateObj
, int len
) {
10365 TdsOutputStream output
= new TdsOutputStream(this, stateObj
, null);
10366 byte[] buff
= new byte[constBinBufferSize
];
10370 int readSize
= constBinBufferSize
;
10371 if (len
> 0 && nWritten
+ readSize
> len
) {
10372 readSize
= len
- nWritten
;
10375 Debug
.Assert(readSize
>= 0);
10378 nRead
= await feed
._source
.ReadAsync(buff
, 0, readSize
).ConfigureAwait(false);
10381 nRead
= feed
._source
.Read(buff
, 0, readSize
);
10389 await output
.WriteAsync(buff
, 0, nRead
).ConfigureAwait(false);
10392 output
.Write(buff
, 0, nRead
);
10396 } while (len
<= 0 || nWritten
< len
);
10399 private Task
NullIfCompletedWriteTask(Task task
) {
10400 if (task
== null) {
10403 switch (task
.Status
) {
10404 case TaskStatus
.RanToCompletion
:
10406 case TaskStatus
.Faulted
:
10407 throw task
.Exception
.InnerException
;
10408 case TaskStatus
.Canceled
:
10409 throw SQL
.OperationCancelled();
10415 private Task
WriteValue(object value, MetaType type
, byte scale
, int actualLength
, int encodingByteSize
, int offset
, TdsParserStateObject stateObj
, int paramSize
, bool isDataFeed
) {
10416 return GetTerminationTask(WriteUnterminatedValue(value, type
, scale
, actualLength
, encodingByteSize
, offset
, stateObj
, paramSize
, isDataFeed
),
10417 value, type
, actualLength
, stateObj
, isDataFeed
);
10420 // For MAX types, this method can only write everything in one big chunk. If multiple
10421 // chunk writes needed, please use WritePlpBytes/WritePlpChars
10422 private Task
WriteUnterminatedValue(object value, MetaType type
, byte scale
, int actualLength
, int encodingByteSize
, int offset
, TdsParserStateObject stateObj
, int paramSize
, bool isDataFeed
)
10424 Debug
.Assert((null != value) && (DBNull
.Value
!= value), "unexpected missing or empty object");
10426 // parameters are always sent over as BIG or N types
10427 switch (type
.NullableType
) {
10428 case TdsEnums
.SQLFLTN
:
10429 if (type
.FixedLength
== 4)
10430 WriteFloat((Single
)value, stateObj
);
10432 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
10433 WriteDouble((Double
)value, stateObj
);
10438 case TdsEnums
.SQLBIGBINARY
:
10439 case TdsEnums
.SQLBIGVARBINARY
:
10440 case TdsEnums
.SQLIMAGE
:
10441 case TdsEnums
.SQLUDT
: {
10442 // An array should be in the object
10443 Debug
.Assert(isDataFeed
|| value is byte[], "Value should be an array of bytes");
10444 Debug
.Assert(!isDataFeed
|| value is StreamDataFeed
, "Value should be a stream");
10447 Debug
.Assert(type
.IsPlp
,"Stream assigned to non-PLP was not converted!");
10448 return NullIfCompletedWriteTask(WriteStreamFeed((StreamDataFeed
)value, stateObj
, paramSize
));
10452 WriteInt(actualLength
, stateObj
); // chunk length
10455 return stateObj
.WriteByteArray((byte[])value, actualLength
, offset
, canAccumulate
: false);
10459 case TdsEnums
.SQLUNIQUEID
: {
10460 System
.Guid guid
= (System
.Guid
)value;
10461 byte[] b
= guid
.ToByteArray();
10463 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
10464 stateObj
.WriteByteArray(b
, actualLength
, 0);
10468 case TdsEnums
.SQLBITN
: {
10469 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
10470 if ((bool)value == true)
10471 stateObj
.WriteByte(1);
10473 stateObj
.WriteByte(0);
10478 case TdsEnums
.SQLINTN
:
10479 if (type
.FixedLength
== 1)
10480 stateObj
.WriteByte((byte)value);
10481 else if (type
.FixedLength
== 2)
10482 WriteShort((Int16
)value, stateObj
);
10483 else if (type
.FixedLength
== 4)
10484 WriteInt((Int32
)value, stateObj
);
10486 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
10487 WriteLong((Int64
)value, stateObj
);
10492 case TdsEnums
.SQLBIGCHAR
:
10493 case TdsEnums
.SQLBIGVARCHAR
:
10494 case TdsEnums
.SQLTEXT
: {
10495 Debug
.Assert(!isDataFeed
|| (value is TextDataFeed
|| value is XmlDataFeed
), "Value must be a TextReader or XmlReader");
10496 Debug
.Assert(isDataFeed
|| (value is string || value is byte[]), "Value is a byte array or string");
10499 Debug
.Assert(type
.IsPlp
, "Stream assigned to non-PLP was not converted!");
10500 TextDataFeed tdf
= value as TextDataFeed
;
10502 return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed
)value, stateObj
, needBom
:true, encoding
:_defaultEncoding
, size
:paramSize
));
10505 return NullIfCompletedWriteTask(WriteTextFeed(tdf
, _defaultEncoding
, false, stateObj
, paramSize
));
10510 WriteInt(encodingByteSize
, stateObj
); // chunk length
10512 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10513 return stateObj
.WriteByteArray((byte[])value, actualLength
, 0, canAccumulate
: false);
10516 return WriteEncodingChar((string)value, actualLength
, offset
, _defaultEncoding
, stateObj
, canAccumulate
: false);
10520 case TdsEnums
.SQLNCHAR
:
10521 case TdsEnums
.SQLNVARCHAR
:
10522 case TdsEnums
.SQLNTEXT
:
10523 case TdsEnums
.SQLXMLTYPE
: {
10524 Debug
.Assert(!isDataFeed
|| (value is TextDataFeed
|| value is XmlDataFeed
), "Value must be a TextReader or XmlReader");
10525 Debug
.Assert(isDataFeed
|| (value is string || value is byte[]), "Value is a byte array or string");
10528 Debug
.Assert(type
.IsPlp
, "Stream assigned to non-PLP was not converted!");
10529 TextDataFeed tdf
= value as TextDataFeed
;
10531 return NullIfCompletedWriteTask(WriteXmlFeed((XmlDataFeed
)value, stateObj
, IsBOMNeeded(type
, value), Encoding
.Unicode
, paramSize
));
10534 return NullIfCompletedWriteTask(WriteTextFeed(tdf
, null, IsBOMNeeded(type
, value), stateObj
, paramSize
));
10539 if (IsBOMNeeded(type
, value)) {
10540 WriteInt(actualLength
+ 2, stateObj
); // chunk length
10541 WriteShort(TdsEnums
.XMLUNICODEBOM
, stateObj
);
10544 WriteInt(actualLength
, stateObj
); // chunk length
10547 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10548 return stateObj
.WriteByteArray((byte[])value, actualLength
, 0, canAccumulate
: false);
10551 // convert to cchars instead of cbytes
10552 actualLength
>>= 1;
10553 return WriteString((string)value, actualLength
, offset
, stateObj
, canAccumulate
: false);
10557 case TdsEnums
.SQLNUMERICN
:
10558 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
10559 WriteDecimal((Decimal
)value, stateObj
);
10562 case TdsEnums
.SQLDATETIMN
:
10563 Debug
.Assert(type
.FixedLength
<= 0xff, "Invalid Fixed Length");
10565 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, (byte)type
.FixedLength
);
10567 if (type
.FixedLength
== 4) {
10568 if (0 > dt
.days
|| dt
.days
> UInt16
.MaxValue
)
10569 throw SQL
.SmallDateTimeOverflow(MetaType
.ToDateTime(dt
.days
, dt
.time
, 4).ToString(CultureInfo
.InvariantCulture
));
10571 WriteShort(dt
.days
, stateObj
);
10572 WriteShort(dt
.time
, stateObj
);
10575 WriteInt(dt
.days
, stateObj
);
10576 WriteInt(dt
.time
, stateObj
);
10581 case TdsEnums
.SQLMONEYN
: {
10582 WriteCurrency((Decimal
)value, type
.FixedLength
, stateObj
);
10586 case TdsEnums
.SQLDATE
: {
10587 WriteDate((DateTime
)value, stateObj
);
10591 case TdsEnums
.SQLTIME
:
10592 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10593 throw SQL
.TimeScaleValueOutOfRange(scale
);
10595 WriteTime((TimeSpan
)value, scale
, actualLength
, stateObj
);
10598 case TdsEnums
.SQLDATETIME2
:
10599 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10600 throw SQL
.TimeScaleValueOutOfRange(scale
);
10602 WriteDateTime2((DateTime
)value, scale
, actualLength
, stateObj
);
10605 case TdsEnums
.SQLDATETIMEOFFSET
:
10606 WriteDateTimeOffset((DateTimeOffset
)value, scale
, actualLength
, stateObj
);
10610 Debug
.Assert(false, "Unknown TdsType!" + type
.NullableType
.ToString("x2", (IFormatProvider
)null));
10613 // return point for accumualated writes, note: non-accumulated writes returned from their case statements
10615 // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture));
10619 /// Write parameter encryption metadata and returns a task if necessary.
10621 private Task
WriteEncryptionMetadata(Task terminatedWriteTask
, SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo
, TdsParserStateObject stateObj
) {
10622 Debug
.Assert(columnEncryptionParameterInfo
!= null, @"columnEncryptionParameterInfo cannot be null");
10623 Debug
.Assert(stateObj
!= null, @"stateObj cannot be null");
10625 // If there is not task already, simply write the encryption metadata synchronously.
10626 if (terminatedWriteTask
== null) {
10627 WriteEncryptionMetadata(columnEncryptionParameterInfo
, stateObj
);
10631 // Otherwise, create a continuation task to write the encryption metadata after the previous write completes.
10632 return AsyncHelper
.CreateContinuationTask
<SqlColumnEncryptionInputParameterInfo
, TdsParserStateObject
>(terminatedWriteTask
,
10633 WriteEncryptionMetadata
, columnEncryptionParameterInfo
, stateObj
,
10634 connectionToDoom: _connHandler
);
10639 /// Write parameter encryption metadata.
10641 private void WriteEncryptionMetadata(SqlColumnEncryptionInputParameterInfo columnEncryptionParameterInfo
, TdsParserStateObject stateObj
) {
10642 Debug
.Assert(columnEncryptionParameterInfo
!= null, @"columnEncryptionParameterInfo cannot be null");
10643 Debug
.Assert(stateObj
!= null, @"stateObj cannot be null");
10645 // Write the TypeInfo.
10646 WriteSmiTypeInfo(columnEncryptionParameterInfo
.ParameterMetadata
, stateObj
);
10648 // Write the serialized array in columnEncryptionParameterInfo.
10649 stateObj
.WriteByteArray(columnEncryptionParameterInfo
.SerializedWireFormat
,
10650 columnEncryptionParameterInfo
.SerializedWireFormat
.Length
,
10654 // For MAX types, this method can only write everything in one big chunk. If multiple
10655 // chunk writes needed, please use WritePlpBytes/WritePlpChars
10656 private byte[] SerializeUnencryptedValue(object value, MetaType type
, byte scale
, int actualLength
, int offset
, bool isDataFeed
, byte normalizationVersion
, TdsParserStateObject stateObj
) {
10657 Debug
.Assert((null != value) && (DBNull
.Value
!= value), "unexpected missing or empty object");
10659 if (normalizationVersion
!= 0x01) {
10660 throw SQL
.UnsupportedNormalizationVersion(normalizationVersion
);
10663 // parameters are always sent over as BIG or N types
10664 switch (type
.NullableType
) {
10665 case TdsEnums
.SQLFLTN
:
10666 if (type
.FixedLength
== 4)
10667 return SerializeFloat((Single
)value);
10669 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
10670 return SerializeDouble((Double
)value);
10673 case TdsEnums
.SQLBIGBINARY
:
10674 case TdsEnums
.SQLBIGVARBINARY
:
10675 case TdsEnums
.SQLIMAGE
:
10676 case TdsEnums
.SQLUDT
: {
10677 Debug
.Assert(!isDataFeed
, "We cannot seriliaze streams");
10678 Debug
.Assert(value is byte[], "Value should be an array of bytes");
10680 byte[] b
= new byte[actualLength
];
10681 Buffer
.BlockCopy((byte[])value, offset
, b
, 0, actualLength
);
10685 case TdsEnums
.SQLUNIQUEID
: {
10686 System
.Guid guid
= (System
.Guid
)value;
10687 byte[] b
= guid
.ToByteArray();
10689 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
10693 case TdsEnums
.SQLBITN
: {
10694 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
10696 // We normalize to allow conversion across data types. BIT is serialized into a BIGINT.
10697 return SerializeLong((bool)value == true ? 1 : 0, stateObj
);
10700 case TdsEnums
.SQLINTN
:
10701 if (type
.FixedLength
== 1)
10702 return SerializeLong((byte)value, stateObj
);
10704 if (type
.FixedLength
== 2)
10705 return SerializeLong((Int16
)value, stateObj
);
10707 if (type
.FixedLength
== 4)
10708 return SerializeLong((Int32
)value, stateObj
);
10710 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
10711 return SerializeLong((Int64
)value, stateObj
);
10713 case TdsEnums
.SQLBIGCHAR
:
10714 case TdsEnums
.SQLBIGVARCHAR
:
10715 case TdsEnums
.SQLTEXT
: {
10716 Debug
.Assert(!isDataFeed
, "We cannot seriliaze streams");
10717 Debug
.Assert((value is string || value is byte[]), "Value is a byte array or string");
10719 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10720 byte[] b
= new byte[actualLength
];
10721 Buffer
.BlockCopy((byte[])value, 0, b
, 0, actualLength
);
10725 return SerializeEncodingChar((string)value, actualLength
, offset
, _defaultEncoding
);
10728 case TdsEnums
.SQLNCHAR
:
10729 case TdsEnums
.SQLNVARCHAR
:
10730 case TdsEnums
.SQLNTEXT
:
10731 case TdsEnums
.SQLXMLTYPE
: {
10732 Debug
.Assert(!isDataFeed
, "We cannot seriliaze streams");
10733 Debug
.Assert((value is string || value is byte[]), "Value is a byte array or string");
10735 if (value is byte[]) { // If LazyMat non-filled blob, send cookie rather than value
10736 byte[] b
= new byte[actualLength
];
10737 Buffer
.BlockCopy((byte[])value, 0, b
, 0, actualLength
);
10740 else { // convert to cchars instead of cbytes
10741 actualLength
>>= 1;
10742 return SerializeString((string)value, actualLength
, offset
);
10745 case TdsEnums
.SQLNUMERICN
:
10746 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
10747 return SerializeDecimal((Decimal
)value, stateObj
);
10749 case TdsEnums
.SQLDATETIMN
:
10750 Debug
.Assert(type
.FixedLength
<= 0xff, "Invalid Fixed Length");
10752 TdsDateTime dt
= MetaType
.FromDateTime((DateTime
)value, (byte)type
.FixedLength
);
10754 if (type
.FixedLength
== 4) {
10755 if (0 > dt
.days
|| dt
.days
> UInt16
.MaxValue
)
10756 throw SQL
.SmallDateTimeOverflow(MetaType
.ToDateTime(dt
.days
, dt
.time
, 4).ToString(CultureInfo
.InvariantCulture
));
10758 if (null == stateObj
._bIntBytes
) {
10759 stateObj
._bIntBytes
= new byte[4];
10762 byte[] b
= stateObj
._bIntBytes
;
10765 byte[] bPart
= SerializeShort(dt
.days
, stateObj
);
10766 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10769 bPart
= SerializeShort(dt
.time
, stateObj
);
10770 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10775 if (null == stateObj
._bLongBytes
) {
10776 stateObj
._bLongBytes
= new byte[8];
10778 byte[] b
= stateObj
._bLongBytes
;
10781 byte[] bPart
= SerializeInt(dt
.days
, stateObj
);
10782 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10785 bPart
= SerializeInt(dt
.time
, stateObj
);
10786 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10791 case TdsEnums
.SQLMONEYN
: {
10792 return SerializeCurrency((Decimal
)value, type
.FixedLength
, stateObj
);
10795 case TdsEnums
.SQLDATE
: {
10796 return SerializeDate((DateTime
)value);
10799 case TdsEnums
.SQLTIME
:
10800 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10801 throw SQL
.TimeScaleValueOutOfRange(scale
);
10803 return SerializeTime((TimeSpan
)value, scale
, actualLength
);
10805 case TdsEnums
.SQLDATETIME2
:
10806 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10807 throw SQL
.TimeScaleValueOutOfRange(scale
);
10809 return SerializeDateTime2((DateTime
)value, scale
, actualLength
);
10811 case TdsEnums
.SQLDATETIMEOFFSET
:
10812 if (scale
> TdsEnums
.DEFAULT_VARTIME_SCALE
) {
10813 throw SQL
.TimeScaleValueOutOfRange(scale
);
10815 return SerializeDateTimeOffset((DateTimeOffset
)value, scale
, actualLength
);
10818 throw SQL
.UnsupportedDatatypeEncryption(type
.TypeName
);
10820 // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture));
10823 // For MAX types, this method can only write everything in one big chunk. If multiple
10824 // chunk writes needed, please use WritePlpBytes/WritePlpChars
10825 private byte[] SerializeUnencryptedSqlValue(object value, MetaType type
, int actualLength
, int offset
, byte normalizationVersion
, TdsParserStateObject stateObj
) {
10826 Debug
.Assert(((type
.NullableType
== TdsEnums
.SQLXMLTYPE
) ||
10827 (value is INullable
&& !((INullable
)value).IsNull
)),
10828 "unexpected null SqlType!");
10830 if (normalizationVersion
!= 0x01) {
10831 throw SQL
.UnsupportedNormalizationVersion(normalizationVersion
);
10834 // parameters are always sent over as BIG or N types
10835 switch (type
.NullableType
) {
10836 case TdsEnums
.SQLFLTN
:
10837 if (type
.FixedLength
== 4)
10838 return SerializeFloat(((SqlSingle
)value).Value
);
10840 Debug
.Assert(type
.FixedLength
== 8, "Invalid length for SqlDouble type!");
10841 return SerializeDouble(((SqlDouble
)value).Value
);
10844 case TdsEnums
.SQLBIGBINARY
:
10845 case TdsEnums
.SQLBIGVARBINARY
:
10846 case TdsEnums
.SQLIMAGE
: {
10847 byte[] b
= new byte[actualLength
];
10849 if (value is SqlBinary
) {
10850 Buffer
.BlockCopy(((SqlBinary
)value).Value
, offset
, b
, 0, actualLength
);
10853 Debug
.Assert(value is SqlBytes
);
10854 Buffer
.BlockCopy(((SqlBytes
)value).Value
, offset
, b
, 0, actualLength
);
10859 case TdsEnums
.SQLUNIQUEID
: {
10860 byte[] b
= ((SqlGuid
)value).ToByteArray();
10862 Debug
.Assert((actualLength
== b
.Length
) && (actualLength
== 16), "Invalid length for guid type in com+ object");
10866 case TdsEnums
.SQLBITN
: {
10867 Debug
.Assert(type
.FixedLength
== 1, "Invalid length for SqlBoolean type");
10869 // We normalize to allow conversion across data types. BIT is serialized into a BIGINT.
10870 return SerializeLong(((SqlBoolean
)value).Value
== true ? 1 : 0, stateObj
);
10873 case TdsEnums
.SQLINTN
:
10874 // We normalize to allow conversion across data types. All data types below are serialized into a BIGINT.
10875 if (type
.FixedLength
== 1)
10876 return SerializeLong(((SqlByte
)value).Value
, stateObj
);
10878 if (type
.FixedLength
== 2)
10879 return SerializeLong(((SqlInt16
)value).Value
, stateObj
);
10881 if (type
.FixedLength
== 4)
10882 return SerializeLong(((SqlInt32
)value).Value
, stateObj
);
10884 Debug
.Assert(type
.FixedLength
== 8, "invalid length for SqlIntN type: " + type
.FixedLength
.ToString(CultureInfo
.InvariantCulture
));
10885 return SerializeLong(((SqlInt64
)value).Value
, stateObj
);
10888 case TdsEnums
.SQLBIGCHAR
:
10889 case TdsEnums
.SQLBIGVARCHAR
:
10890 case TdsEnums
.SQLTEXT
:
10891 if (value is SqlChars
) {
10892 String sch
= new String(((SqlChars
)value).Value
);
10893 return SerializeEncodingChar(sch
, actualLength
, offset
, _defaultEncoding
);
10896 Debug
.Assert(value is SqlString
);
10897 return SerializeEncodingChar(((SqlString
)value).Value
, actualLength
, offset
, _defaultEncoding
);
10901 case TdsEnums
.SQLNCHAR
:
10902 case TdsEnums
.SQLNVARCHAR
:
10903 case TdsEnums
.SQLNTEXT
:
10904 case TdsEnums
.SQLXMLTYPE
:
10905 // convert to cchars instead of cbytes
10906 // Xml type is already converted to string through GetCoercedValue
10907 if (actualLength
!= 0)
10908 actualLength
>>= 1;
10910 if (value is SqlChars
) {
10911 return SerializeCharArray(((SqlChars
)value).Value
, actualLength
, offset
);
10914 Debug
.Assert(value is SqlString
);
10915 return SerializeString(((SqlString
)value).Value
, actualLength
, offset
);
10918 case TdsEnums
.SQLNUMERICN
:
10919 Debug
.Assert(type
.FixedLength
<= 17, "Decimal length cannot be greater than 17 bytes");
10920 return SerializeSqlDecimal((SqlDecimal
)value, stateObj
);
10922 case TdsEnums
.SQLDATETIMN
:
10923 SqlDateTime dt
= (SqlDateTime
)value;
10925 if (type
.FixedLength
== 4) {
10926 if (0 > dt
.DayTicks
|| dt
.DayTicks
> UInt16
.MaxValue
)
10927 throw SQL
.SmallDateTimeOverflow(dt
.ToString());
10929 if (null == stateObj
._bIntBytes
) {
10930 stateObj
._bIntBytes
= new byte[4];
10933 byte[] b
= stateObj
._bIntBytes
;
10936 byte[] bPart
= SerializeShort(dt
.DayTicks
, stateObj
);
10937 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10940 bPart
= SerializeShort(dt
.TimeTicks
/ SqlDateTime
.SQLTicksPerMinute
, stateObj
);
10941 Buffer
.BlockCopy(bPart
, 0, b
, current
, 2);
10946 if (null == stateObj
._bLongBytes
) {
10947 stateObj
._bLongBytes
= new byte[8];
10950 byte[] b
= stateObj
._bLongBytes
;
10953 byte[] bPart
= SerializeInt(dt
.DayTicks
, stateObj
);
10954 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10957 bPart
= SerializeInt(dt
.TimeTicks
, stateObj
);
10958 Buffer
.BlockCopy(bPart
, 0, b
, current
, 4);
10963 case TdsEnums
.SQLMONEYN
: {
10964 return SerializeSqlMoney((SqlMoney
)value, type
.FixedLength
, stateObj
);
10968 throw SQL
.UnsupportedDatatypeEncryption(type
.TypeName
);
10973 // we always send over nullable types for parameters so we always write the varlen fields
10976 internal void WriteParameterVarLen(MetaType type
, int size
, bool isNull
, TdsParserStateObject stateObj
, bool unknownLength
=false) {
10977 if (type
.IsLong
) { // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths
10980 WriteLong(unchecked((long)TdsEnums
.SQL_PLP_NULL
), stateObj
);
10983 WriteInt(unchecked((int)TdsEnums
.VARLONGNULL
), stateObj
);
10986 else if (type
.NullableType
== TdsEnums
.SQLXMLTYPE
|| unknownLength
) {
10987 WriteUnsignedLong(TdsEnums
.SQL_PLP_UNKNOWNLEN
, stateObj
);
10989 else if (type
.IsPlp
) {
10990 // Non-xml plp types
10991 WriteLong((long)size
, stateObj
);
10994 WriteInt(size
, stateObj
);
10997 else if (type
.IsVarTime
) {
10999 stateObj
.WriteByte(TdsEnums
.FIXEDNULL
);
11002 stateObj
.WriteByte((byte)size
);
11005 else if (false == type
.IsFixed
) { // non-long but variable length column, must be a BIG* type: 2 byte length
11007 WriteShort(TdsEnums
.VARNULL
, stateObj
);
11010 WriteShort(size
, stateObj
);
11015 stateObj
.WriteByte(TdsEnums
.FIXEDNULL
);
11018 Debug
.Assert(type
.FixedLength
<= 0xff, "WriteParameterVarLen: invalid one byte length!");
11019 stateObj
.WriteByte((byte)(type
.FixedLength
& 0xff)); // 1 byte for everything else
11024 // Reads the next chunk in a nvarchar(max) data stream.
11025 // This call must be preceeded by a call to ReadPlpLength or ReadDataLength.
11026 // Will not start reading into the next chunk if bytes requested is larger than
11027 // the current chunk length. Do another ReadPlpLength, ReadPlpUnicodeChars in that case.
11028 // Returns the actual chars read
11029 private bool TryReadPlpUnicodeCharsChunk(char[] buff
, int offst
, int len
, TdsParserStateObject stateObj
, out int charsRead
) {
11031 Debug
.Assert((buff
== null && len
== 0) || (buff
.Length
>= offst
+ len
), "Invalid length sent to ReadPlpUnicodeChars()!");
11032 Debug
.Assert((stateObj
._longlen
!= 0) && (stateObj
._longlen
!= TdsEnums
.SQL_PLP_NULL
),
11033 "Out of sync plp read request");
11034 if (stateObj
._longlenleft
== 0) {
11035 Debug
.Assert(false, "Out of sync read request");
11042 // stateObj._longlenleft is in bytes
11043 if ((stateObj
._longlenleft
>> 1) < (ulong)len
)
11044 charsRead
= (int)(stateObj
._longlenleft
>> 1);
11046 for (int ii
= 0; ii
< charsRead
; ii
++) {
11047 if (!stateObj
.TryReadChar(out buff
[offst
+ ii
])) {
11052 stateObj
._longlenleft
-= ((ulong)charsRead
<< 1);
11056 internal int ReadPlpUnicodeChars(ref char[] buff
, int offst
, int len
, TdsParserStateObject stateObj
) {
11058 Debug
.Assert(stateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
11059 bool result
= TryReadPlpUnicodeChars(ref buff
, offst
, len
, stateObj
, out charsRead
);
11060 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
11064 // Reads the requested number of chars from a plp data stream, or the entire data if
11065 // requested length is -1 or larger than the actual length of data. First call to this method
11066 // should be preceeded by a call to ReadPlpLength or ReadDataLength.
11067 // Returns the actual chars read.
11068 internal bool TryReadPlpUnicodeChars(ref char[] buff
, int offst
, int len
, TdsParserStateObject stateObj
, out int totalCharsRead
) {
11073 if (stateObj
._longlen
== 0) {
11074 Debug
.Assert(stateObj
._longlenleft
== 0);
11075 totalCharsRead
= 0;
11076 return true; // No data
11079 Debug
.Assert(((ulong)stateObj
._longlen
!= TdsEnums
.SQL_PLP_NULL
),
11080 "Out of sync plp read request");
11082 Debug
.Assert((buff
== null && offst
== 0) || (buff
.Length
>= offst
+ len
), "Invalid length sent to ReadPlpUnicodeChars()!");
11085 // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time
11086 if (buff
== null && stateObj
._longlen
!= TdsEnums
.SQL_PLP_UNKNOWNLEN
) {
11087 buff
= new char[(int)Math
.Min((int)stateObj
._longlen
, len
)];
11090 if (stateObj
._longlenleft
== 0) {
11092 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11093 totalCharsRead
= 0;
11096 if (stateObj
._longlenleft
== 0) { // Data read complete
11097 totalCharsRead
= 0;
11102 totalCharsRead
= 0;
11104 while (charsLeft
> 0) {
11105 charsRead
= (int)Math
.Min((stateObj
._longlenleft
+ 1) >> 1, (ulong)charsLeft
);
11106 if ((buff
== null) || (buff
.Length
< (offst
+ charsRead
))) {
11108 newbuf
= new char[offst
+ charsRead
];
11109 if (buff
!= null) {
11110 Buffer
.BlockCopy(buff
, 0, newbuf
, 0, offst
*2);
11114 if (charsRead
> 0) {
11115 if (!TryReadPlpUnicodeCharsChunk(buff
, offst
, charsRead
, stateObj
, out charsRead
)) {
11118 charsLeft
-= charsRead
;
11119 offst
+= charsRead
;
11120 totalCharsRead
+= charsRead
;
11122 // Special case single byte left
11123 if (stateObj
._longlenleft
== 1 && (charsLeft
> 0)) {
11125 if (!stateObj
.TryReadByte(out b1
)) {
11128 stateObj
._longlenleft
--;
11130 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11133 Debug
.Assert((stateObj
._longlenleft
!= 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
11135 if (!stateObj
.TryReadByte(out b2
)) {
11138 stateObj
._longlenleft
--;
11139 // Put it at the end of the array. At this point we know we have an extra byte.
11140 buff
[offst
] = (char)(((b2
& 0xff) << 8) + (b1
& 0xff));
11141 offst
= checked((int)offst
+ 1);
11146 if (stateObj
._longlenleft
== 0) { // Read the next chunk or cleanup state if hit the end
11148 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11153 if (stateObj
._longlenleft
== 0) // Data read complete
11159 internal int ReadPlpAnsiChars(ref char[] buff
, int offst
, int len
, SqlMetaDataPriv metadata
, TdsParserStateObject stateObj
) {
11163 int totalcharsRead
= 0;
11165 if (stateObj
._longlen
== 0) {
11166 Debug
.Assert(stateObj
._longlenleft
== 0);
11167 return 0; // No data
11170 Debug
.Assert(((ulong)stateObj
._longlen
!= TdsEnums
.SQL_PLP_NULL
),
11171 "Out of sync plp read request");
11173 Debug
.Assert((buff
== null && offst
== 0) || (buff
.Length
>= offst
+ len
), "Invalid length sent to ReadPlpAnsiChars()!");
11176 if (stateObj
._longlenleft
== 0) {
11177 stateObj
.ReadPlpLength(false);
11178 if (stateObj
._longlenleft
== 0) {// Data read complete
11179 stateObj
._plpdecoder
= null;
11184 if (stateObj
._plpdecoder
== null) {
11185 Encoding enc
= metadata
.encoding
;
11189 if (null == _defaultEncoding
) {
11190 ThrowUnsupportedCollationEncountered(stateObj
);
11194 enc
= _defaultEncoding
;
11196 stateObj
._plpdecoder
= enc
.GetDecoder();
11199 while (charsLeft
> 0) {
11200 bytesRead
= (int)Math
.Min(stateObj
._longlenleft
, (ulong)charsLeft
);
11201 if ((stateObj
._bTmp
== null) || (stateObj
._bTmp
.Length
< bytesRead
)) {
11203 stateObj
._bTmp
= new byte[bytesRead
];
11206 bytesRead
= stateObj
.ReadPlpBytesChunk(stateObj
._bTmp
, 0, bytesRead
);
11208 charsRead
= stateObj
._plpdecoder
.GetChars(stateObj
._bTmp
, 0, bytesRead
, buff
, offst
);
11209 charsLeft
-= charsRead
;
11210 offst
+= charsRead
;
11211 totalcharsRead
+= charsRead
;
11212 if (stateObj
._longlenleft
== 0) // Read the next chunk or cleanup state if hit the end
11213 stateObj
.ReadPlpLength(false);
11215 if (stateObj
._longlenleft
== 0) { // Data read complete
11216 stateObj
._plpdecoder
= null;
11220 return (totalcharsRead
);
11223 // ensure value is not null and does not have an NBC bit set for it before using this method
11224 internal ulong SkipPlpValue(ulong cb
, TdsParserStateObject stateObj
) {
11226 Debug
.Assert(stateObj
._syncOverAsync
, "Should not attempt pends in a synchronous call");
11227 bool result
= TrySkipPlpValue(cb
, stateObj
, out skipped
);
11228 if (!result
) { throw SQL.SynchronousCallMayNotPend(); }
11232 internal bool TrySkipPlpValue(ulong cb
, TdsParserStateObject stateObj
, out ulong totalBytesSkipped
) {
11233 // Read and skip cb bytes or until ReadPlpLength returns 0.
11235 totalBytesSkipped
= 0;
11237 if (stateObj
._longlenleft
== 0) {
11239 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11244 while ((totalBytesSkipped
< cb
) &&
11245 (stateObj
._longlenleft
> 0)) {
11246 if (stateObj
._longlenleft
> Int32
.MaxValue
)
11247 bytesSkipped
= Int32
.MaxValue
;
11249 bytesSkipped
= (int)stateObj
._longlenleft
;
11250 bytesSkipped
= ((cb
- totalBytesSkipped
) < (ulong) bytesSkipped
) ? (int)(cb
- totalBytesSkipped
) : bytesSkipped
;
11252 if (!stateObj
.TrySkipBytes(bytesSkipped
)) {
11255 stateObj
._longlenleft
-= (ulong)bytesSkipped
;
11256 totalBytesSkipped
+= (ulong)bytesSkipped
;
11258 if (stateObj
._longlenleft
== 0) {
11260 if (!stateObj
.TryReadPlpLength(false, out ignored
)) {
11269 internal ulong PlpBytesLeft(TdsParserStateObject stateObj
) {
11270 if ((stateObj
._longlen
!= 0) && (stateObj
._longlenleft
== 0))
11271 stateObj
.ReadPlpLength(false);
11273 return stateObj
._longlenleft
;
11276 internal bool TryPlpBytesLeft(TdsParserStateObject stateObj
, out ulong left
) {
11277 if ((stateObj
._longlen
!= 0) && (stateObj
._longlenleft
== 0)) {
11278 if (!stateObj
.TryReadPlpLength(false, out left
)) {
11283 left
= stateObj
._longlenleft
;
11287 private const ulong _indeterminateSize
= 0xffffffffffffffff; // Represents unknown size
11289 internal ulong PlpBytesTotalLength(TdsParserStateObject stateObj
) {
11290 if (stateObj
._longlen
== TdsEnums
.SQL_PLP_UNKNOWNLEN
)
11291 return _indeterminateSize
;
11292 else if (stateObj
._longlen
== TdsEnums
.SQL_PLP_NULL
)
11295 return stateObj
._longlen
;
11298 const string StateTraceFormatString
= "\n\t"
11299 + " _physicalStateObj = {0}\n\t"
11300 + " _pMarsPhysicalConObj = {1}\n\t"
11301 + " _state = {2}\n\t"
11302 + " _server = {3}\n\t"
11303 + " _fResetConnection = {4}\n\t"
11304 + " _defaultCollation = {5}\n\t"
11305 + " _defaultCodePage = {6}\n\t"
11306 + " _defaultLCID = {7}\n\t"
11307 + " _defaultEncoding = {8}\n\t"
11308 + " _encryptionOption = {10}\n\t"
11309 + " _currentTransaction = {11}\n\t"
11310 + " _pendingTransaction = {12}\n\t"
11311 + " _retainedTransactionId = {13}\n\t"
11312 + " _nonTransactedOpenResultCount = {14}\n\t"
11313 + " _connHandler = {15}\n\t"
11314 + " _fMARS = {16}\n\t"
11315 + " _sessionPool = {17}\n\t"
11316 + " _isShiloh = {18}\n\t"
11317 + " _isShilohSP1 = {19}\n\t"
11318 + " _isYukon = {20}\n\t"
11319 + " _sniSpnBuffer = {21}\n\t"
11320 + " _errors = {22}\n\t"
11321 + " _warnings = {23}\n\t"
11322 + " _attentionErrors = {24}\n\t"
11323 + " _attentionWarnings = {25}\n\t"
11324 + " _statistics = {26}\n\t"
11325 + " _statisticsIsInTransaction = {27}\n\t"
11326 + " _fPreserveTransaction = {28}"
11327 + " _fParallel = {29}"
11329 internal string TraceString() {
11330 return String
.Format(/*IFormatProvider*/ null,
11331 StateTraceFormatString
,
11332 null == _physicalStateObj
,
11333 null == _pMarsPhysicalConObj
,
11337 null == _defaultCollation
? "(null)" : _defaultCollation
.TraceString(),
11340 TraceObjectClass(_defaultEncoding
),
11343 null == _currentTransaction
? "(null)" : _currentTransaction
.TraceString(),
11344 null == _pendingTransaction
? "(null)" : _pendingTransaction
.TraceString(),
11345 _retainedTransactionId
,
11346 _nonTransactedOpenResultCount
,
11347 null == _connHandler
? "(null)" : _connHandler
.ObjectID
.ToString((IFormatProvider
)null),
11349 null == _sessionPool
? "(null)" : _sessionPool
.TraceString(),
11353 null == _sniSpnBuffer
? "(null)" : _sniSpnBuffer
.Length
.ToString((IFormatProvider
)null),
11354 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.ErrorCount
.ToString((IFormatProvider
)null),
11355 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.WarningCount
.ToString((IFormatProvider
)null),
11356 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.PreAttentionErrorCount
.ToString((IFormatProvider
)null),
11357 _physicalStateObj
!= null ? "(null)" : _physicalStateObj
.PreAttentionWarningCount
.ToString((IFormatProvider
)null),
11358 null == _statistics
,
11359 _statisticsIsInTransaction
,
11360 _fPreserveTransaction
,
11361 null == _connHandler
? "(null)" : _connHandler
.ConnectionOptions
.MultiSubnetFailover
.ToString((IFormatProvider
)null),
11362 null == _connHandler
? "(null)" : _connHandler
.ConnectionOptions
.TransparentNetworkIPResolution
.ToString((IFormatProvider
)null));
11365 private string TraceObjectClass(object instance
) {
11366 if (null == instance
) {
11370 return instance
.GetType().ToString();