Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlCommand.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlCommand.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.SqlClient {
10     using System;
11     using System.Collections;
12     using System.Collections.Generic;
13     using System.Collections.ObjectModel;
14     using System.ComponentModel;
15     using System.Configuration.Assemblies;
16     using System.Data;
17     using System.Data.Common;
18     using System.Data.ProviderBase;
19     using System.Data.Sql;
20     using System.Data.SqlTypes;
21     using System.Diagnostics;
22     using System.Diagnostics.Tracing;
23     using System.Globalization;
24     using System.IO;
25     using System.Linq;
26     using System.Reflection;
27     using System.Runtime.CompilerServices;
28     using System.Runtime.ConstrainedExecution;
29     using System.Runtime.Serialization.Formatters;
30     using System.Security.Permissions;
31     using System.Text;
32     using System.Threading;
33     using System.Threading.Tasks;
34     using SysTx = System.Transactions;
35     using System.Xml;
36
37     using Microsoft.SqlServer.Server;
38
39     [
40     DefaultEvent("RecordsAffected"),
41     ToolboxItem(true),
42     Designer("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner)
43     ]
44     public sealed class SqlCommand : DbCommand, ICloneable {
45
46         private  static int     _objectTypeCount; // Bid counter
47         internal readonly int   ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
48
49         private string          _commandText;
50         private CommandType     _commandType;
51         private int             _commandTimeout = ADP.DefaultCommandTimeout;
52         private UpdateRowSource _updatedRowSource = UpdateRowSource.Both;
53         private bool            _designTimeInvisible;
54
55         /// <summary>
56         /// Indicates if the column encryption setting was set at-least once in the batch rpc mode, when using AddBatchCommand.
57         /// </summary>
58         private bool            _wasBatchModeColumnEncryptionSettingSetOnce;
59
60         /// <summary>
61         /// Column Encryption Override. Defaults to SqlConnectionSetting, in which case
62         /// it will be Enabled if SqlConnectionOptions.IsColumnEncryptionSettingEnabled = true, Disabled if false.
63         /// This may also be used to set other behavior which overrides connection level setting.
64         /// </summary>
65         private SqlCommandColumnEncryptionSetting _columnEncryptionSetting = SqlCommandColumnEncryptionSetting.UseConnectionSetting;
66
67         internal SqlDependency  _sqlDep;
68
69 #if DEBUG
70         /// <summary>
71         /// Force the client to sleep during sp_describe_parameter_encryption in the function TryFetchInputParameterEncryptionInfo.
72         /// </summary>
73         private static bool _sleepDuringTryFetchInputParameterEncryptionInfo = false;
74
75         /// <summary>
76         /// Force the client to sleep during sp_describe_parameter_encryption in the function RunExecuteReaderTds.
77         /// </summary>
78         private static bool _sleepDuringRunExecuteReaderTdsForSpDescribeParameterEncryption = false;
79
80         /// <summary>
81         /// Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults.
82         /// </summary>
83         private static bool _sleepAfterReadDescribeEncryptionParameterResults = false;
84
85         /// <summary>
86         /// Internal flag for testing purposes that forces all queries to internally end async calls.
87         /// </summary>
88         private static bool _forceInternalEndQuery = false;
89 #endif 
90
91         // devnote: Prepare
92         // Against 7.0 Server (Sphinx) a prepare/unprepare requires an extra roundtrip to the server.
93         //
94         // From 8.0 (Shiloh) and above (Yukon) the preparation can be done as part of the command execution.
95         //
96         private enum EXECTYPE {
97             UNPREPARED,         // execute unprepared commands, all server versions (results in sp_execsql call)
98             PREPAREPENDING,     // prepare and execute command, 8.0 and above only  (results in sp_prepexec call)
99             PREPARED,           // execute prepared commands, all server versions   (results in sp_exec call)
100         }
101
102         // devnotes
103         //
104         // _hiddenPrepare
105         // On 8.0 and above the Prepared state cannot be left. Once a command is prepared it will always be prepared.
106         // A change in parameters, commandtext etc (IsDirty) automatically causes a hidden prepare
107         //
108         // _inPrepare will be set immediately before the actual prepare is done.
109         // The OnReturnValue function will test this flag to determine whether the returned value is a _prepareHandle or something else.
110         //
111         // _prepareHandle - the handle of a prepared command. Apparently there can be multiple prepared commands at a time - a feature that we do not support yet.
112
113         private bool _inPrepare         = false;
114         private int  _prepareHandle     = -1;
115         private bool _hiddenPrepare     = false;
116         private int _preparedConnectionCloseCount = -1;
117         private int _preparedConnectionReconnectCount = -1;
118
119         private SqlParameterCollection _parameters;
120         private SqlConnection          _activeConnection;
121         private bool                   _dirty            = false;               // true if the user changes the commandtext or number of parameters after the command is already prepared
122         private EXECTYPE               _execType         = EXECTYPE.UNPREPARED; // by default, assume the user is not sharing a connection so the command has not been prepared
123         private _SqlRPC[]              _rpcArrayOf1      = null;                // Used for RPC executes
124         private _SqlRPC                _rpcForEncryption = null;                // Used for sp_describe_parameter_encryption RPC executes
125
126         // cut down on object creation and cache all these
127         // cached metadata
128         private _SqlMetaDataSet _cachedMetaData;
129         
130         // Last TaskCompletionSource for reconnect task - use for cancellation only
131         TaskCompletionSource<object> _reconnectionCompletionSource = null;
132
133 #if DEBUG
134         static internal int DebugForceAsyncWriteDelay { get; set; }
135 #endif
136         internal bool InPrepare {
137             get {
138                 return _inPrepare;
139             }
140         }
141
142         /// <summary>
143         /// Return if column encryption setting is enabled.
144         /// The order in the below if is important since _activeConnection.Parser can throw if the 
145         /// underlying tds connection is closed and we don't want to change the behavior for folks
146         /// not trying to use transparent parameter encryption i.e. who don't use (SqlCommandColumnEncryptionSetting.Enabled or _activeConnection.IsColumnEncryptionSettingEnabled) here.
147         /// </summary>
148         internal bool IsColumnEncryptionEnabled {
149             get {
150                 return (_columnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled
151                                  || (_columnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && _activeConnection.IsColumnEncryptionSettingEnabled))
152                                  && _activeConnection.Parser != null
153                                  && _activeConnection.Parser.IsColumnEncryptionSupported;
154             }
155         }
156
157         // Cached info for async executions
158         private class CachedAsyncState {
159             private int           _cachedAsyncCloseCount = -1;    // value of the connection's CloseCount property when the asyncResult was set; tracks when connections are closed after an async operation
160             private TaskCompletionSource<object> _cachedAsyncResult     = null;
161             private SqlConnection _cachedAsyncConnection = null;  // Used to validate that the connection hasn't changed when end the connection;
162             private SqlDataReader _cachedAsyncReader     = null;
163             private RunBehavior   _cachedRunBehavior     = RunBehavior.ReturnImmediately;
164             private string        _cachedSetOptions      = null;
165             private string        _cachedEndMethod       = null;
166
167             internal CachedAsyncState () {
168             }
169
170             internal SqlDataReader CachedAsyncReader {
171                 get {return _cachedAsyncReader;}
172             }
173             internal  RunBehavior CachedRunBehavior {
174                 get {return _cachedRunBehavior;}
175             }
176             internal  string CachedSetOptions {
177                 get {return _cachedSetOptions;}
178             }
179             internal bool PendingAsyncOperation {
180                 get {return (null != _cachedAsyncResult);}
181             }
182             internal string EndMethodName { 
183                 get { return _cachedEndMethod; } 
184             }
185
186             internal bool IsActiveConnectionValid(SqlConnection activeConnection) {
187                 return (_cachedAsyncConnection == activeConnection && _cachedAsyncCloseCount == activeConnection.CloseCount);
188             }
189
190             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
191             internal void ResetAsyncState() {
192                 _cachedAsyncCloseCount = -1;
193                 _cachedAsyncResult     = null;
194                 if (_cachedAsyncConnection != null) {
195                     _cachedAsyncConnection.AsyncCommandInProgress = false;
196                     _cachedAsyncConnection = null;
197                 }
198                 _cachedAsyncReader     = null;
199                 _cachedRunBehavior     = RunBehavior.ReturnImmediately;
200                 _cachedSetOptions      = null;
201                 _cachedEndMethod       = null;
202             }
203
204             internal void SetActiveConnectionAndResult(TaskCompletionSource<object> completion, string endMethod, SqlConnection activeConnection) {
205                 Debug.Assert(activeConnection != null, "Unexpected null connection argument on SetActiveConnectionAndResult!");
206                 TdsParser parser = activeConnection.Parser;
207                 if ((parser == null) || (parser.State == TdsParserState.Closed) || (parser.State == TdsParserState.Broken)) {
208                     throw ADP.ClosedConnectionError();
209                 }
210
211                 _cachedAsyncCloseCount = activeConnection.CloseCount;
212                 _cachedAsyncResult     = completion;
213                 if (null != activeConnection && !parser.MARSOn) {
214                     if (activeConnection.AsyncCommandInProgress)
215                         throw SQL.MARSUnspportedOnConnection();
216                 }
217                 _cachedAsyncConnection = activeConnection;
218
219                 // Should only be needed for non-MARS, but set anyways.
220                 _cachedAsyncConnection.AsyncCommandInProgress = true;
221                 _cachedEndMethod = endMethod;
222             }
223
224             internal void SetAsyncReaderState (SqlDataReader ds, RunBehavior runBehavior, string optionSettings) {
225                 _cachedAsyncReader  = ds;
226                 _cachedRunBehavior  = runBehavior;
227                 _cachedSetOptions   = optionSettings;
228             }
229         }
230
231         CachedAsyncState _cachedAsyncState = null;
232
233         private CachedAsyncState cachedAsyncState {
234             get {
235                 if (_cachedAsyncState == null) {
236                     _cachedAsyncState = new CachedAsyncState ();
237                 }
238                 return  _cachedAsyncState;
239             }
240         }
241
242         // sql reader will pull this value out for each NextResult call.  It is not cumulative
243         // _rowsAffected is cumulative for ExecuteNonQuery across all rpc batches
244         internal int _rowsAffected = -1; // rows affected by the command
245
246         // number of rows affected by sp_describe_parameter_encryption.
247         // The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
248         private int _rowsAffectedBySpDescribeParameterEncryption = -1;
249
250         private SqlNotificationRequest _notification;
251         private bool _notificationAutoEnlist = true;            // Notifications auto enlistment is turned on by default
252
253         // transaction support
254         private SqlTransaction _transaction;
255
256         private StatementCompletedEventHandler _statementCompletedEventHandler;
257
258         private TdsParserStateObject _stateObj; // this is the TDS session we're using.
259
260         // Volatile bool used to synchronize with cancel thread the state change of an executing
261         // command going from pre-processing to obtaining a stateObject.  The cancel synchronization
262         // we require in the command is only from entering an Execute* API to obtaining a 
263         // stateObj.  Once a stateObj is successfully obtained, cancel synchronization is handled
264         // by the stateObject.
265         private volatile bool _pendingCancel;
266
267         private bool _batchRPCMode;
268         private List<_SqlRPC> _RPCList;
269         private _SqlRPC[] _SqlRPCBatchArray;
270         private _SqlRPC[] _sqlRPCParameterEncryptionReqArray;
271         private List<SqlParameterCollection>  _parameterCollectionList;
272         private int     _currentlyExecutingBatch;
273
274         /// <summary>
275         /// This variable is used to keep track of which RPC batch's results are being read when reading the results of
276         /// describe parameter encryption RPC requests in BatchRPCMode.
277         /// </summary>
278         private int _currentlyExecutingDescribeParameterEncryptionRPC;
279
280         /// <summary>
281         /// A flag to indicate if we have in-progress describe parameter encryption RPC requests.
282         /// Reset to false when completed.
283         /// </summary>
284         private bool _isDescribeParameterEncryptionRPCCurrentlyInProgress;
285
286         /// <summary>
287         /// Return the flag that indicates if describe parameter encryption RPC requests are in-progress.
288         /// </summary>
289         internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress {
290             get {
291                 return _isDescribeParameterEncryptionRPCCurrentlyInProgress;
292             }
293         }
294
295         /// <summary>
296         /// A flag to indicate if EndExecute was already initiated by the Begin call.
297         /// </summary>
298         private volatile bool _internalEndExecuteInitiated;
299
300         /// <summary>
301         /// A flag to indicate whether we postponed caching the query metadata for this command.
302         /// </summary>
303         internal bool CachingQueryMetadataPostponed { get; set; }
304
305         //
306         //  Smi execution-specific stuff
307         //
308         sealed private class CommandEventSink : SmiEventSink_Default {
309             private SqlCommand _command;
310
311             internal CommandEventSink( SqlCommand command ) : base( ) {
312                 _command = command;
313             }
314
315             internal override void StatementCompleted( int rowsAffected ) {
316                 if (Bid.AdvancedOn) {
317                     Bid.Trace("<sc.SqlCommand.CommandEventSink.StatementCompleted|ADV> %d#, rowsAffected=%d.\n", _command.ObjectID, rowsAffected);
318                 }
319                 _command.InternalRecordsAffected = rowsAffected;
320
321 // 
322
323
324
325
326             }
327
328             internal override void BatchCompleted() {
329                 if (Bid.AdvancedOn) {
330                     Bid.Trace("<sc.SqlCommand.CommandEventSink.BatchCompleted|ADV> %d#.\n", _command.ObjectID);
331                 }
332             }
333
334             internal override void ParametersAvailable( SmiParameterMetaData[] metaData, ITypedGettersV3 parameterValues ) {
335                 if (Bid.AdvancedOn) {
336                     Bid.Trace("<sc.SqlCommand.CommandEventSink.ParametersAvailable|ADV> %d# metaData.Length=%d.\n", _command.ObjectID, (null!=metaData)?metaData.Length:-1);
337
338                     if (null != metaData) {
339                         for (int i=0; i < metaData.Length; i++) {
340                             Bid.Trace("<sc.SqlCommand.CommandEventSink.ParametersAvailable|ADV> %d#, metaData[%d] is %ls%ls\n", 
341                                         _command.ObjectID, i, metaData[i].GetType().ToString(), metaData[i].TraceString());
342                         }
343                     }
344                 }
345                 Debug.Assert(SmiContextFactory.Instance.NegotiatedSmiVersion >= SmiContextFactory.YukonVersion);
346                 _command.OnParametersAvailableSmi( metaData, parameterValues );
347             }
348
349             internal override void ParameterAvailable(SmiParameterMetaData metaData, SmiTypedGetterSetter parameterValues, int ordinal)
350             {
351                 if (Bid.AdvancedOn) {
352                     if (null != metaData) {
353                         Bid.Trace("<sc.SqlCommand.CommandEventSink.ParameterAvailable|ADV> %d#, metaData[%d] is %ls%ls\n", 
354                                     _command.ObjectID, ordinal, metaData.GetType().ToString(), metaData.TraceString());
355                     }
356                 }
357                 Debug.Assert(SmiContextFactory.Instance.NegotiatedSmiVersion >= SmiContextFactory.KatmaiVersion);
358                 _command.OnParameterAvailableSmi(metaData, parameterValues, ordinal);
359             }
360         }
361
362         private SmiContext              _smiRequestContext; // context that _smiRequest came from
363         private CommandEventSink _smiEventSink;
364         private SmiEventSink_DeferedProcessing _outParamEventSink;
365
366         private CommandEventSink EventSink {
367             get {
368                 if ( null == _smiEventSink ) {
369                     _smiEventSink = new CommandEventSink( this );
370                 }
371
372                 _smiEventSink.Parent = InternalSmiConnection.CurrentEventSink;
373                 return _smiEventSink;
374             }
375         }
376
377         private SmiEventSink_DeferedProcessing OutParamEventSink {
378             get {
379                 if (null == _outParamEventSink) {
380                     _outParamEventSink = new SmiEventSink_DeferedProcessing(EventSink);
381                 }
382                 else {
383                     _outParamEventSink.Parent = EventSink;
384                 }
385
386                 return _outParamEventSink;
387             }
388         }
389
390
391         public SqlCommand() : base() {
392             GC.SuppressFinalize(this);
393         }
394
395         public SqlCommand(string cmdText) : this() {
396             CommandText = cmdText;
397         }
398
399         public SqlCommand(string cmdText, SqlConnection connection) : this() {
400             CommandText = cmdText;
401             Connection = connection;
402         }
403
404         public SqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction) : this() {
405             CommandText = cmdText;
406             Connection = connection;
407             Transaction = transaction;
408         }
409
410         public SqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction, SqlCommandColumnEncryptionSetting columnEncryptionSetting) : this() {
411             CommandText = cmdText;
412             Connection = connection;
413             Transaction = transaction;
414             _columnEncryptionSetting = columnEncryptionSetting;
415         }
416
417         private SqlCommand(SqlCommand from) : this() { // Clone
418             CommandText = from.CommandText;
419             CommandTimeout = from.CommandTimeout;
420             CommandType = from.CommandType;
421             Connection = from.Connection;
422             DesignTimeVisible = from.DesignTimeVisible;
423             Transaction = from.Transaction;
424             UpdatedRowSource = from.UpdatedRowSource;
425             _columnEncryptionSetting = from.ColumnEncryptionSetting;
426
427             SqlParameterCollection parameters = Parameters;
428             foreach(object parameter in from.Parameters) {
429                 parameters.Add((parameter is ICloneable) ? (parameter as ICloneable).Clone() : parameter);
430             }
431         }
432
433         [
434         DefaultValue(null),
435         Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
436         ResCategoryAttribute(Res.DataCategory_Data),
437         ResDescriptionAttribute(Res.DbCommand_Connection),
438         ]
439         new public SqlConnection Connection {
440             get {
441                 return _activeConnection;
442             }
443             set {                
444                 // Don't allow the connection to be changed while in a async opperation.
445                 if (_activeConnection != value && _activeConnection != null) { // If new value...
446                     if (cachedAsyncState.PendingAsyncOperation) { // If in pending async state, throw.
447                         throw SQL.CannotModifyPropertyAsyncOperationInProgress(SQL.Connection);
448                     }
449                 }
450
451                 // Check to see if the currently set transaction has completed.  If so,
452                 // null out our local reference.
453                 if (null != _transaction && _transaction.Connection == null) {
454                     _transaction = null;
455                 }
456
457                 // If the connection has changes, then the request context may have changed as well
458                 _smiRequestContext = null;
459
460                 // Command is no longer prepared on new connection, cleanup prepare status
461                 if (IsPrepared) {
462                     if (_activeConnection != value && _activeConnection != null) {
463                         RuntimeHelpers.PrepareConstrainedRegions();
464                         try {
465 #if DEBUG
466                             TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
467                             RuntimeHelpers.PrepareConstrainedRegions();
468                             try {
469                                 tdsReliabilitySection.Start();
470 #endif //DEBUG
471                                 // cleanup
472                                 Unprepare();
473 #if DEBUG
474                             }
475                             finally {
476                                 tdsReliabilitySection.Stop();
477                             }
478 #endif //DEBUG
479                         }
480                         catch (System.OutOfMemoryException) {
481                             _activeConnection.InnerConnection.DoomThisConnection();
482                             throw;
483                         }
484                         catch (System.StackOverflowException) {
485                             _activeConnection.InnerConnection.DoomThisConnection();
486                             throw;
487                         }
488                         catch (System.Threading.ThreadAbortException) {
489                             _activeConnection.InnerConnection.DoomThisConnection();
490                             throw;
491                         }
492                         catch (Exception) {
493                             // we do not really care about errors in unprepare (may be the old connection went bad)                                        
494                         }
495                         finally {
496                             // clean prepare status (even successfull Unprepare does not do that)
497                             _prepareHandle = -1;
498                             _execType = EXECTYPE.UNPREPARED;
499                         }
500                     }
501                 }
502
503                 _activeConnection = value; // 
504
505                 Bid.Trace("<sc.SqlCommand.set_Connection|API> %d#, %d#\n", ObjectID, ((null != value) ? value.ObjectID : -1));
506             }
507         }
508
509         override protected DbConnection DbConnection { // V1.2.3300
510             get {
511                 return Connection;
512             }
513             set {
514                 Connection = (SqlConnection)value;
515             }
516         }
517
518         private SqlInternalConnectionSmi InternalSmiConnection {
519             get {
520                 return (SqlInternalConnectionSmi)_activeConnection.InnerConnection;
521             }
522         }
523
524         private SqlInternalConnectionTds InternalTdsConnection {
525             get {
526                 return (SqlInternalConnectionTds)_activeConnection.InnerConnection;
527             }
528         }
529
530         private bool IsShiloh {
531             get {
532                 Debug.Assert(_activeConnection != null, "The active connection is null!");
533                 if (_activeConnection == null)
534                     return false;
535                 return _activeConnection.IsShiloh;
536             }
537         }
538
539         [
540         DefaultValue(true),
541         ResCategoryAttribute(Res.DataCategory_Notification),
542         ResDescriptionAttribute(Res.SqlCommand_NotificationAutoEnlist),
543         ]
544         public bool NotificationAutoEnlist {
545             get {
546                 return _notificationAutoEnlist;
547             }
548             set {
549                 _notificationAutoEnlist = value;
550             }
551          }
552
553         [
554         Browsable(false),
555         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), // MDAC 90471
556         ResCategoryAttribute(Res.DataCategory_Notification),
557         ResDescriptionAttribute(Res.SqlCommand_Notification),
558         ]
559         public SqlNotificationRequest Notification {
560             get {
561                 return _notification;
562             }
563             set {
564                 Bid.Trace("<sc.SqlCommand.set_Notification|API> %d#\n", ObjectID);
565                 _sqlDep = null;
566                 _notification = value;
567             }
568         }
569
570
571         internal SqlStatistics Statistics {
572             get {
573                 if (null != _activeConnection) {
574                     if (_activeConnection.StatisticsEnabled) {
575                         return _activeConnection.Statistics;
576                     }
577                 }
578                 return null;
579             }
580         }
581
582         [
583         Browsable(false),
584         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
585         ResDescriptionAttribute(Res.DbCommand_Transaction),
586         ]
587         new public SqlTransaction Transaction {
588             get {
589                 // if the transaction object has been zombied, just return null
590                 if ((null != _transaction) && (null == _transaction.Connection)) { // MDAC 72720
591                     _transaction = null;
592                 }
593                 return _transaction;
594             }
595             set {
596                 // Don't allow the transaction to be changed while in a async opperation.
597                 if (_transaction != value && _activeConnection != null) { // If new value...
598                     if (cachedAsyncState.PendingAsyncOperation) { // If in pending async state, throw
599                         throw SQL.CannotModifyPropertyAsyncOperationInProgress(SQL.Transaction);
600                     }
601                 }
602
603                 // 
604                 Bid.Trace("<sc.SqlCommand.set_Transaction|API> %d#\n", ObjectID);
605                 _transaction = value;
606             }
607         }
608
609         override protected DbTransaction DbTransaction { // V1.2.3300
610             get {
611                 return Transaction;
612             }
613             set {
614                 Transaction = (SqlTransaction)value;
615             }
616         }
617
618         [
619         DefaultValue(""),
620         Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
621         RefreshProperties(RefreshProperties.All), // MDAC 67707
622         ResCategoryAttribute(Res.DataCategory_Data),
623         ResDescriptionAttribute(Res.DbCommand_CommandText),
624         ]
625         override public string CommandText { // V1.2.3300, XXXCommand V1.0.5000
626             get {
627                 string value = _commandText;
628                 return ((null != value) ? value : ADP.StrEmpty);
629             }
630             set {
631                 if (Bid.TraceOn) {
632                     Bid.Trace("<sc.SqlCommand.set_CommandText|API> %d#, '", ObjectID);
633                     Bid.PutStr(value); // Use PutStr to write out entire string
634                     Bid.Trace("'\n");
635                 }
636                 if (0 != ADP.SrcCompare(_commandText, value)) {
637                     PropertyChanging();
638                     _commandText = value;
639                 }
640             }
641         }
642
643         [
644         Browsable(false),
645         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
646         ResCategoryAttribute(Res.DataCategory_Data),
647         ResDescriptionAttribute(Res.TCE_SqlCommand_ColumnEncryptionSetting),
648         ]
649         public SqlCommandColumnEncryptionSetting ColumnEncryptionSetting {
650             get {
651                 return _columnEncryptionSetting;
652             }
653         }
654
655         [
656         ResCategoryAttribute(Res.DataCategory_Data),
657         ResDescriptionAttribute(Res.DbCommand_CommandTimeout),
658         ]
659         override public int CommandTimeout { // V1.2.3300, XXXCommand V1.0.5000
660             get {
661                 return _commandTimeout;
662             }
663             set {
664                 Bid.Trace("<sc.SqlCommand.set_CommandTimeout|API> %d#, %d\n", ObjectID, value);
665                 if (value < 0) {
666                     throw ADP.InvalidCommandTimeout(value);
667                 }
668                 if (value != _commandTimeout) {
669                     PropertyChanging();
670                     _commandTimeout = value;
671                 }
672             }
673         }
674
675         public void ResetCommandTimeout() { // V1.2.3300
676             if (ADP.DefaultCommandTimeout != _commandTimeout) {
677                 PropertyChanging();
678                 _commandTimeout = ADP.DefaultCommandTimeout;
679             }
680         }
681
682         private bool ShouldSerializeCommandTimeout() { // V1.2.3300
683             return (ADP.DefaultCommandTimeout != _commandTimeout);
684         }
685
686         [
687         DefaultValue(System.Data.CommandType.Text),
688         RefreshProperties(RefreshProperties.All),
689         ResCategoryAttribute(Res.DataCategory_Data),
690         ResDescriptionAttribute(Res.DbCommand_CommandType),
691         ]
692         override public CommandType CommandType { // V1.2.3300, XXXCommand V1.0.5000
693             get {
694                 CommandType cmdType = _commandType;
695                 return ((0 != cmdType) ? cmdType : CommandType.Text);
696             }
697             set {
698                 Bid.Trace("<sc.SqlCommand.set_CommandType|API> %d#, %d{ds.CommandType}\n", ObjectID, (int)value);
699                 if (_commandType != value) {
700                     switch(value) { // @perfnote: Enum.IsDefined
701                     case CommandType.Text:
702                     case CommandType.StoredProcedure:
703                         PropertyChanging();
704                         _commandType = value;
705                         break;
706                     case System.Data.CommandType.TableDirect:
707                         throw SQL.NotSupportedCommandType(value);
708                     default:
709                         throw ADP.InvalidCommandType(value);
710                     }
711                 }
712             }
713         }
714
715         // @devnote: By default, the cmd object is visible on the design surface (i.e. VS7 Server Tray)
716         // to limit the number of components that clutter the design surface,
717         // when the DataAdapter design wizard generates the insert/update/delete commands it will
718         // set the DesignTimeVisible property to false so that cmds won't appear as individual objects
719         [
720         DefaultValue(true),
721         DesignOnly(true),
722         Browsable(false),
723         EditorBrowsableAttribute(EditorBrowsableState.Never),
724         ]
725         public override bool DesignTimeVisible { // V1.2.3300, XXXCommand V1.0.5000
726             get {
727                 return !_designTimeInvisible;
728             }
729             set {
730                 _designTimeInvisible = !value;
731                 TypeDescriptor.Refresh(this); // VS7 208845
732             }
733         }
734
735         [
736         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
737         ResCategoryAttribute(Res.DataCategory_Data),
738         ResDescriptionAttribute(Res.DbCommand_Parameters),
739         ]
740         new public SqlParameterCollection Parameters {
741             get {
742                 if (null == this._parameters) {
743                     // delay the creation of the SqlParameterCollection
744                     // until user actually uses the Parameters property
745                     this._parameters = new SqlParameterCollection();
746                 }
747                 return this._parameters;
748             }
749         }
750
751         override protected DbParameterCollection DbParameterCollection { // V1.2.3300
752             get {
753                 return Parameters;
754             }
755         }
756
757         [
758         DefaultValue(System.Data.UpdateRowSource.Both),
759         ResCategoryAttribute(Res.DataCategory_Update),
760         ResDescriptionAttribute(Res.DbCommand_UpdatedRowSource),
761         ]
762         override public UpdateRowSource UpdatedRowSource { // V1.2.3300, XXXCommand V1.0.5000
763             get {
764                 return _updatedRowSource;
765             }
766             set {
767                 switch(value) { // @perfnote: Enum.IsDefined
768                 case UpdateRowSource.None:
769                 case UpdateRowSource.OutputParameters:
770                 case UpdateRowSource.FirstReturnedRecord:
771                 case UpdateRowSource.Both:
772                     _updatedRowSource = value;
773                     break;
774                 default:
775                     throw ADP.InvalidUpdateRowSource(value);
776                 }
777             }
778         }
779
780         [
781         ResCategoryAttribute(Res.DataCategory_StatementCompleted),
782         ResDescriptionAttribute(Res.DbCommand_StatementCompleted),
783         ]
784         public event StatementCompletedEventHandler StatementCompleted {
785             add {
786                 _statementCompletedEventHandler += value;
787             }
788             remove {
789                 _statementCompletedEventHandler -= value;
790             }
791         }
792
793         internal void OnStatementCompleted(int recordCount) { // V1.2.3300
794              if (0 <= recordCount) {
795                 StatementCompletedEventHandler handler = _statementCompletedEventHandler;
796                 if (null != handler) {
797                     try {
798                         Bid.Trace("<sc.SqlCommand.OnStatementCompleted|INFO> %d#, recordCount=%d\n", ObjectID, recordCount);
799                         handler(this, new StatementCompletedEventArgs(recordCount));
800                     }
801                     catch(Exception e) {
802                         // 
803                         if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
804                             throw;
805                         }
806
807                         ADP.TraceExceptionWithoutRethrow(e);
808                     }
809                 }
810             }
811         }
812
813         private void PropertyChanging() { // also called from SqlParameterCollection
814             this.IsDirty = true;
815         }
816
817         override public void Prepare() {
818             SqlConnection.ExecutePermission.Demand();
819
820             // Reset _pendingCancel upon entry into any Execute - used to synchronize state
821             // between entry into Execute* API and the thread obtaining the stateObject.
822             _pendingCancel = false;
823
824             // Context connection's prepare is a no-op
825             if (null != _activeConnection && _activeConnection.IsContextConnection) {
826                 return;
827             }
828
829             SqlStatistics statistics = null;
830             IntPtr hscp;
831             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.Prepare|API> %d#", ObjectID);
832             Bid.CorrelationTrace("<sc.SqlCommand.Prepare|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
833             statistics = SqlStatistics.StartTimer(Statistics);
834
835             // only prepare if batch with parameters
836             // MDAC 
837             if (
838                 this.IsPrepared && !this.IsDirty
839                 || (this.CommandType == CommandType.StoredProcedure)
840                 ||  (
841                         (System.Data.CommandType.Text == this.CommandType)
842                         && (0 == GetParameterCount (_parameters))
843                     )
844
845             ) {
846                 if (null != Statistics) {
847                     Statistics.SafeIncrement (ref Statistics._prepares);
848                 }
849                 _hiddenPrepare = false;
850             }
851             else {
852                 // Validate the command outside of the try\catch to avoid putting the _stateObj on error
853                 ValidateCommand(ADP.Prepare, false /*not async*/);
854
855                 bool processFinallyBlock = true;
856                 TdsParser bestEffortCleanupTarget = null;
857                 RuntimeHelpers.PrepareConstrainedRegions();
858                 try {
859                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
860
861                     // NOTE: The state object isn't actually needed for this, but it is still here for back-compat (since it does a bunch of checks)
862                     GetStateObject();
863
864                     // Loop through parameters ensuring that we do not have unspecified types, sizes, scales, or precisions
865                     if (null != _parameters) {
866                         int count = _parameters.Count;
867                         for (int i = 0; i < count; ++i) {
868                             _parameters[i].Prepare(this); // MDAC 67063
869                         }
870                     }
871
872 #if DEBUG
873                     TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
874                     RuntimeHelpers.PrepareConstrainedRegions();
875                     try {
876                         tdsReliabilitySection.Start();
877 #else
878                     {
879 #endif //DEBUG
880                         InternalPrepare();
881                     }
882 #if DEBUG
883                     finally {
884                         tdsReliabilitySection.Stop();
885                     }
886 #endif //DEBUG
887                 }
888                 catch (System.OutOfMemoryException e) {
889                     processFinallyBlock = false;
890                     _activeConnection.Abort(e);
891                     throw;
892                 }
893                 catch (System.StackOverflowException e) {
894                     processFinallyBlock = false;
895                     _activeConnection.Abort(e);
896                     throw;
897                 }
898                 catch (System.Threading.ThreadAbortException e)  {
899                     processFinallyBlock = false;
900                     _activeConnection.Abort(e);
901
902                     SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
903                     throw;
904                 }
905                 catch (Exception e) {
906                     processFinallyBlock = ADP.IsCatchableExceptionType(e);
907                     throw;
908                 }
909                 finally {
910                     if (processFinallyBlock) {
911                         _hiddenPrepare = false; // The command is now officially prepared
912     
913                         ReliablePutStateObject();
914                     }
915                 }
916             }
917
918             SqlStatistics.StopTimer(statistics);
919             Bid.ScopeLeave(ref hscp);
920         }
921
922         private void InternalPrepare() {
923             if (this.IsDirty) {
924                 Debug.Assert(_cachedMetaData == null || !_dirty, "dirty query should not have cached metadata!"); // can have cached metadata if dirty because of parameters
925                 //
926                 // someone changed the command text or the parameter schema so we must unprepare the command
927                 //
928                 this.Unprepare();
929                 this.IsDirty = false;
930             }
931             Debug.Assert(_execType != EXECTYPE.PREPARED, "Invalid attempt to Prepare already Prepared command!");
932             Debug.Assert(_activeConnection != null, "must have an open connection to Prepare");
933             Debug.Assert(null != _stateObj, "TdsParserStateObject should not be null");
934             Debug.Assert(null != _stateObj.Parser, "TdsParser class should not be null in Command.Execute!");
935             Debug.Assert(_stateObj.Parser == _activeConnection.Parser, "stateobject parser not same as connection parser");
936             Debug.Assert(false == _inPrepare, "Already in Prepare cycle, this.inPrepare should be false!");
937
938             // remember that the user wants to do a prepare but don't actually do an rpc
939             _execType = EXECTYPE.PREPAREPENDING;
940             // Note the current close count of the connection - this will tell us if the connection has been closed between calls to Prepare() and Execute
941             _preparedConnectionCloseCount = _activeConnection.CloseCount;
942             _preparedConnectionReconnectCount = _activeConnection.ReconnectCount;
943
944             if (null != Statistics) {
945                 Statistics.SafeIncrement(ref Statistics._prepares);
946             }
947         }
948
949         // SqlInternalConnectionTds needs to be able to unprepare a statement
950         internal void Unprepare() {
951             // Context connection's prepare is a no-op
952             if (_activeConnection.IsContextConnection) {
953                 return;
954             }
955
956             Debug.Assert(true == IsPrepared, "Invalid attempt to Unprepare a non-prepared command!");
957             Debug.Assert(_activeConnection != null, "must have an open connection to UnPrepare");
958             Debug.Assert(false == _inPrepare, "_inPrepare should be false!");
959
960             // @devnote: we're always falling back to Prepare pending
961             // @devnote: This seems broken because once the command is prepared it will - always - be a
962             // @devnote: prepared execution.
963             // @devnote: Even replacing the parameterlist with something completely different or
964             // @devnote: changing the commandtext to a non-parameterized query will result in prepared execution
965             // @devnote:
966             // @devnote: We need to keep the behavior for backward compatibility though (non-breaking change)
967             //
968             _execType = EXECTYPE.PREPAREPENDING;
969             // Don't zero out the handle because we'll pass it in to sp_prepexec on the next prepare
970             // Unless the close count isn't the same as when we last prepared
971             if ((_activeConnection.CloseCount != _preparedConnectionCloseCount) || (_activeConnection.ReconnectCount != _preparedConnectionReconnectCount)) {
972                 // reset our handle
973                 _prepareHandle = -1;
974             }
975
976             _cachedMetaData = null;
977             Bid.Trace("<sc.SqlCommand.Prepare|INFO> %d#, Command unprepared.\n", ObjectID);
978         }
979
980
981         // Cancel is supposed to be multi-thread safe.
982         // It doesn't make sense to verify the connection exists or that it is open during cancel
983         // because immediately after checkin the connection can be closed or removed via another thread.
984         //
985         override public void Cancel() {
986             IntPtr hscp;
987             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.Cancel|API> %d#", ObjectID);
988             
989             Bid.CorrelationTrace("<sc.SqlCommand.Cancel|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
990
991             SqlStatistics statistics = null;
992             try {
993                 statistics = SqlStatistics.StartTimer(Statistics);
994
995                 // If we are in reconnect phase simply cancel the waiting task
996                 var reconnectCompletionSource = _reconnectionCompletionSource;
997                 if (reconnectCompletionSource != null) {
998                     if (reconnectCompletionSource.TrySetCanceled()) {                        
999                         return;
1000                     }
1001                 }
1002                 
1003                 // the pending data flag means that we are awaiting a response or are in the middle of proccessing a response
1004                 // if we have no pending data, then there is nothing to cancel
1005                 // if we have pending data, but it is not a result of this command, then we don't cancel either.  Note that
1006                 // this model is implementable because we only allow one active command at any one time.  This code
1007                 // will have to change we allow multiple outstanding batches
1008
1009                 // 
1010                 if (null == _activeConnection) {
1011                     return;
1012                 }
1013                 SqlInternalConnectionTds connection = (_activeConnection.InnerConnection as SqlInternalConnectionTds);
1014                 if (null == connection) {  // Fail with out locking
1015                      return;
1016                 }
1017
1018                 // The lock here is to protect against the command.cancel / connection.close race condition
1019                 // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and 
1020                 // the command will no longer be cancelable.  It might be desirable to be able to cancel the close opperation, but this is
1021                 // outside of the scope of Whidbey RTM.  See (SqlConnection::Close) for other lock.
1022                 lock (connection) {                                              
1023                     if (connection != (_activeConnection.InnerConnection as SqlInternalConnectionTds)) { // make sure the connection held on the active connection is what we have stored in our temp connection variable, if not between getting "connection" and takeing the lock, the connection has been closed
1024                         return;
1025                     }
1026                     
1027                     TdsParser parser = connection.Parser;
1028                     if (null == parser) {
1029                         return;
1030                     }
1031
1032                     TdsParser bestEffortCleanupTarget = null;
1033                     RuntimeHelpers.PrepareConstrainedRegions();
1034                     try {
1035 #if DEBUG
1036                         TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1037
1038                         RuntimeHelpers.PrepareConstrainedRegions();
1039                         try {
1040                             tdsReliabilitySection.Start();
1041 #else
1042                         {
1043 #endif //DEBUG
1044                             bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
1045
1046                             if (!_pendingCancel) { // Do nothing if aleady pending.
1047                                 // Before attempting actual cancel, set the _pendingCancel flag to false.
1048                                 // This denotes to other thread before obtaining stateObject from the
1049                                 // session pool that there is another thread wishing to cancel.
1050                                 // The period in question is between entering the ExecuteAPI and obtaining 
1051                                 // a stateObject.
1052                                 _pendingCancel = true;
1053
1054                                 TdsParserStateObject stateObj = _stateObj;
1055                                 if (null != stateObj) {
1056                                     stateObj.Cancel(ObjectID);
1057                                 }
1058                                 else {
1059                                     SqlDataReader reader = connection.FindLiveReader(this);
1060                                     if (reader != null) {
1061                                         reader.Cancel(ObjectID);
1062                                     }
1063                                 }
1064                             }
1065                         }
1066 #if DEBUG
1067                         finally {
1068                             tdsReliabilitySection.Stop();
1069                         }
1070 #endif //DEBUG
1071                     }
1072                     catch (System.OutOfMemoryException e) {
1073                         _activeConnection.Abort(e);
1074                         throw;
1075                     }
1076                     catch (System.StackOverflowException e) {
1077                         _activeConnection.Abort(e);
1078                         throw;
1079                     }
1080                     catch (System.Threading.ThreadAbortException e)  {
1081                         _activeConnection.Abort(e);
1082                         SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1083                         throw;
1084                     }
1085                 }
1086             }
1087             finally {
1088                 SqlStatistics.StopTimer(statistics);
1089                 Bid.ScopeLeave(ref hscp);
1090             }
1091         }
1092
1093         new public SqlParameter CreateParameter() {
1094             return new SqlParameter();
1095         }
1096
1097         override protected DbParameter CreateDbParameter() {
1098             return CreateParameter();
1099         }
1100
1101         override protected void Dispose(bool disposing) {
1102             if (disposing) { // release mananged objects
1103
1104                 // V1.0, V1.1 did not reset the Connection, Parameters, CommandText, WebData 100524
1105                 //_parameters = null;
1106                 //_activeConnection = null;
1107                 //_statistics = null;
1108                 //CommandText = null;
1109                 _cachedMetaData = null;
1110             }
1111             // release unmanaged objects
1112             base.Dispose(disposing);
1113         }
1114
1115         override public object ExecuteScalar() {
1116             SqlConnection.ExecutePermission.Demand();
1117
1118             // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1119             // between entry into Execute* API and the thread obtaining the stateObject.
1120             _pendingCancel = false;
1121
1122             SqlStatistics statistics = null;
1123             IntPtr hscp;
1124             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteScalar|API> %d#", ObjectID);
1125             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteScalar|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1126
1127             bool success = false;
1128             int? sqlExceptionNumber = null;
1129             try {
1130                 statistics = SqlStatistics.StartTimer(Statistics);
1131                 WriteBeginExecuteEvent();
1132                 SqlDataReader ds;
1133                 ds = RunExecuteReader(0, RunBehavior.ReturnImmediately, true, ADP.ExecuteScalar);
1134                 object result = CompleteExecuteScalar(ds, false);
1135                 success = true;
1136                 return result;
1137             }
1138             catch (SqlException ex) {
1139                 sqlExceptionNumber = ex.Number;
1140                 throw;
1141             }
1142             finally {
1143                 SqlStatistics.StopTimer(statistics);
1144                 Bid.ScopeLeave(ref hscp);
1145                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
1146             }
1147         }
1148
1149         private object CompleteExecuteScalar(SqlDataReader ds, bool returnSqlValue) {
1150             object retResult = null;
1151
1152             try {
1153                 if (ds.Read()) {
1154                     if (ds.FieldCount > 0) {
1155                         if (returnSqlValue) {
1156                             retResult = ds.GetSqlValue(0);
1157                         }
1158                         else {
1159                             retResult = ds.GetValue(0);
1160                         }
1161                     }
1162                 }
1163             }
1164             finally {
1165                 // clean off the wire
1166                 ds.Close();
1167             }
1168
1169             return retResult;
1170         }
1171
1172         override public int ExecuteNonQuery() {
1173             SqlConnection.ExecutePermission.Demand();
1174
1175             // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1176             // between entry into Execute* API and the thread obtaining the stateObject.
1177             _pendingCancel = false;
1178
1179             SqlStatistics statistics = null;
1180             IntPtr hscp;
1181             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteNonQuery|API> %d#", ObjectID);
1182             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQuery|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1183             bool success = false;
1184             int? sqlExceptionNumber = null;
1185             try {
1186                 statistics = SqlStatistics.StartTimer(Statistics);
1187                 WriteBeginExecuteEvent();
1188                 bool usedCache;
1189                 InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, false, CommandTimeout, out usedCache);
1190                 success = true;
1191                 return _rowsAffected;
1192             }
1193             catch (SqlException ex) {
1194                 sqlExceptionNumber = ex.Number;
1195                 throw;
1196             }
1197             finally {
1198                 SqlStatistics.StopTimer(statistics);
1199                 Bid.ScopeLeave(ref hscp);
1200                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous : true);
1201             }
1202         }
1203
1204         // Handles in-proc execute-to-pipe functionality
1205         //  Identical to ExecuteNonQuery
1206         internal void ExecuteToPipe( SmiContext pipeContext ) {
1207             SqlConnection.ExecutePermission.Demand();
1208
1209             // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1210             // between entry into Execute* API and the thread obtaining the stateObject.
1211             _pendingCancel = false;
1212
1213             SqlStatistics statistics = null;
1214             IntPtr hscp;
1215             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteToPipe|INFO> %d#", ObjectID);
1216             try {
1217                 statistics = SqlStatistics.StartTimer(Statistics);
1218                 bool usedCache;
1219                 InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, true, CommandTimeout, out usedCache);
1220             }
1221             finally {
1222                 SqlStatistics.StopTimer(statistics);
1223                 Bid.ScopeLeave(ref hscp);
1224             }
1225         }
1226
1227         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1228         public IAsyncResult BeginExecuteNonQuery() {
1229             // BeginExecuteNonQuery will track ExecutionTime for us
1230             return BeginExecuteNonQuery(null, null);
1231         }
1232
1233         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1234         public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) {
1235             Bid.CorrelationTrace("<sc.SqlCommand.BeginExecuteNonQuery|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1236             SqlConnection.ExecutePermission.Demand();
1237             return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false);
1238         }
1239
1240         private IAsyncResult BeginExecuteNonQueryAsync(AsyncCallback callback, object stateObject) {
1241             return BeginExecuteNonQueryInternal(0, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite:true);
1242         }
1243
1244         private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false) {
1245             TaskCompletionSource<object> globalCompletion = new TaskCompletionSource<object>(stateObject);
1246             TaskCompletionSource<object> localCompletion = new TaskCompletionSource<object>(stateObject);
1247
1248             if (!inRetry) {
1249                 // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1250                 // between entry into Execute* API and the thread obtaining the stateObject.
1251                 _pendingCancel = false;
1252
1253                 ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj
1254                                         // back into pool when we should not.
1255             }
1256
1257             SqlStatistics statistics = null;
1258             try {
1259                 if (!inRetry) {
1260                     statistics = SqlStatistics.StartTimer(Statistics);
1261                     WriteBeginExecuteEvent();
1262                 }
1263
1264                 bool usedCache;
1265                 try { // InternalExecuteNonQuery already has reliability block, but if failure will not put stateObj back into pool.
1266                     Task execNQ = InternalExecuteNonQuery(localCompletion, ADP.BeginExecuteNonQuery, false, timeout, out usedCache, asyncWrite, inRetry: inRetry);
1267                     if (execNQ != null) {
1268                         AsyncHelper.ContinueTask(execNQ, localCompletion, () => BeginExecuteNonQueryInternalReadStage(localCompletion));
1269                     }
1270                     else {
1271                         BeginExecuteNonQueryInternalReadStage(localCompletion);
1272                     }
1273                 }
1274                 catch (Exception e) {
1275                     if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
1276                         // If not catchable - the connection has already been caught and doomed in RunExecuteReader.
1277                         throw;
1278                     }
1279
1280                     // For async, RunExecuteReader will never put the stateObj back into the pool, so do so now.
1281                     ReliablePutStateObject(); 
1282                     throw;
1283                 }
1284
1285                 // When we use query caching for parameter encryption we need to retry on specific errors.
1286                 // In these cases finalize the call internally and trigger a retry when needed.
1287                 if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteNonQuery, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteNonQuery, BeginExecuteNonQueryInternal)) {
1288                     globalCompletion = localCompletion;
1289                 }
1290
1291                 // Add callback after work is done to avoid overlapping Begin\End methods
1292                 if (callback != null) {
1293                     globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default);
1294                 }
1295
1296                 return globalCompletion.Task;
1297             }
1298             finally {
1299                 SqlStatistics.StopTimer(statistics);
1300             }
1301         }
1302
1303         private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object> completion) {
1304             // Read SNI does not have catches for async exceptions, handle here.
1305             TdsParser bestEffortCleanupTarget = null;
1306             RuntimeHelpers.PrepareConstrainedRegions();
1307             try {
1308 #if DEBUG
1309                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1310
1311                 RuntimeHelpers.PrepareConstrainedRegions();
1312                 try {
1313                     tdsReliabilitySection.Start();
1314 #else
1315                     {
1316 #endif //DEBUG
1317                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
1318                     // must finish caching information before ReadSni which can activate the callback before returning
1319                     cachedAsyncState.SetActiveConnectionAndResult(completion, ADP.EndExecuteNonQuery, _activeConnection);
1320                     _stateObj.ReadSni(completion);
1321                 }
1322 #if DEBUG
1323                 finally {
1324                     tdsReliabilitySection.Stop();
1325                 }
1326 #endif //DEBUG
1327             }
1328             catch (System.OutOfMemoryException e) {
1329                 _activeConnection.Abort(e);
1330                 throw;
1331             }
1332             catch (System.StackOverflowException e) {
1333                 _activeConnection.Abort(e);
1334                 throw;
1335             }
1336             catch (System.Threading.ThreadAbortException e) {
1337                 _activeConnection.Abort(e);
1338                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1339                 throw;
1340             }
1341             catch (Exception) {
1342                 // Similarly, if an exception occurs put the stateObj back into the pool.
1343                 // and reset async cache information to allow a second async execute
1344                 if (null != _cachedAsyncState) {
1345                     _cachedAsyncState.ResetAsyncState();
1346                 }
1347                 ReliablePutStateObject();
1348                 throw;
1349             }
1350         }
1351
1352         private void VerifyEndExecuteState(Task completionTask, String endMethod, bool fullCheckForColumnEncryption = false) {
1353             if (null == completionTask) {
1354                 throw ADP.ArgumentNull("asyncResult");
1355             }
1356             if (completionTask.IsCanceled) {
1357                 if (_stateObj != null) {
1358                     _stateObj.Parser.State = TdsParserState.Broken; // We failed to respond to attention, we have to quit!
1359                     _stateObj.Parser.Connection.BreakConnection();
1360                     _stateObj.Parser.ThrowExceptionAndWarning(_stateObj);
1361                 }
1362                 else {
1363                     Debug.Assert(_reconnectionCompletionSource == null || _reconnectionCompletionSource.Task.IsCanceled, "ReconnectCompletionSource should be null or cancelled");
1364                     throw SQL.CR_ReconnectionCancelled();
1365                 }
1366             }
1367             else if (completionTask.IsFaulted) {
1368                 throw completionTask.Exception.InnerException;
1369             }
1370
1371             // If transparent parameter encryption was attempted, then we need to skip other checks like those on EndMethodName
1372             // since we want to wait for async results before checking those fields.
1373             if (IsColumnEncryptionEnabled && !fullCheckForColumnEncryption) {
1374                 if (_activeConnection.State != ConnectionState.Open) {
1375                     // If the connection is not 'valid' then it was closed while we were executing
1376                     throw ADP.ClosedConnectionError();
1377                 }
1378
1379                 return;
1380             }
1381
1382             if (cachedAsyncState.EndMethodName == null) {
1383                 throw ADP.MethodCalledTwice(endMethod);
1384             }
1385             if (endMethod != cachedAsyncState.EndMethodName) {
1386                 throw ADP.MismatchedAsyncResult(cachedAsyncState.EndMethodName, endMethod);
1387             }
1388             if ((_activeConnection.State != ConnectionState.Open) || (!cachedAsyncState.IsActiveConnectionValid(_activeConnection))) {
1389                 // If the connection is not 'valid' then it was closed while we were executing
1390                 throw ADP.ClosedConnectionError();
1391             }
1392         }
1393
1394         private void WaitForAsyncResults(IAsyncResult asyncResult, bool isInternal) {
1395             Task completionTask = (Task) asyncResult;
1396             if (!asyncResult.IsCompleted) {
1397                 asyncResult.AsyncWaitHandle.WaitOne();
1398             }
1399
1400             if (_stateObj != null) {
1401                 _stateObj._networkPacketTaskSource = null;
1402             }
1403
1404             // If this is an internal command we will decrement the count when the End method is actually called by the user.
1405             // If we are using Column Encryption and the previous task failed, the async count should have already been fixed up. 
1406             // There is a generic issue in how we handle the async count because:
1407             // a) BeginExecute might or might not clean it up on failure.
1408             // b) In EndExecute, we check the task state before waiting and throw if it's failed, whereas if we wait we will always adjust the count.
1409             if (!isInternal && (!IsColumnEncryptionEnabled || !completionTask.IsFaulted)) {
1410                 _activeConnection.GetOpenTdsConnection().DecrementAsyncCount();
1411             }
1412         }
1413
1414         public int EndExecuteNonQuery(IAsyncResult asyncResult) {
1415             try {
1416                 return EndExecuteNonQueryInternal(asyncResult);
1417             } 
1418             finally {
1419                 Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteNonQuery|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1420             }
1421         }
1422
1423         private void ThrowIfReconnectionHasBeenCanceled() {
1424             if (_stateObj == null) {
1425                 var reconnectionCompletionSource = _reconnectionCompletionSource;
1426                 if (reconnectionCompletionSource != null && reconnectionCompletionSource.Task.IsCanceled) {
1427                     throw SQL.CR_ReconnectionCancelled();
1428                 }
1429             }
1430         }
1431         
1432         private int EndExecuteNonQueryAsync(IAsyncResult asyncResult) {
1433             Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteNonQueryAsync|Info|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1434             Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null);
1435
1436             Exception asyncException = ((Task)asyncResult).Exception;
1437             if (asyncException != null) {
1438                 // Leftover exception from the Begin...InternalReadStage
1439                 ReliablePutStateObject();
1440                 throw asyncException.InnerException;
1441             }
1442             else {
1443                 ThrowIfReconnectionHasBeenCanceled();
1444                 // lock on _stateObj prevents ----s with close/cancel.
1445                 // If we have already initiate the End call internally, we have already done that, so no point doing it again.
1446                 if (!_internalEndExecuteInitiated) {
1447                     lock (_stateObj) {
1448                         return EndExecuteNonQueryInternal(asyncResult);
1449                     }
1450                 }
1451                 else {
1452                     return EndExecuteNonQueryInternal(asyncResult);
1453                 }
1454             }
1455         }
1456
1457         private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) {
1458             SqlStatistics statistics = null;
1459             bool success = false;
1460             int? sqlExceptionNumber = null;
1461             try {
1462                 statistics = SqlStatistics.StartTimer(Statistics);
1463                 int result = (int)InternalEndExecuteNonQuery(asyncResult, ADP.EndExecuteNonQuery, isInternal: false);
1464                 success = true;
1465                 return result;
1466             }
1467             catch (SqlException e) {
1468                 sqlExceptionNumber = e.Number;
1469                 if (cachedAsyncState != null) {
1470                     cachedAsyncState.ResetAsyncState();
1471                 };
1472
1473                 //  SqlException is always catchable 
1474                 ReliablePutStateObject();
1475                 throw;
1476             }
1477             catch (Exception e) {
1478                 if (cachedAsyncState != null) {
1479                     cachedAsyncState.ResetAsyncState();
1480                 };
1481                 if (ADP.IsCatchableExceptionType(e)) {
1482                     ReliablePutStateObject();
1483                 };
1484                 throw;
1485             }
1486             finally {
1487                 SqlStatistics.StopTimer(statistics);
1488                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false);
1489             }
1490         }
1491
1492         private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, string endMethod, bool isInternal) {
1493             TdsParser bestEffortCleanupTarget = null;
1494             RuntimeHelpers.PrepareConstrainedRegions();
1495
1496             try {
1497 #if DEBUG
1498                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1499
1500                 RuntimeHelpers.PrepareConstrainedRegions();
1501                 try {
1502                     tdsReliabilitySection.Start();
1503 #else
1504                 {
1505 #endif //DEBUG
1506                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
1507                     VerifyEndExecuteState((Task)asyncResult, endMethod);
1508                     WaitForAsyncResults(asyncResult, isInternal);
1509
1510                     // If column encryption is enabled, also check the state after waiting for the task.
1511                     // It would be better to do this for all cases, but avoiding for compatibility reasons.
1512                     if (IsColumnEncryptionEnabled) {
1513                         VerifyEndExecuteState((Task)asyncResult, endMethod, fullCheckForColumnEncryption: true);
1514                     }
1515
1516                     bool processFinallyBlock = true;
1517                     try {
1518                         // If this is not for internal usage, notify the dependency. 
1519                         // If we have already initiated the end internally, the reader should be ready, so just return the rows affected.
1520                         if (!isInternal) {
1521                             NotifyDependency();
1522
1523                             if (_internalEndExecuteInitiated) {
1524                                 Debug.Assert(_stateObj == null);
1525
1526                                 // Reset the state since we exit early.
1527                                 cachedAsyncState.ResetAsyncState();
1528
1529                                 return _rowsAffected;
1530                             }
1531                         }
1532
1533                         CheckThrowSNIException();
1534
1535                         // only send over SQL Batch command if we are not a stored proc and have no parameters
1536                         if ((System.Data.CommandType.Text == this.CommandType) && (0 == GetParameterCount(_parameters))) {
1537                             try {
1538                                 bool dataReady;
1539                                 Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
1540                                 bool result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, null, null, _stateObj, out dataReady);
1541                                 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
1542                             }
1543                             finally {
1544                                 // Don't reset the state for internal End. The user End will do that eventually.
1545                                 if (!isInternal) {
1546                                     cachedAsyncState.ResetAsyncState();
1547                                 }
1548                             }
1549                         }
1550                         else { // otherwise, use a full-fledged execute that can handle params and stored procs
1551                             SqlDataReader reader = CompleteAsyncExecuteReader(isInternal);
1552                             if (null != reader) {
1553                                 reader.Close();
1554                             }
1555                         }
1556                     }
1557                     catch (Exception e) {
1558                         processFinallyBlock = ADP.IsCatchableExceptionType(e);
1559                         throw;
1560                     }
1561                     finally {
1562                         if (processFinallyBlock) {
1563                             PutStateObject();
1564                         }
1565                     }
1566
1567                     Debug.Assert(null == _stateObj, "non-null state object in EndExecuteNonQuery");
1568                     return _rowsAffected;
1569                 }
1570 #if DEBUG
1571                 finally {
1572                     tdsReliabilitySection.Stop();
1573                 }
1574 #endif //DEBUG
1575             }
1576             catch (System.OutOfMemoryException e) {
1577                 _activeConnection.Abort(e);
1578                 throw;
1579             }
1580             catch (System.StackOverflowException e) {
1581                 _activeConnection.Abort(e);
1582                 throw;
1583             }
1584             catch (System.Threading.ThreadAbortException e) {
1585                 _activeConnection.Abort(e);
1586                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1587                 throw;
1588             }
1589         }
1590
1591         private Task InternalExecuteNonQuery(TaskCompletionSource<object> completion, string methodName, bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite = false, bool inRetry = false) {
1592             bool async = (null != completion);
1593             usedCache = false;
1594
1595             SqlStatistics statistics = Statistics;
1596             _rowsAffected = -1;
1597
1598             TdsParser bestEffortCleanupTarget = null;
1599             RuntimeHelpers.PrepareConstrainedRegions();
1600             try {
1601 #if DEBUG
1602                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1603
1604                 RuntimeHelpers.PrepareConstrainedRegions();
1605                 try {
1606                     tdsReliabilitySection.Start();
1607 #else
1608                 {
1609 #endif //DEBUG
1610                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
1611                     // @devnote: this function may throw for an invalid connection
1612                     // @devnote: returns false for empty command text
1613                     if (!inRetry) {
1614                         ValidateCommand(methodName, async);
1615                     }
1616                     CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection!
1617
1618                     Task task = null;
1619
1620                     // only send over SQL Batch command if we are not a stored proc and have no parameters and not in batch RPC mode
1621                     if ( _activeConnection.IsContextConnection ) {
1622                         if (null != statistics) {
1623                             statistics.SafeIncrement(ref statistics._unpreparedExecs);
1624                         }
1625
1626                         RunExecuteNonQuerySmi( sendToPipe );
1627                     }
1628                     else if (!BatchRPCMode && (System.Data.CommandType.Text == this.CommandType) && (0 == GetParameterCount(_parameters))) {
1629                         Debug.Assert( !sendToPipe, "trying to send non-context command to pipe" );
1630                         if (null != statistics) {
1631                             if (!this.IsDirty && this.IsPrepared) {
1632                                 statistics.SafeIncrement(ref statistics._preparedExecs);
1633                             }
1634                             else {
1635                                 statistics.SafeIncrement(ref statistics._unpreparedExecs);
1636                             }
1637                         }
1638
1639                         // We should never get here for a retry since we only have retries for parameters.
1640                         Debug.Assert(!inRetry);
1641
1642                         task = RunExecuteNonQueryTds(methodName, async, timeout, asyncWrite);
1643                     }
1644                     else  { // otherwise, use a full-fledged execute that can handle params and stored procs
1645                         Debug.Assert( !sendToPipe, "trying to send non-context command to pipe" );
1646                         Bid.Trace("<sc.SqlCommand.ExecuteNonQuery|INFO> %d#, Command executed as RPC.\n", ObjectID);
1647                         SqlDataReader reader = RunExecuteReader(0, RunBehavior.UntilDone, false, methodName, completion, timeout, out task, out usedCache, asyncWrite, inRetry);
1648                         if (null!=reader) {
1649                             if (task != null) {
1650                                 task = AsyncHelper.CreateContinuationTask(task, () => reader.Close());
1651                             }
1652                             else {
1653                                 reader.Close();
1654                             }
1655                         }
1656                     }
1657                     Debug.Assert(async || null == _stateObj, "non-null state object in InternalExecuteNonQuery");
1658                     return task;
1659                 }
1660 #if DEBUG
1661                 finally {
1662                     tdsReliabilitySection.Stop();
1663                 }
1664 #endif //DEBUG
1665             }
1666             catch (System.OutOfMemoryException e) {
1667                 _activeConnection.Abort(e);
1668                 throw;
1669             }
1670             catch (System.StackOverflowException e) {
1671                 _activeConnection.Abort(e);
1672                 throw;
1673             }
1674             catch (System.Threading.ThreadAbortException e)  {
1675                 _activeConnection.Abort(e);
1676                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1677                 throw;
1678             }
1679         }
1680
1681         public XmlReader ExecuteXmlReader() {
1682             SqlConnection.ExecutePermission.Demand();
1683
1684             // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1685             // between entry into Execute* API and the thread obtaining the stateObject.
1686             _pendingCancel = false;
1687
1688             SqlStatistics statistics = null;
1689             IntPtr hscp;
1690             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteXmlReader|API> %d#", ObjectID);
1691             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteXmlReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1692             bool success = false;
1693             int? sqlExceptionNumber = null;
1694             try {
1695                 statistics = SqlStatistics.StartTimer(Statistics);
1696                 WriteBeginExecuteEvent();
1697
1698                 // use the reader to consume metadata
1699                 SqlDataReader ds;
1700                 ds = RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, true, ADP.ExecuteXmlReader);
1701                 XmlReader result = CompleteXmlReader(ds);
1702                 success = true;
1703                 return result;
1704             }
1705             catch (SqlException ex) {
1706                 sqlExceptionNumber = ex.Number;
1707                 throw;
1708             }
1709             finally {
1710                 SqlStatistics.StopTimer(statistics);
1711                 Bid.ScopeLeave(ref hscp);
1712                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous : true);
1713             }
1714         }
1715
1716         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1717         public IAsyncResult BeginExecuteXmlReader() {
1718             // BeginExecuteXmlReader will track executiontime
1719             return BeginExecuteXmlReader(null, null);
1720         }
1721
1722         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1723         public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject) {
1724             Bid.CorrelationTrace("<sc.SqlCommand.BeginExecuteXmlReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1725             SqlConnection.ExecutePermission.Demand();
1726             return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false);
1727         }
1728
1729         private IAsyncResult BeginExecuteXmlReaderAsync(AsyncCallback callback, object stateObject) {
1730             return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true);
1731         }
1732
1733         private IAsyncResult BeginExecuteXmlReaderInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false) {
1734             TaskCompletionSource<object> globalCompletion = new TaskCompletionSource<object>(stateObject);
1735             TaskCompletionSource<object> localCompletion = new TaskCompletionSource<object>(stateObject);
1736
1737             if (!inRetry) {
1738                 // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1739                 // between entry into Execute* API and the thread obtaining the stateObject.
1740                 _pendingCancel = false;
1741
1742                 ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj
1743                                         // back into pool when we should not.
1744             }
1745
1746             SqlStatistics statistics = null;
1747             try {
1748                 if (!inRetry) {
1749                     statistics = SqlStatistics.StartTimer(Statistics);
1750                     WriteBeginExecuteEvent();
1751                 }
1752
1753                 bool usedCache;
1754                 Task writeTask;
1755                 try { // InternalExecuteNonQuery already has reliability block, but if failure will not put stateObj back into pool.
1756                     RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, ADP.BeginExecuteXmlReader, localCompletion, timeout, out writeTask, out usedCache, asyncWrite, inRetry);
1757                 }
1758                 catch (Exception e) {
1759                     if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
1760                         // If not catchable - the connection has already been caught and doomed in RunExecuteReader.
1761                         throw;
1762                     }
1763         
1764                     // For async, RunExecuteReader will never put the stateObj back into the pool, so do so now.
1765                     ReliablePutStateObject(); 
1766                     throw;
1767                 }
1768
1769                 if (writeTask != null) {
1770                     AsyncHelper.ContinueTask(writeTask, localCompletion, () => BeginExecuteXmlReaderInternalReadStage(localCompletion));
1771                 }
1772                 else {
1773                     BeginExecuteXmlReaderInternalReadStage(localCompletion);
1774                 }
1775
1776                 // When we use query caching for parameter encryption we need to retry on specific errors.
1777                 // In these cases finalize the call internally and trigger a retry when needed.
1778                 if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteXmlReader, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteReader, BeginExecuteXmlReaderInternal)) {
1779                     globalCompletion = localCompletion;
1780                 }
1781
1782                 // Add callback after work is done to avoid overlapping Begin\End methods
1783                 if (callback != null) {
1784                     globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default);
1785                 }
1786                 return globalCompletion.Task;
1787             }
1788             finally {
1789                 SqlStatistics.StopTimer(statistics);
1790             }
1791         }
1792
1793         private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource<object> completion) {
1794             Debug.Assert(completion != null,"Completion source should not be null");
1795             // Read SNI does not have catches for async exceptions, handle here.
1796             TdsParser bestEffortCleanupTarget = null;
1797             RuntimeHelpers.PrepareConstrainedRegions();
1798             try {
1799 #if DEBUG
1800                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1801
1802                 RuntimeHelpers.PrepareConstrainedRegions();
1803                 try {
1804                     tdsReliabilitySection.Start();
1805 #else
1806                     {
1807 #endif //DEBUG
1808                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
1809                     // must finish caching information before ReadSni which can activate the callback before returning
1810                     cachedAsyncState.SetActiveConnectionAndResult(completion, ADP.EndExecuteXmlReader, _activeConnection);
1811                     _stateObj.ReadSni(completion);
1812                 }
1813 #if DEBUG
1814                 finally {
1815                     tdsReliabilitySection.Stop();
1816                 }
1817 #endif //DEBUG
1818             }
1819             catch (System.OutOfMemoryException e) {
1820                 _activeConnection.Abort(e);
1821                 completion.TrySetException(e);
1822                 throw;
1823             }
1824             catch (System.StackOverflowException e) {
1825                 _activeConnection.Abort(e);
1826                 completion.TrySetException(e);
1827                 throw;
1828             }
1829             catch (System.Threading.ThreadAbortException e) {
1830                 _activeConnection.Abort(e);
1831                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1832                 completion.TrySetException(e);
1833                 throw;
1834             }
1835             catch (Exception e) {
1836                 // Similarly, if an exception occurs put the stateObj back into the pool.
1837                 // and reset async cache information to allow a second async execute
1838                 if (null != _cachedAsyncState) {
1839                     _cachedAsyncState.ResetAsyncState();
1840                 }
1841                 ReliablePutStateObject();
1842                 completion.TrySetException(e);
1843             }
1844         }
1845
1846         public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult) {
1847             try {
1848                 return EndExecuteXmlReaderInternal(asyncResult);
1849             }
1850             finally {
1851                 Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteXmlReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1852             }
1853         }
1854
1855         private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) {
1856             Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteXmlReaderAsync|Info|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1857             Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null);
1858
1859             Exception asyncException = ((Task)asyncResult).Exception;
1860             if (asyncException != null) {
1861                 // Leftover exception from the Begin...InternalReadStage
1862                 ReliablePutStateObject();
1863                 throw asyncException.InnerException;
1864             }
1865             else {
1866                 ThrowIfReconnectionHasBeenCanceled();
1867                 // lock on _stateObj prevents ----s with close/cancel.
1868                 // If we have already initiate the End call internally, we have already done that, so no point doing it again.
1869                 if (!_internalEndExecuteInitiated) {
1870                     lock (_stateObj) {
1871                         return EndExecuteXmlReaderInternal(asyncResult);
1872                     }
1873                 }
1874                 else {
1875                     return EndExecuteXmlReaderInternal(asyncResult);
1876                 }
1877             }
1878         }
1879
1880         private XmlReader EndExecuteXmlReaderInternal(IAsyncResult asyncResult) {
1881             bool success = false;
1882             int? sqlExceptionNumber = null;
1883             try {
1884                 XmlReader result = CompleteXmlReader(InternalEndExecuteReader(asyncResult, ADP.EndExecuteXmlReader, isInternal: false));
1885                 success = true;
1886                 return result;
1887             }
1888             catch (SqlException e){
1889                 sqlExceptionNumber = e.Number;
1890                 if (cachedAsyncState != null) {
1891                     cachedAsyncState.ResetAsyncState();
1892                 };
1893
1894                 //  SqlException is always catchable 
1895                 ReliablePutStateObject();
1896                 throw;
1897             }
1898             catch (Exception e) {
1899                 if (cachedAsyncState != null) {
1900                     cachedAsyncState.ResetAsyncState();
1901                 };
1902                 if (ADP.IsCatchableExceptionType(e)) {
1903                     ReliablePutStateObject();
1904                 };
1905                 throw;
1906             }
1907             finally {
1908                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous : false);
1909             }
1910         }
1911
1912         private XmlReader CompleteXmlReader(SqlDataReader ds) {
1913             XmlReader xr = null;
1914
1915             SmiExtendedMetaData[] md = ds.GetInternalSmiMetaData();
1916             bool isXmlCapable = (null != md && md.Length == 1 && (md[0].SqlDbType == SqlDbType.NText 
1917                                                          || md[0].SqlDbType == SqlDbType.NVarChar 
1918                                                          || md[0].SqlDbType == SqlDbType.Xml));
1919
1920             if (isXmlCapable) {
1921                 try {
1922                     SqlStream sqlBuf = new SqlStream(ds, true /*addByteOrderMark*/, (md[0].SqlDbType == SqlDbType.Xml) ? false : true /*process all rows*/);
1923                     xr = sqlBuf.ToXmlReader();
1924                 }
1925                 catch (Exception e) {
1926                     if (ADP.IsCatchableExceptionType(e)) {
1927                         ds.Close();
1928                     }
1929                     throw;
1930                 }
1931             }
1932             if (xr == null) {
1933                 ds.Close();
1934                 throw SQL.NonXmlResult();
1935             }
1936             return xr;
1937         }
1938
1939         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1940         public IAsyncResult BeginExecuteReader() {
1941             return BeginExecuteReader(null, null, CommandBehavior.Default);
1942         }
1943
1944         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1945         public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject) {
1946             return BeginExecuteReader(callback, stateObject, CommandBehavior.Default);
1947         }
1948
1949         override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) {
1950             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteDbDataReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1951             return ExecuteReader(behavior, ADP.ExecuteReader);
1952         }
1953
1954         new public SqlDataReader ExecuteReader() {
1955             SqlStatistics statistics = null;
1956             IntPtr hscp;
1957             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteReader|API> %d#", ObjectID);
1958             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1959             try {
1960                 statistics = SqlStatistics.StartTimer(Statistics);
1961                 return ExecuteReader(CommandBehavior.Default, ADP.ExecuteReader);
1962             }
1963             finally {
1964                 SqlStatistics.StopTimer(statistics);
1965                 Bid.ScopeLeave(ref hscp);
1966             }
1967         }
1968
1969         new public SqlDataReader ExecuteReader(CommandBehavior behavior) {
1970             IntPtr hscp;
1971             Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteReader|API> %d#, behavior=%d{ds.CommandBehavior}", ObjectID, (int)behavior);
1972             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteReader|API|Correlation> ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior);
1973             try {
1974                 return ExecuteReader(behavior, ADP.ExecuteReader);
1975             }
1976             finally {
1977                 Bid.ScopeLeave(ref hscp);
1978             }
1979         }
1980
1981         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1982         public IAsyncResult BeginExecuteReader(CommandBehavior behavior) {
1983             return BeginExecuteReader(null, null, behavior);
1984         }
1985
1986         [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)]
1987         public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) {
1988             Bid.CorrelationTrace("<sc.SqlCommand.BeginExecuteReader|API|Correlation> ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior);
1989             SqlConnection.ExecutePermission.Demand();
1990             return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false);
1991         }
1992
1993         internal SqlDataReader ExecuteReader(CommandBehavior behavior, string method) {
1994             SqlConnection.ExecutePermission.Demand(); // 
1995
1996             // Reset _pendingCancel upon entry into any Execute - used to synchronize state
1997             // between entry into Execute* API and the thread obtaining the stateObject.
1998             _pendingCancel = false;
1999
2000             SqlStatistics statistics = null;
2001
2002             TdsParser bestEffortCleanupTarget = null;
2003             RuntimeHelpers.PrepareConstrainedRegions();
2004             bool success = false;
2005             int? sqlExceptionNumber = null;
2006             try {
2007                 WriteBeginExecuteEvent();
2008 #if DEBUG
2009                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2010
2011                 RuntimeHelpers.PrepareConstrainedRegions();
2012                 try {
2013                     tdsReliabilitySection.Start();
2014 #else
2015                 {
2016 #endif //DEBUG
2017                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
2018                     statistics = SqlStatistics.StartTimer(Statistics);
2019                     SqlDataReader result = RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, method);
2020                     success = true;
2021                     return result;
2022                 }
2023 #if DEBUG
2024                 finally {
2025                     tdsReliabilitySection.Stop();
2026                 }
2027 #endif //DEBUG
2028             }
2029             catch (SqlException e) {
2030                 sqlExceptionNumber = e.Number;
2031                 throw;
2032             }
2033             catch (System.OutOfMemoryException e) {
2034                 _activeConnection.Abort(e);
2035                 throw;
2036             }
2037             catch (System.StackOverflowException e) {
2038                 _activeConnection.Abort(e);
2039                 throw;
2040             }
2041             catch (System.Threading.ThreadAbortException e)  {
2042                 _activeConnection.Abort(e);
2043                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
2044                 throw;
2045             }
2046             finally {
2047                 SqlStatistics.StopTimer(statistics);
2048                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous : true);
2049             }
2050         }
2051
2052         public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) {
2053             try {
2054                 return EndExecuteReaderInternal(asyncResult);
2055             }          
2056             finally {
2057                 Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
2058             }
2059         }
2060
2061         private SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) {
2062             Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteReaderAsync|Info|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
2063             Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null);
2064
2065             Exception asyncException = ((Task)asyncResult).Exception;
2066             if (asyncException != null) {
2067                 // Leftover exception from the Begin...InternalReadStage
2068                 ReliablePutStateObject();
2069                 throw asyncException.InnerException;
2070             }
2071             else {
2072                 ThrowIfReconnectionHasBeenCanceled();
2073                 // lock on _stateObj prevents ----s with close/cancel.
2074                 // If we have already initiate the End call internally, we have already done that, so no point doing it again.
2075                 if (!_internalEndExecuteInitiated) {
2076                     lock (_stateObj) {
2077                         return EndExecuteReaderInternal(asyncResult);
2078                     }
2079                 }
2080                 else {
2081                     return EndExecuteReaderInternal(asyncResult);
2082                 }
2083             }
2084         }
2085
2086         private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult) {
2087             SqlStatistics statistics = null;
2088             bool success = false;
2089             int? sqlExceptionNumber = null;
2090             try {
2091                 statistics = SqlStatistics.StartTimer(Statistics);
2092                 SqlDataReader result = InternalEndExecuteReader(asyncResult, ADP.EndExecuteReader, isInternal: false);
2093                 success = true;
2094                 return result;
2095             }
2096             catch (SqlException e) {
2097                 sqlExceptionNumber = e.Number;
2098                 if (cachedAsyncState != null)
2099                 {
2100                     cachedAsyncState.ResetAsyncState();
2101                 };
2102
2103                 //  SqlException is always catchable 
2104                 ReliablePutStateObject();
2105                 throw;
2106             }
2107             catch (Exception e) {
2108                 if (cachedAsyncState != null) {
2109                     cachedAsyncState.ResetAsyncState();
2110                 };
2111                 if (ADP.IsCatchableExceptionType(e)) {
2112                     ReliablePutStateObject();
2113                 };
2114                 throw;
2115             }
2116             finally {
2117                 SqlStatistics.StopTimer(statistics);
2118                 WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous : false);
2119             }
2120         }
2121
2122         private IAsyncResult BeginExecuteReaderAsync(CommandBehavior behavior, AsyncCallback callback, object stateObject) {
2123             return BeginExecuteReaderInternal(behavior, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite:true);
2124         }
2125
2126         private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false) {
2127             TaskCompletionSource<object> globalCompletion = new TaskCompletionSource<object>(stateObject);
2128             TaskCompletionSource<object> localCompletion = new TaskCompletionSource<object>(stateObject);
2129
2130             if (!inRetry) {
2131                 // Reset _pendingCancel upon entry into any Execute - used to synchronize state
2132                 // between entry into Execute* API and the thread obtaining the stateObject.
2133                 _pendingCancel = false;
2134             }
2135
2136             SqlStatistics statistics = null;
2137             try {
2138                 if (!inRetry) {
2139                     statistics = SqlStatistics.StartTimer(Statistics);
2140                     WriteBeginExecuteEvent();
2141
2142                     ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj
2143                                             // back into pool when we should not.
2144                 }
2145                 
2146                 bool usedCache;
2147                 Task writeTask = null;
2148                 try { // InternalExecuteNonQuery already has reliability block, but if failure will not put stateObj back into pool.
2149                     RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, ADP.BeginExecuteReader, localCompletion, timeout, out writeTask, out usedCache, asyncWrite, inRetry);
2150                 }
2151                 catch (Exception e) {
2152                     if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
2153                         // If not catchable - the connection has already been caught and doomed in RunExecuteReader.
2154                         throw;
2155                     }
2156     
2157                     // For async, RunExecuteReader will never put the stateObj back into the pool, so do so now.
2158                     ReliablePutStateObject(); 
2159                     throw;
2160                 }
2161
2162                 if (writeTask != null ) {
2163                     AsyncHelper.ContinueTask(writeTask, localCompletion, () => BeginExecuteReaderInternalReadStage(localCompletion));
2164                 }
2165                 else {
2166                     BeginExecuteReaderInternalReadStage(localCompletion);
2167                 }
2168
2169                 // When we use query caching for parameter encryption we need to retry on specific errors.
2170                 // In these cases finalize the call internally and trigger a retry when needed.
2171                 if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteReader, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteReader, BeginExecuteReaderInternal)) {
2172                     globalCompletion = localCompletion;
2173                 }
2174
2175                 // Add callback after work is done to avoid overlapping Begin\End methods
2176                 if (callback != null) {
2177                     globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default);
2178                 }
2179
2180                 return globalCompletion.Task;
2181             }
2182             finally {
2183                 SqlStatistics.StopTimer(statistics);
2184             }
2185         }
2186
2187         private bool TriggerInternalEndAndRetryIfNecessary(CommandBehavior behavior, object stateObject, int timeout, string endMethod, bool usedCache, bool inRetry, bool asyncWrite, TaskCompletionSource<object> globalCompletion, TaskCompletionSource<object> localCompletion, Func<IAsyncResult, string, bool, object> endFunc, Func<CommandBehavior, AsyncCallback, object, int, bool, bool, IAsyncResult> retryFunc) {
2188             // We shouldn't be using the cache if we are in retry.
2189             Debug.Assert(!usedCache || !inRetry);
2190
2191             // If column ecnryption is enabled and we used the cache, we want to catch any potential exceptions that were caused by the query cache and retry if the error indicates that we should.
2192             // So, try to read the result of the query before completing the overall task and trigger a retry if appropriate.
2193             if ((IsColumnEncryptionEnabled && !inRetry && usedCache) 
2194 #if DEBUG
2195                 || _forceInternalEndQuery
2196 #endif
2197                 ) {
2198                 long firstAttemptStart = ADP.TimerCurrent();
2199
2200                 localCompletion.Task.ContinueWith(tsk => {
2201                     if (tsk.IsFaulted) {
2202                         globalCompletion.TrySetException(tsk.Exception.InnerException);
2203                     }
2204                     else if (tsk.IsCanceled) {
2205                         globalCompletion.TrySetCanceled();
2206                     }
2207                     else {
2208                         try {
2209                             // Mark that we initiated the internal EndExecute. This should always be false until we set it here.
2210                             Debug.Assert(!_internalEndExecuteInitiated);
2211                             _internalEndExecuteInitiated = true;
2212
2213                             // lock on _stateObj prevents ----s with close/cancel.
2214                             lock (_stateObj) {
2215                                 endFunc(tsk, endMethod, true/*inInternal*/);
2216                             }
2217                             globalCompletion.TrySetResult(tsk.Result);
2218                         }
2219                         catch (Exception e) {
2220                             // Put the state object back to the cache.
2221                             // Do not reset the async state, since this is managed by the user Begin/End and not internally.
2222                             if (ADP.IsCatchableExceptionType(e)) {
2223                                 ReliablePutStateObject();
2224                             }
2225
2226                             bool shouldRetry = false;
2227
2228                             // Check if we have an error indicating that we can retry.
2229                             if (e is SqlException) {
2230                                 SqlException sqlEx = e as SqlException;
2231
2232                                 for (int i = 0; i < sqlEx.Errors.Count; i++) {
2233                                     if (sqlEx.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY) {
2234                                         shouldRetry = true;
2235                                         break;
2236                                     }
2237                                 }
2238                             }
2239
2240                             if (!shouldRetry) {
2241                                 // If we cannot retry, Reset the async state to make sure we leave a clean state.
2242                                 if (null != _cachedAsyncState) {
2243                                     _cachedAsyncState.ResetAsyncState();
2244                                 }
2245                                 _activeConnection.GetOpenTdsConnection().DecrementAsyncCount();
2246
2247                                 globalCompletion.TrySetException(e);
2248                             }
2249                             else {
2250                                 // Remove the enrty from the cache since it was inconsistent.
2251                                 SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this);
2252
2253                                 try {
2254                                     // Kick off the retry.
2255                                     _internalEndExecuteInitiated = false;
2256                                     Task<object> retryTask = (Task<object>)retryFunc(behavior, null, stateObject, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), true/*inRetry*/, asyncWrite);
2257
2258                                     retryTask.ContinueWith(retryTsk => {
2259                                         if (retryTsk.IsFaulted) {
2260                                             globalCompletion.TrySetException(retryTsk.Exception.InnerException);
2261                                         }
2262                                         else if (retryTsk.IsCanceled) {
2263                                             globalCompletion.TrySetCanceled();
2264                                         }
2265                                         else {
2266                                             globalCompletion.TrySetResult(retryTsk.Result);
2267                                         }
2268                                     }, TaskScheduler.Default);
2269                                 }
2270                                 catch (Exception e2) {
2271                                     globalCompletion.TrySetException(e2);
2272                                 }
2273                             }
2274                         }
2275                     }
2276                 }, TaskScheduler.Default);
2277
2278                 return true;
2279             }
2280             else {
2281                 return false;
2282             }
2283         }
2284
2285         private void BeginExecuteReaderInternalReadStage(TaskCompletionSource<object> completion) {
2286             Debug.Assert(completion != null,"CompletionSource should not be null");
2287             // Read SNI does not have catches for async exceptions, handle here.
2288             TdsParser bestEffortCleanupTarget = null;
2289             RuntimeHelpers.PrepareConstrainedRegions();
2290             try {
2291 #if DEBUG
2292                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2293
2294                 RuntimeHelpers.PrepareConstrainedRegions();
2295                 try {
2296                     tdsReliabilitySection.Start();
2297 #else
2298                     {
2299 #endif //DEBUG
2300                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
2301                     // must finish caching information before ReadSni which can activate the callback before returning
2302                     cachedAsyncState.SetActiveConnectionAndResult(completion, ADP.EndExecuteReader, _activeConnection);
2303                     _stateObj.ReadSni(completion);
2304                 }
2305 #if DEBUG
2306                 finally {
2307                     tdsReliabilitySection.Stop();
2308                 }
2309 #endif //DEBUG
2310             }
2311             catch (System.OutOfMemoryException e) {
2312                 _activeConnection.Abort(e);
2313                 completion.TrySetException(e);
2314                 throw;
2315             }
2316             catch (System.StackOverflowException e) {
2317                 _activeConnection.Abort(e);
2318                 completion.TrySetException(e);
2319                 throw;
2320             }
2321             catch (System.Threading.ThreadAbortException e) {
2322                 _activeConnection.Abort(e);
2323                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
2324                 completion.TrySetException(e);
2325                 throw;
2326             }
2327             catch (Exception e) {
2328                 // Similarly, if an exception occurs put the stateObj back into the pool.
2329                 // and reset async cache information to allow a second async execute
2330                 if (null != _cachedAsyncState) {
2331                     _cachedAsyncState.ResetAsyncState();
2332                 }
2333                 ReliablePutStateObject();
2334                 completion.TrySetException(e);
2335             }
2336         }
2337
2338         private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string endMethod, bool isInternal) {
2339             
2340             VerifyEndExecuteState((Task)asyncResult, endMethod);
2341             WaitForAsyncResults(asyncResult, isInternal);
2342
2343             // If column encryption is enabled, also check the state after waiting for the task.
2344             // It would be better to do this for all cases, but avoiding for compatibility reasons.
2345             if (IsColumnEncryptionEnabled) {
2346                 VerifyEndExecuteState((Task)asyncResult, endMethod, fullCheckForColumnEncryption: true);
2347             }
2348
2349             CheckThrowSNIException();
2350
2351             TdsParser bestEffortCleanupTarget = null;
2352             RuntimeHelpers.PrepareConstrainedRegions();
2353             try {
2354 #if DEBUG
2355                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2356
2357                 RuntimeHelpers.PrepareConstrainedRegions();
2358                 try {
2359                     tdsReliabilitySection.Start();
2360 #else
2361                 {
2362 #endif //DEBUG
2363                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
2364                     SqlDataReader reader = CompleteAsyncExecuteReader(isInternal);
2365                     Debug.Assert(null == _stateObj, "non-null state object in InternalEndExecuteReader");
2366                     return reader;
2367                 }
2368 #if DEBUG
2369                 finally {
2370                     tdsReliabilitySection.Stop();
2371                 }
2372 #endif //DEBUG
2373             }
2374             catch (System.OutOfMemoryException e) {
2375                 _activeConnection.Abort(e);
2376                 throw;
2377             }
2378             catch (System.StackOverflowException e) {
2379                 _activeConnection.Abort(e);
2380                 throw;
2381             }
2382             catch (System.Threading.ThreadAbortException e)  {
2383                 _activeConnection.Abort(e);
2384                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
2385                 throw;
2386             }
2387         }
2388
2389         public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) {
2390
2391             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQueryAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
2392             SqlConnection.ExecutePermission.Demand();
2393
2394             TaskCompletionSource<int> source = new TaskCompletionSource<int>();
2395
2396             CancellationTokenRegistration registration = new CancellationTokenRegistration();
2397             if (cancellationToken.CanBeCanceled) {
2398                 if (cancellationToken.IsCancellationRequested) {
2399                     source.SetCanceled();
2400                     return source.Task;
2401                 }
2402                 registration = cancellationToken.Register(CancelIgnoreFailure);
2403             }
2404
2405             Task<int> returnedTask = source.Task;
2406             try {
2407                 RegisterForConnectionCloseNotification(ref returnedTask);
2408
2409                 Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) => {
2410                     registration.Dispose();
2411                     if (t.IsFaulted) {
2412                         Exception e = t.Exception.InnerException;
2413                         source.SetException(e);
2414                     }
2415                     else {
2416                         if (t.IsCanceled) {
2417                             source.SetCanceled();
2418                         }
2419                         else {
2420                             source.SetResult(t.Result);
2421                         }
2422                     }
2423                 }, TaskScheduler.Default);
2424             }
2425             catch (Exception e) {
2426                 source.SetException(e);
2427             }
2428
2429             return returnedTask;
2430         }
2431
2432         protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) {
2433             return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith<DbDataReader>((result) => {
2434                 if (result.IsFaulted) {
2435                     throw result.Exception.InnerException;
2436                 }
2437                 return result.Result;
2438             }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
2439         }
2440
2441         new public Task<SqlDataReader> ExecuteReaderAsync() {
2442             return ExecuteReaderAsync(CommandBehavior.Default, CancellationToken.None);
2443         }
2444
2445         new public Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior) {
2446             return ExecuteReaderAsync(behavior, CancellationToken.None);
2447         }
2448
2449         new public Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken) {
2450             return ExecuteReaderAsync(CommandBehavior.Default, cancellationToken);
2451         }
2452
2453         new public Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) {
2454
2455             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteReaderAsync|API|Correlation> ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior);
2456             SqlConnection.ExecutePermission.Demand();
2457
2458             TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>();
2459
2460             CancellationTokenRegistration registration = new CancellationTokenRegistration();
2461             if (cancellationToken.CanBeCanceled) {
2462                 if (cancellationToken.IsCancellationRequested) {
2463                     source.SetCanceled();
2464                     return source.Task;
2465                 }
2466                 registration = cancellationToken.Register(CancelIgnoreFailure);
2467             }
2468
2469             Task<SqlDataReader> returnedTask = source.Task;
2470             try {
2471                 RegisterForConnectionCloseNotification(ref returnedTask);
2472
2473                 Task<SqlDataReader>.Factory.FromAsync(BeginExecuteReaderAsync, EndExecuteReaderAsync, behavior, null).ContinueWith((t) => {
2474                     registration.Dispose();
2475                     if (t.IsFaulted) {
2476                         Exception e = t.Exception.InnerException;
2477                         source.SetException(e);
2478                     }
2479                     else {
2480                         if (t.IsCanceled) {
2481                             source.SetCanceled();
2482                         }
2483                         else {
2484                             source.SetResult(t.Result);
2485                         }
2486                     }
2487                 }, TaskScheduler.Default);
2488             }
2489             catch (Exception e) {
2490                 source.SetException(e);
2491             }
2492
2493             return returnedTask;
2494         }
2495
2496         public override Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)
2497         {
2498             return ExecuteReaderAsync(cancellationToken).ContinueWith((executeTask) => {
2499                 TaskCompletionSource<object> source = new TaskCompletionSource<object>();
2500                 if (executeTask.IsCanceled) {
2501                     source.SetCanceled();
2502                 }
2503                 else if (executeTask.IsFaulted) {
2504                     source.SetException(executeTask.Exception.InnerException);
2505                 }
2506                 else {
2507                     SqlDataReader reader = executeTask.Result;
2508                     reader.ReadAsync(cancellationToken).ContinueWith((readTask) => {
2509                         try {
2510                             if (readTask.IsCanceled) {
2511                                 reader.Dispose();
2512                                 source.SetCanceled();
2513                             }
2514                             else if (readTask.IsFaulted) {
2515                                 reader.Dispose();
2516                                 source.SetException(readTask.Exception.InnerException);
2517                             } 
2518                             else {
2519                                 Exception exception = null;
2520                                 object result = null;
2521                                 try {
2522                                     bool more = readTask.Result;
2523                                     if (more && reader.FieldCount > 0) {
2524                                         try {
2525                                             result = reader.GetValue(0);
2526                                         } 
2527                                         catch (Exception e) {
2528                                             exception = e;
2529                                         }
2530                                     }
2531                                 }
2532                                 finally {
2533                                     reader.Dispose();
2534                                 }
2535                                 if (exception != null) {
2536                                     source.SetException(exception);
2537                                 }
2538                                 else {
2539                                     source.SetResult(result);
2540                                 }
2541                             }
2542                         }
2543                         catch (Exception e) {
2544                             // exception thrown by Dispose...
2545                             source.SetException(e);
2546                         }
2547                     }, TaskScheduler.Default);
2548                 }
2549                 return source.Task;
2550             }, TaskScheduler.Default).Unwrap();
2551         }
2552
2553         public Task<XmlReader> ExecuteXmlReaderAsync() {
2554             return ExecuteXmlReaderAsync(CancellationToken.None);
2555         }
2556
2557         public Task<XmlReader> ExecuteXmlReaderAsync(CancellationToken cancellationToken) {
2558
2559             Bid.CorrelationTrace("<sc.SqlCommand.ExecuteXmlReaderAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
2560             SqlConnection.ExecutePermission.Demand();
2561
2562             TaskCompletionSource<XmlReader> source = new TaskCompletionSource<XmlReader>();
2563
2564             CancellationTokenRegistration registration = new CancellationTokenRegistration();
2565             if (cancellationToken.CanBeCanceled) {
2566                 if (cancellationToken.IsCancellationRequested) {
2567                     source.SetCanceled();
2568                     return source.Task;
2569                 }
2570                 registration = cancellationToken.Register(CancelIgnoreFailure);
2571             }
2572
2573             Task<XmlReader> returnedTask = source.Task;
2574             try {
2575                 RegisterForConnectionCloseNotification(ref returnedTask);
2576
2577                 Task<XmlReader>.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null).ContinueWith((t) => {
2578                     registration.Dispose();
2579                     if (t.IsFaulted) {
2580                         Exception e = t.Exception.InnerException;
2581                         source.SetException(e);
2582                     }
2583                     else {
2584                         if (t.IsCanceled) {
2585                             source.SetCanceled();
2586                         }
2587                         else {
2588                             source.SetResult(t.Result);
2589                         }
2590                     }
2591                 }, TaskScheduler.Default);
2592             }
2593             catch (Exception e) {
2594                 source.SetException(e);
2595             }
2596
2597             return returnedTask;
2598         }
2599
2600         // If the user part is quoted, remove first and last brackets and then unquote any right square
2601         // brackets in the procedure.  This is a very simple parser that performs no validation.  As
2602         // with the function below, ideally we should have support from the server for this.
2603         private static string UnquoteProcedurePart(string part) {
2604             if ((null != part) && (2 <= part.Length)) {
2605                 if ('[' == part[0] && ']' == part[part.Length-1]) {
2606                     part = part.Substring(1, part.Length-2); // strip outer '[' & ']'
2607                     part = part.Replace("]]", "]"); // undo quoted "]" from "]]" to "]"
2608                 }
2609             }
2610             return part;
2611         }
2612
2613         // User value in this format: [server].[database].[schema].[sp_foo];1
2614         // This function should only be passed "[sp_foo];1".
2615         // This function uses a pretty simple parser that doesn't do any validation.
2616         // Ideally, we would have support from the server rather than us having to do this.
2617         private static string UnquoteProcedureName(string name, out object groupNumber) {
2618             groupNumber  = null; // Out param - initialize value to no value.
2619             string sproc = name;
2620
2621             if (null != sproc) {
2622                 if (Char.IsDigit(sproc[sproc.Length-1])) { // If last char is a digit, parse.
2623                     int semicolon = sproc.LastIndexOf(';');
2624                     if (semicolon != -1) { // If we found a semicolon, obtain the integer.
2625                         string part   = sproc.Substring(semicolon+1);
2626                         int    number = 0;
2627                         if (Int32.TryParse(part, out number)) { // No checking, just fail if this doesn't work.
2628                             groupNumber = number;
2629                             sproc = sproc.Substring(0, semicolon);
2630                         }
2631                     }
2632                 }
2633                 sproc = UnquoteProcedurePart(sproc);
2634             }
2635             return sproc;
2636         }
2637
2638         //index into indirection arrays for columns of interest to DeriveParameters
2639         private enum ProcParamsColIndex {
2640             ParameterName = 0,
2641             ParameterType,
2642             DataType,                  // obsolete in katmai, use ManagedDataType instead
2643             ManagedDataType,          // new in katmai
2644             CharacterMaximumLength,
2645             NumericPrecision,
2646             NumericScale,
2647             TypeCatalogName,
2648             TypeSchemaName,
2649             TypeName,
2650             XmlSchemaCollectionCatalogName,
2651             XmlSchemaCollectionSchemaName,
2652             XmlSchemaCollectionName,
2653             UdtTypeName,                // obsolete in Katmai.  Holds the actual typename if UDT, since TypeName didn't back then.
2654             DateTimeScale               // new in Katmai
2655         };
2656
2657         // Yukon- column ordinals (this array indexed by ProcParamsColIndex
2658         static readonly internal string[] PreKatmaiProcParamsNames = new string[] {
2659             "PARAMETER_NAME",           // ParameterName,
2660             "PARAMETER_TYPE",           // ParameterType,
2661             "DATA_TYPE",                // DataType
2662             null,                       // ManagedDataType,     introduced in Katmai
2663             "CHARACTER_MAXIMUM_LENGTH", // CharacterMaximumLength,
2664             "NUMERIC_PRECISION",        // NumericPrecision,
2665             "NUMERIC_SCALE",            // NumericScale,
2666             "UDT_CATALOG",              // TypeCatalogName,
2667             "UDT_SCHEMA",               // TypeSchemaName,
2668             "TYPE_NAME",                // TypeName,
2669             "XML_CATALOGNAME",          // XmlSchemaCollectionCatalogName,
2670             "XML_SCHEMANAME",           // XmlSchemaCollectionSchemaName,
2671             "XML_SCHEMACOLLECTIONNAME", // XmlSchemaCollectionName
2672             "UDT_NAME",                 // UdtTypeName
2673             null,                       // Scale for datetime types with scale, introduced in Katmai
2674         };
2675
2676         // Katmai+ column ordinals (this array indexed by ProcParamsColIndex
2677         static readonly internal string[] KatmaiProcParamsNames = new string[] {
2678             "PARAMETER_NAME",           // ParameterName,
2679             "PARAMETER_TYPE",           // ParameterType,
2680             null,                       // DataType, removed from Katmai+
2681             "MANAGED_DATA_TYPE",        // ManagedDataType,
2682             "CHARACTER_MAXIMUM_LENGTH", // CharacterMaximumLength,
2683             "NUMERIC_PRECISION",        // NumericPrecision,
2684             "NUMERIC_SCALE",            // NumericScale,
2685             "TYPE_CATALOG_NAME",        // TypeCatalogName,
2686             "TYPE_SCHEMA_NAME",         // TypeSchemaName,
2687             "TYPE_NAME",                // TypeName,
2688             "XML_CATALOGNAME",          // XmlSchemaCollectionCatalogName,
2689             "XML_SCHEMANAME",           // XmlSchemaCollectionSchemaName,
2690             "XML_SCHEMACOLLECTIONNAME", // XmlSchemaCollectionName
2691             null,                       // UdtTypeName, removed from Katmai+
2692             "SS_DATETIME_PRECISION",    // Scale for datetime types with scale
2693         };
2694
2695
2696         internal void DeriveParameters() {
2697             switch (this.CommandType) {
2698                 case System.Data.CommandType.Text:
2699                     throw ADP.DeriveParametersNotSupported(this);
2700                 case System.Data.CommandType.StoredProcedure:
2701                     break;
2702                 case System.Data.CommandType.TableDirect:
2703                     // CommandType.TableDirect - do nothing, parameters are not supported
2704                     throw ADP.DeriveParametersNotSupported(this);
2705                 default:
2706                     throw ADP.InvalidCommandType(this.CommandType);
2707             }
2708
2709             // validate that we have a valid connection
2710             ValidateCommand(ADP.DeriveParameters, false /*not async*/);
2711
2712             // Use common parser for SqlClient and OleDb - parse into 4 parts - Server, Catalog, Schema, ProcedureName
2713             string[] parsedSProc = MultipartIdentifier.ParseMultipartIdentifier(this.CommandText, "[\"", "]\"", Res.SQL_SqlCommandCommandText, false);
2714             if (null == parsedSProc[3] || ADP.IsEmpty(parsedSProc[3]))
2715             {
2716                 throw ADP.NoStoredProcedureExists(this.CommandText);
2717             }
2718
2719             Debug.Assert(parsedSProc.Length == 4, "Invalid array length result from SqlCommandBuilder.ParseProcedureName");
2720
2721             SqlCommand    paramsCmd = null;
2722             StringBuilder cmdText   = new StringBuilder();
2723
2724             // Build call for sp_procedure_params_rowset built of unquoted values from user:
2725             // [user server, if provided].[user catalog, else current database].[sys if Yukon, else blank].[sp_procedure_params_rowset]
2726
2727             // Server - pass only if user provided.
2728             if (!ADP.IsEmpty(parsedSProc[0])) {
2729                 SqlCommandSet.BuildStoredProcedureName(cmdText, parsedSProc[0]);
2730                 cmdText.Append(".");
2731             }
2732
2733             // Catalog - pass user provided, otherwise use current database.
2734             if (ADP.IsEmpty(parsedSProc[1])) {
2735                 parsedSProc[1] = this.Connection.Database;
2736             }
2737             SqlCommandSet.BuildStoredProcedureName(cmdText, parsedSProc[1]);
2738             cmdText.Append(".");
2739
2740             // Schema - only if Yukon, and then only pass sys.  Also - pass managed version of sproc
2741             // for Yukon, else older sproc.
2742             string[] colNames;
2743             bool useManagedDataType;
2744             if (this.Connection.IsKatmaiOrNewer) {
2745                 // Procedure - [sp_procedure_params_managed]
2746                 cmdText.Append("[sys].[").Append(TdsEnums.SP_PARAMS_MGD10).Append("]");
2747
2748                 colNames = KatmaiProcParamsNames;
2749                 useManagedDataType = true;
2750             }
2751             else {
2752                 if (this.Connection.IsYukonOrNewer) {
2753                     // Procedure - [sp_procedure_params_managed]
2754                     cmdText.Append("[sys].[").Append(TdsEnums.SP_PARAMS_MANAGED).Append("]");
2755                 }
2756                 else {
2757                     // Procedure - [sp_procedure_params_rowset]
2758                     cmdText.Append(".[").Append(TdsEnums.SP_PARAMS).Append("]");
2759                 }
2760
2761                 colNames = PreKatmaiProcParamsNames;
2762                 useManagedDataType = false;
2763             }
2764
2765
2766             paramsCmd = new SqlCommand(cmdText.ToString(), this.Connection, this.Transaction);
2767             paramsCmd.CommandType = CommandType.StoredProcedure;
2768
2769             object groupNumber;
2770
2771             // Prepare parameters for sp_procedure_params_rowset:
2772             // 1) procedure name - unquote user value
2773             // 2) group number - parsed at the time we unquoted procedure name
2774             // 3) procedure schema - unquote user value
2775
2776             // 
2777
2778
2779
2780             paramsCmd.Parameters.Add(new SqlParameter("@procedure_name", SqlDbType.NVarChar, 255));
2781             paramsCmd.Parameters[0].Value = UnquoteProcedureName(parsedSProc[3], out groupNumber); // ProcedureName is 4rd element in parsed array
2782
2783             if (null != groupNumber) {
2784                 SqlParameter param = paramsCmd.Parameters.Add(new SqlParameter("@group_number", SqlDbType.Int));
2785                 param.Value = groupNumber;
2786             }
2787
2788             if (!ADP.IsEmpty(parsedSProc[2])) { // SchemaName is 3rd element in parsed array
2789                 SqlParameter param = paramsCmd.Parameters.Add(new SqlParameter("@procedure_schema", SqlDbType.NVarChar, 255));
2790                 param.Value = UnquoteProcedurePart(parsedSProc[2]);
2791             }
2792
2793             SqlDataReader r = null;
2794
2795             List<SqlParameter> parameters = new List<SqlParameter>();
2796             bool processFinallyBlock = true;
2797             
2798             try {
2799                 r = paramsCmd.ExecuteReader();
2800
2801                 SqlParameter p = null;
2802
2803                 while (r.Read()) {
2804                     // each row corresponds to a parameter of the stored proc.  Fill in all the info
2805             
2806                     p = new SqlParameter();
2807
2808                     // name
2809                     p.ParameterName = (string) r[colNames[(int)ProcParamsColIndex.ParameterName]];
2810
2811                     // type
2812                     if (useManagedDataType) {
2813                         p.SqlDbType = (SqlDbType)(short)r[colNames[(int)ProcParamsColIndex.ManagedDataType]];
2814
2815                         // Yukon didn't have as accurate of information as we're getting for Katmai, so re-map a couple of
2816                         //  types for backward compatability.
2817                         switch (p.SqlDbType) {
2818                             case SqlDbType.Image:
2819                             case SqlDbType.Timestamp:
2820                                 p.SqlDbType = SqlDbType.VarBinary;
2821                                 break;
2822
2823                             case SqlDbType.NText:
2824                                 p.SqlDbType = SqlDbType.NVarChar;
2825                                 break;
2826
2827                             case SqlDbType.Text:
2828                                 p.SqlDbType = SqlDbType.VarChar;
2829                                 break;
2830
2831                             default:
2832                                 break;
2833                         }
2834                     }
2835                     else {
2836                         p.SqlDbType = MetaType.GetSqlDbTypeFromOleDbType((short)r[colNames[(int)ProcParamsColIndex.DataType]], 
2837                             ADP.IsNull(r[colNames[(int)ProcParamsColIndex.TypeName]]) ? 
2838                                 ADP.StrEmpty : 
2839                                 (string)r[colNames[(int)ProcParamsColIndex.TypeName]]);
2840                     }
2841
2842                     // size
2843                     object a = r[colNames[(int)ProcParamsColIndex.CharacterMaximumLength]];
2844                     if (a is int) {
2845                         int size = (int)a;
2846
2847                         // Map MAX sizes correctly.  The Katmai server-side proc sends 0 for these instead of -1.
2848                         //  Should be fixed on the Katmai side, but would likely hold up the RI, and is safer to fix here.
2849                         //  If we can get the server-side fixed before shipping Katmai, we can remove this mapping.
2850                         if (0 == size && 
2851                                 (p.SqlDbType == SqlDbType.NVarChar ||
2852                                  p.SqlDbType == SqlDbType.VarBinary ||
2853                                  p.SqlDbType == SqlDbType.VarChar)) {
2854                             size = -1;
2855                         }
2856                         p.Size = size;
2857                     }
2858
2859                     // direction
2860                     p.Direction = ParameterDirectionFromOleDbDirection((short)r[colNames[(int)ProcParamsColIndex.ParameterType]]);
2861
2862                     if (p.SqlDbType == SqlDbType.Decimal) {
2863                         p.ScaleInternal = (byte) ((short)r[colNames[(int)ProcParamsColIndex.NumericScale]] & 0xff);
2864                         p.PrecisionInternal = (byte)((short)r[colNames[(int)ProcParamsColIndex.NumericPrecision]] & 0xff);
2865                     }
2866
2867                     // type name for Udt 
2868                     if (SqlDbType.Udt == p.SqlDbType) {
2869
2870                         Debug.Assert(this._activeConnection.IsYukonOrNewer,"Invalid datatype token received from pre-yukon server");
2871
2872                         string udtTypeName;
2873                         if (useManagedDataType) {
2874                             udtTypeName = (string)r[colNames[(int)ProcParamsColIndex.TypeName]];
2875                         }
2876                         else {
2877                             udtTypeName = (string)r[colNames[(int)ProcParamsColIndex.UdtTypeName]];
2878                         }
2879
2880                         //read the type name
2881                         p.UdtTypeName = r[colNames[(int)ProcParamsColIndex.TypeCatalogName]]+"."+
2882                             r[colNames[(int)ProcParamsColIndex.TypeSchemaName]]+"."+
2883                             udtTypeName;
2884                     }
2885
2886                     // type name for Structured types (same as for Udt's except assign p.TypeName instead of p.UdtTypeName
2887                     if (SqlDbType.Structured == p.SqlDbType) {
2888
2889                         Debug.Assert(this._activeConnection.IsKatmaiOrNewer,"Invalid datatype token received from pre-katmai server");
2890
2891                         //read the type name
2892                         p.TypeName = r[colNames[(int)ProcParamsColIndex.TypeCatalogName]]+"."+
2893                             r[colNames[(int)ProcParamsColIndex.TypeSchemaName]]+"."+
2894                             r[colNames[(int)ProcParamsColIndex.TypeName]];
2895                     }
2896
2897                     // XmlSchema name for Xml types
2898                     if (SqlDbType.Xml == p.SqlDbType) {
2899                         object value;
2900
2901                         value = r[colNames[(int)ProcParamsColIndex.XmlSchemaCollectionCatalogName]];
2902                         p.XmlSchemaCollectionDatabase = ADP.IsNull(value) ? String.Empty : (string) value;
2903
2904                         value = r[colNames[(int)ProcParamsColIndex.XmlSchemaCollectionSchemaName]];
2905                         p.XmlSchemaCollectionOwningSchema = ADP.IsNull(value) ? String.Empty : (string) value;
2906
2907                         value = r[colNames[(int)ProcParamsColIndex.XmlSchemaCollectionName]];
2908                         p.XmlSchemaCollectionName = ADP.IsNull(value) ? String.Empty : (string) value;
2909                     }
2910
2911                     if (MetaType._IsVarTime(p.SqlDbType)) {
2912                         object value = r[colNames[(int)ProcParamsColIndex.DateTimeScale]];
2913                         if (value is int) {
2914                             p.ScaleInternal = (byte)(((int)value) & 0xff);
2915                         }
2916                     }
2917
2918                     parameters.Add(p);
2919                 }
2920             }
2921             catch (Exception e) {
2922                 processFinallyBlock = ADP.IsCatchableExceptionType(e);
2923                 throw;
2924             }
2925             finally {
2926                 TdsParser.ReliabilitySection.Assert("unreliable call to DeriveParameters");  // you need to setup for a thread abort somewhere before you call this method
2927                 if (processFinallyBlock) {
2928                     if (null != r)
2929                         r.Close();
2930
2931                     // always unhook the user's connection
2932                     paramsCmd.Connection = null;
2933                 }
2934             }
2935
2936             if (parameters.Count == 0) {
2937                 throw ADP.NoStoredProcedureExists(this.CommandText);
2938             }
2939
2940             this.Parameters.Clear();
2941
2942             foreach (SqlParameter temp in parameters) {
2943                 this._parameters.Add(temp);
2944             }
2945         }
2946
2947         private ParameterDirection ParameterDirectionFromOleDbDirection(short oledbDirection) {
2948             Debug.Assert(oledbDirection >= 1 && oledbDirection <= 4, "invalid parameter direction from params_rowset!");
2949
2950             switch (oledbDirection) {
2951                 case 2:
2952                     return ParameterDirection.InputOutput;
2953                 case 3:
2954                     return ParameterDirection.Output;
2955                 case 4:
2956                     return ParameterDirection.ReturnValue;
2957                 default:
2958                     return ParameterDirection.Input;
2959             }
2960
2961         }
2962
2963         // get cached metadata
2964         internal _SqlMetaDataSet MetaData {
2965             get {
2966                 return _cachedMetaData;
2967             }
2968         }
2969
2970         // Check to see if notificactions auto enlistment is turned on. Enlist if so.
2971         private void CheckNotificationStateAndAutoEnlist() {
2972             // First, if auto-enlist is on, check server version and then obtain context if
2973             // present.  If so, auto enlist to the dependency ID given in the context data.
2974             if (NotificationAutoEnlist) {
2975                 if (_activeConnection.IsYukonOrNewer) { // Only supported for Yukon...
2976                     string notifyContext = SqlNotificationContext();
2977                     if (!ADP.IsEmpty(notifyContext)) {
2978                         // Map to dependency by ID set in context data.
2979                         SqlDependency dependency = SqlDependencyPerAppDomainDispatcher.SingletonInstance.LookupDependencyEntry(notifyContext);
2980
2981                         if (null != dependency) {
2982                             // Add this command to the dependency.
2983                             dependency.AddCommandDependency(this);
2984                         }
2985                     }
2986                 }
2987             }
2988
2989             // If we have a notification with a dependency, setup the notification options at this time.
2990
2991             // If user passes options, then we will always have option data at the time the SqlDependency
2992             // ctor is called.  But, if we are using default queue, then we do not have this data until
2993             // Start().  Due to this, we always delay setting options until execute.
2994
2995             // There is a variance in order between Start(), SqlDependency(), and Execute.  This is the 
2996             // best way to solve that problem.
2997             if (null != Notification) {
2998                 if (_sqlDep != null) {
2999                     if (null == _sqlDep.Options) { 
3000                         // If null, SqlDependency was not created with options, so we need to obtain default options now.
3001                         // GetDefaultOptions can and will throw under certain conditions.
3002
3003                         // In order to match to the appropriate start - we need 3 pieces of info:
3004                         // 1) server 2) user identity (SQL Auth or Int Sec) 3) database
3005
3006                         SqlDependency.IdentityUserNamePair identityUserName = null;
3007
3008                         // Obtain identity from connection.
3009                         SqlInternalConnectionTds internalConnection = _activeConnection.InnerConnection as SqlInternalConnectionTds;
3010                         if (internalConnection.Identity != null) {
3011                             identityUserName = new SqlDependency.IdentityUserNamePair(internalConnection.Identity, null);
3012                         }
3013                         else {
3014                             identityUserName = new SqlDependency.IdentityUserNamePair(null, internalConnection.ConnectionOptions.UserID);
3015                         }
3016
3017                         Notification.Options = SqlDependency.GetDefaultComposedOptions(_activeConnection.DataSource,
3018                                                              InternalTdsConnection.ServerProvidedFailOverPartner,
3019                                                              identityUserName, _activeConnection.Database);
3020                     }
3021
3022                     // Set UserData on notifications, as well as adding to the appdomain dispatcher.  The value is
3023                     // computed by an algorithm on the dependency - fixed and will always produce the same value
3024                     // given identical commandtext + parameter values.
3025                     Notification.UserData = _sqlDep.ComputeHashAndAddToDispatcher(this);
3026                     // Maintain server list for SqlDependency.
3027                     _sqlDep.AddToServerList(_activeConnection.DataSource);
3028                 }
3029             }   
3030         }
3031
3032         [System.Security.Permissions.SecurityPermission(SecurityAction.Assert, Infrastructure=true)]
3033         static internal string SqlNotificationContext() {
3034             SqlConnection.VerifyExecutePermission();
3035
3036             // since this information is protected, follow it so that it is not exposed to the user.
3037             // SQLBU 329633, SQLBU 329637
3038             return (System.Runtime.Remoting.Messaging.CallContext.GetData("MS.SqlDependencyCookie") as string);
3039         }
3040
3041         // Tds-specific logic for ExecuteNonQuery run handling
3042         private Task RunExecuteNonQueryTds(string methodName, bool async, int timeout, bool asyncWrite ) {
3043             Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async");
3044             bool processFinallyBlock = true;
3045             try {
3046
3047                 Task reconnectTask = _activeConnection.ValidateAndReconnect(null, timeout);
3048
3049                 if (reconnectTask != null) {
3050                     long reconnectionStart = ADP.TimerCurrent();
3051                     if (async) {
3052                         TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
3053                         _activeConnection.RegisterWaitingForReconnect(completion.Task);
3054                         _reconnectionCompletionSource = completion;
3055                         CancellationTokenSource timeoutCTS = new CancellationTokenSource();
3056                         AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token);                        
3057                         AsyncHelper.ContinueTask(reconnectTask, completion,
3058                             () => {                               
3059                                 if (completion.Task.IsCompleted) {
3060                                     return;
3061                                 }                                
3062                                 Interlocked.CompareExchange(ref _reconnectionCompletionSource, null, completion);
3063                                 timeoutCTS.Cancel();
3064                                 Task subTask = RunExecuteNonQueryTds(methodName, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart), asyncWrite);
3065                                 if (subTask == null) {
3066                                     completion.SetResult(null);
3067                                 }
3068                                 else {
3069                                     AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null));
3070                                 }
3071                             }, connectionToAbort: _activeConnection);
3072                         return completion.Task;
3073                     }
3074                     else {
3075                         AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); });
3076                         timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart);
3077                     }
3078                 }
3079
3080                 if (asyncWrite) {
3081                     _activeConnection.AddWeakReference(this, SqlReferenceCollection.CommandTag);
3082                 }
3083
3084                 GetStateObject();
3085
3086                 // Reset the encryption state in case it has been set by a previous command.
3087                 ResetEncryptionState();
3088
3089                 // we just send over the raw text with no annotation
3090                 // no parameters are sent over
3091                 // no data reader is returned
3092                 // use this overload for "batch SQL" tds token type
3093                 Bid.Trace("<sc.SqlCommand.ExecuteNonQuery|INFO> %d#, Command executed as SQLBATCH.\n", ObjectID);
3094                 Task executeTask = _stateObj.Parser.TdsExecuteSQLBatch(this.CommandText, timeout, this.Notification, _stateObj, sync: true);
3095                 Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes");
3096
3097                 NotifyDependency();
3098                 if (async) {
3099                     _activeConnection.GetOpenTdsConnection(methodName).IncrementAsyncCount();
3100                 }
3101                 else {
3102                     bool dataReady;
3103                     Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
3104                     bool result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, null, null, _stateObj, out dataReady);
3105                     if (!result) { throw SQL.SynchronousCallMayNotPend(); }
3106                 }
3107             }
3108             catch (Exception e) {
3109                 processFinallyBlock = ADP.IsCatchableExceptionType(e);
3110                 throw;
3111             }
3112             finally {
3113                 TdsParser.ReliabilitySection.Assert("unreliable call to RunExecuteNonQueryTds");  // you need to setup for a thread abort somewhere before you call this method
3114                 if (processFinallyBlock && !async) {
3115                     // When executing Async, we need to keep the _stateObj alive...
3116                     PutStateObject();
3117                 }
3118             }
3119             return null;
3120         }
3121
3122         // Smi-specific logic for ExecuteNonQuery
3123         private void RunExecuteNonQuerySmi( bool sendToPipe ) {
3124             SqlInternalConnectionSmi innerConnection = InternalSmiConnection;
3125
3126             // Set it up, process all of the events, and we're done!
3127             SmiRequestExecutor requestExecutor = null;
3128             try {
3129                 requestExecutor = SetUpSmiRequest(innerConnection);
3130                 SmiExecuteType execType;
3131                 if ( sendToPipe )
3132                     execType = SmiExecuteType.ToPipe;
3133                 else
3134                     execType = SmiExecuteType.NonQuery;
3135
3136
3137                 SmiEventStream eventStream = null;
3138                 // Don't need a CER here because caller already has one that will doom the
3139                 //  connection if it's a finally-skipping type of problem.
3140                 bool processFinallyBlock = true;
3141                 try {
3142                     long transactionId;
3143                     SysTx.Transaction transaction;
3144                     innerConnection.GetCurrentTransactionPair(out transactionId, out transaction);
3145
3146                     if (Bid.AdvancedOn) {
3147                         Bid.Trace("<sc.SqlCommand.RunExecuteNonQuerySmi|ADV> %d#, innerConnection=%d#, transactionId=0x%I64x, cmdBehavior=%d.\n", ObjectID, innerConnection.ObjectID, transactionId, (int)CommandBehavior.Default);
3148                     }
3149
3150                     if (SmiContextFactory.Instance.NegotiatedSmiVersion >= SmiContextFactory.KatmaiVersion) {
3151                         eventStream = requestExecutor.Execute(
3152                                                           innerConnection.SmiConnection,
3153                                                           transactionId,
3154                                                           transaction,
3155                                                           CommandBehavior.Default,
3156                                                           execType);
3157                     }
3158                     else {
3159                         eventStream = requestExecutor.Execute(
3160                                                           innerConnection.SmiConnection,
3161                                                           transactionId,
3162                                                           CommandBehavior.Default,
3163                                                           execType);
3164                     }
3165
3166                     while ( eventStream.HasEvents ) {
3167                         eventStream.ProcessEvent( EventSink );
3168                     }
3169                 }
3170                 catch (Exception e) {
3171                     processFinallyBlock = ADP.IsCatchableExceptionType(e);
3172                     throw;
3173                 }
3174                 finally {
3175                     TdsParser.ReliabilitySection.Assert("unreliable call to RunExecuteNonQuerySmi");  // you need to setup for a thread abort somewhere before you call this method
3176                     if (null != eventStream && processFinallyBlock) {
3177                         eventStream.Close( EventSink );
3178                     }
3179                 }
3180
3181                 EventSink.ProcessMessagesAndThrow();
3182             }
3183             finally {
3184                 if (requestExecutor != null) {
3185                     requestExecutor.Close(EventSink);
3186                     EventSink.ProcessMessagesAndThrow(ignoreNonFatalMessages: true);
3187                 }
3188             }
3189         }
3190
3191         /// <summary>
3192         /// Resets the encryption related state of the command object and each of the parameters.
3193         /// BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and 
3194         /// parameters are generated on every execution.
3195         /// </summary>
3196         private void ResetEncryptionState() {
3197             // First reset the command level state.
3198             ClearDescribeParameterEncryptionRequests();
3199
3200             // Reset the state for internal End execution.
3201             _internalEndExecuteInitiated = false;
3202
3203             // Reset the state for the cache.
3204             CachingQueryMetadataPostponed = false;
3205
3206             // Reset the state of each of the parameters.
3207             if (_parameters != null) {
3208                 for (int i = 0; i < _parameters.Count; i++) {
3209                     _parameters[i].CipherMetadata = null;
3210                     _parameters[i].HasReceivedMetadata = false;
3211                 }
3212             }
3213         }
3214
3215         /// <summary>
3216         /// Steps to be executed in the Prepare Transparent Encryption finally block.
3217         /// </summary>
3218         private void PrepareTransparentEncryptionFinallyBlock(  bool closeDataReader,
3219                                                                 bool clearDataStructures,
3220                                                                 bool decrementAsyncCount,
3221                                                                 bool wasDescribeParameterEncryptionNeeded,
3222                                                                 ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap,
3223                                                                 SqlDataReader describeParameterEncryptionDataReader) {
3224             if (clearDataStructures) {
3225                 // Clear some state variables in SqlCommand that reflect in-progress describe parameter encryption requests.
3226                 ClearDescribeParameterEncryptionRequests();
3227
3228                 if (describeParameterEncryptionRpcOriginalRpcMap != null) {
3229                     describeParameterEncryptionRpcOriginalRpcMap = null;
3230                 }
3231             }
3232
3233             // Decrement the async count.
3234             if (decrementAsyncCount) {
3235                 SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection();
3236                 if (internalConnectionTds != null) {
3237                     internalConnectionTds.DecrementAsyncCount();
3238                 }
3239             }
3240
3241             if (closeDataReader) {
3242                 // Close the data reader to reset the _stateObj
3243                 if (null != describeParameterEncryptionDataReader) {
3244                     describeParameterEncryptionDataReader.Close();
3245                 }
3246             }
3247         }
3248
3249         /// <summary>
3250         /// Executes the reader after checking to see if we need to encrypt input parameters and then encrypting it if required.
3251         /// TryFetchInputParameterEncryptionInfo() -> ReadDescribeEncryptionParameterResults()-> EncryptInputParameters() ->RunExecuteReaderTds()
3252         /// </summary>
3253         /// <param name="cmdBehavior"></param>
3254         /// <param name="returnStream"></param>
3255         /// <param name="async"></param>
3256         /// <param name="timeout"></param>
3257         /// <param name="task"></param>
3258         /// <param name="asyncWrite"></param>
3259         /// <returns></returns>
3260         private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool returnStream, bool async, int timeout, TaskCompletionSource<object> completion, out Task returnTask, bool asyncWrite, out bool usedCache, bool inRetry) {
3261             // Fetch reader with input params
3262             Task fetchInputParameterEncryptionInfoTask = null;
3263             bool describeParameterEncryptionNeeded = false;
3264             SqlDataReader describeParameterEncryptionDataReader = null;
3265             returnTask = null;
3266             usedCache = false;
3267
3268             Debug.Assert(_activeConnection != null, "_activeConnection should not be null in PrepareForTransparentEncryption.");
3269             Debug.Assert(_activeConnection.Parser != null, "_activeConnection.Parser should not be null in PrepareForTransparentEncryption.");
3270             Debug.Assert(_activeConnection.Parser.IsColumnEncryptionSupported,
3271                 "_activeConnection.Parser.IsColumnEncryptionSupported should be true in PrepareForTransparentEncryption.");
3272             Debug.Assert(_columnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled
3273                         || (_columnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && _activeConnection.IsColumnEncryptionSettingEnabled),
3274                         "ColumnEncryption setting should be enabled for input parameter encryption.");
3275             Debug.Assert(async == (completion != null), "completion should can be null if and only if mode is async.");
3276
3277             // If we are not in Batch RPC and not already retrying, attempt to fetch the cipher MD for each parameter from the cache.
3278             // If this succeeds then return immediately, otherwise just fall back to the full crypto MD discovery.
3279             if (!BatchRPCMode && !inRetry && SqlQueryMetadataCache.GetInstance().GetQueryMetadataIfExists(this)) {
3280                 usedCache = true;
3281                 return;
3282             }
3283
3284             // A flag to indicate if finallyblock needs to execute.
3285             bool processFinallyBlock = true;
3286
3287             // A flag to indicate if we need to decrement async count on the connection in finally block.
3288             bool decrementAsyncCountInFinallyBlock = false;
3289
3290             // Flag to indicate if exception is caught during the execution, to govern clean up.
3291             bool exceptionCaught = false;
3292
3293             // Used in BatchRPCMode to maintain a map of describe parameter encryption RPC requests (Keys) and their corresponding original RPC requests (Values).
3294             ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap = null;
3295
3296             TdsParser bestEffortCleanupTarget = null;
3297             RuntimeHelpers.PrepareConstrainedRegions();
3298             try {
3299 #if DEBUG
3300                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3301
3302                 RuntimeHelpers.PrepareConstrainedRegions();
3303                 try {
3304                     tdsReliabilitySection.Start();
3305 #else
3306                 {
3307 #endif //DEBUG
3308                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
3309                     try {
3310                         // Fetch the encryption information that applies to any of the input parameters.
3311                         describeParameterEncryptionDataReader = TryFetchInputParameterEncryptionInfo(timeout,
3312                                                                                                      async,
3313                                                                                                      asyncWrite,
3314                                                                                                      out describeParameterEncryptionNeeded,
3315                                                                                                      out fetchInputParameterEncryptionInfoTask,
3316                                                                                                      out describeParameterEncryptionRpcOriginalRpcMap);
3317
3318                         Debug.Assert(describeParameterEncryptionNeeded || describeParameterEncryptionDataReader == null,
3319                             "describeParameterEncryptionDataReader should be null if we don't need to request describe parameter encryption request.");
3320
3321                         Debug.Assert(fetchInputParameterEncryptionInfoTask == null || async,
3322                             "Task returned by TryFetchInputParameterEncryptionInfo, when in sync mode, in PrepareForTransparentEncryption.");
3323
3324                         Debug.Assert((describeParameterEncryptionRpcOriginalRpcMap != null) == BatchRPCMode,
3325                             "describeParameterEncryptionRpcOriginalRpcMap can be non-null if and only if it is in BatchRPCMode.");
3326
3327                         // If we didn't have parameters, we can fall back to regular code path, by simply returning.
3328                         if (!describeParameterEncryptionNeeded) {
3329                             Debug.Assert(null == fetchInputParameterEncryptionInfoTask,
3330                                 "fetchInputParameterEncryptionInfoTask should not be set if describe parameter encryption is not needed.");
3331
3332                             Debug.Assert(null == describeParameterEncryptionDataReader,
3333                                 "SqlDataReader created for describe parameter encryption params when it is not needed.");
3334
3335                             return;
3336                         }
3337
3338                         // If we are in async execution, we need to decrement our async count on exception.
3339                         decrementAsyncCountInFinallyBlock = async;
3340
3341                         Debug.Assert(describeParameterEncryptionDataReader != null,
3342                             "describeParameterEncryptionDataReader should not be null, as it is required to get results of describe parameter encryption.");
3343
3344                         // Fire up another task to read the results of describe parameter encryption
3345                         if (fetchInputParameterEncryptionInfoTask != null) {
3346                             // Mark that we should not process the finally block since we have async execution pending.
3347                             // Note that this should be done outside the task's continuation delegate.
3348                             processFinallyBlock = false;
3349                             returnTask = AsyncHelper.CreateContinuationTask(fetchInputParameterEncryptionInfoTask, () => {
3350                                 bool processFinallyBlockAsync = true;
3351                                 bool decrementAsyncCountInFinallyBlockAsync = true;
3352
3353                                 RuntimeHelpers.PrepareConstrainedRegions();
3354                                 try {
3355 #if DEBUG
3356                                     TdsParser.ReliabilitySection tdsReliabilitySectionAsync = new TdsParser.ReliabilitySection();
3357                                     RuntimeHelpers.PrepareConstrainedRegions();
3358                                     try {
3359                                         tdsReliabilitySectionAsync.Start();
3360 #endif //DEBUG
3361                                         // Check for any exceptions on network write, before reading.
3362                                         CheckThrowSNIException();
3363
3364                                         // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count.
3365                                         // Decrement it when we are about to complete async execute reader.
3366                                         SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection();
3367                                         if (internalConnectionTds != null) {
3368                                             internalConnectionTds.DecrementAsyncCount();
3369                                             decrementAsyncCountInFinallyBlockAsync = false;
3370                                         }
3371
3372                                         // Complete executereader.
3373                                         describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true);
3374                                         Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption.");
3375
3376                                         // Read the results of describe parameter encryption.
3377                                         ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap);
3378
3379 #if DEBUG
3380                                         // Failpoint to force the thread to halt to simulate cancellation of SqlCommand.
3381                                         if (_sleepAfterReadDescribeEncryptionParameterResults) {
3382                                             Thread.Sleep(10000);
3383                                         }
3384                                     }
3385                                     finally {
3386                                         tdsReliabilitySectionAsync.Stop();
3387                                     }
3388 #endif //DEBUG
3389                                 }
3390                                 catch (Exception e) {
3391                                     processFinallyBlockAsync = ADP.IsCatchableExceptionType(e);
3392                                     throw;
3393                                 }
3394                                 finally {
3395                                     PrepareTransparentEncryptionFinallyBlock(   closeDataReader: processFinallyBlockAsync,
3396                                                                                 decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync,
3397                                                                                 clearDataStructures: processFinallyBlockAsync,
3398                                                                                 wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded,
3399                                                                                 describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap,
3400                                                                                 describeParameterEncryptionDataReader: describeParameterEncryptionDataReader);
3401                                 }
3402                             }, 
3403                             onFailure: ((exception) => {
3404                             if (_cachedAsyncState != null) {
3405                                 _cachedAsyncState.ResetAsyncState();
3406                             }
3407                             if (exception != null) {
3408                                 throw exception;
3409                             }}));
3410
3411                             decrementAsyncCountInFinallyBlock = false;
3412                         }
3413                         else {
3414                             // If it was async, ending the reader is still pending.
3415                             if (async) {
3416                                 // Mark that we should not process the finally block since we have async execution pending.
3417                                 // Note that this should be done outside the task's continuation delegate.
3418                                 processFinallyBlock = false;
3419                                 returnTask = Task.Run(() => {
3420                                         bool processFinallyBlockAsync = true;
3421                                         bool decrementAsyncCountInFinallyBlockAsync = true;
3422
3423                                         RuntimeHelpers.PrepareConstrainedRegions();
3424                                         try {
3425 #if DEBUG
3426                                             TdsParser.ReliabilitySection tdsReliabilitySectionAsync = new TdsParser.ReliabilitySection();
3427                                             RuntimeHelpers.PrepareConstrainedRegions();
3428                                             try {
3429                                                 tdsReliabilitySectionAsync.Start();
3430 #endif //DEBUG
3431
3432                                                 // Check for any exceptions on network write, before reading.
3433                                                 CheckThrowSNIException();
3434
3435                                                 // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count.
3436                                                 // Decrement it when we are about to complete async execute reader.
3437                                                 SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection();
3438                                                 if (internalConnectionTds != null) {
3439                                                     internalConnectionTds.DecrementAsyncCount();
3440                                                     decrementAsyncCountInFinallyBlockAsync = false;
3441                                                 }
3442
3443                                                 // Complete executereader.
3444                                                 describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true);
3445                                                 Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption.");
3446
3447                                                 // Read the results of describe parameter encryption.
3448                                                 ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap);
3449 #if DEBUG
3450                                                 // Failpoint to force the thread to halt to simulate cancellation of SqlCommand.
3451                                                 if (_sleepAfterReadDescribeEncryptionParameterResults) {
3452                                                     Thread.Sleep(10000);
3453                                                 }
3454 #endif
3455 #if DEBUG
3456                                             }
3457                                             finally {
3458                                                 tdsReliabilitySectionAsync.Stop();
3459                                             }
3460 #endif //DEBUG
3461                                         }
3462                                         catch (Exception e) {
3463                                             processFinallyBlockAsync = ADP.IsCatchableExceptionType(e);
3464                                             throw;
3465                                         }
3466                                         finally {
3467                                             PrepareTransparentEncryptionFinallyBlock(   closeDataReader: processFinallyBlockAsync,
3468                                                                                         decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync,
3469                                                                                         clearDataStructures: processFinallyBlockAsync,
3470                                                                                         wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded,
3471                                                                                         describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap,
3472                                                                                         describeParameterEncryptionDataReader: describeParameterEncryptionDataReader);
3473                                         }
3474                                     });
3475
3476                                 decrementAsyncCountInFinallyBlock = false;
3477                             }
3478                             else {
3479                                 // For synchronous execution, read the results of describe parameter encryption here.
3480                                 ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap);
3481                             }
3482
3483 #if DEBUG
3484                             // Failpoint to force the thread to halt to simulate cancellation of SqlCommand.
3485                             if (_sleepAfterReadDescribeEncryptionParameterResults) {
3486                                 Thread.Sleep(10000);
3487                             }
3488 #endif
3489                         }
3490                     }
3491                     catch (Exception e) {
3492                         processFinallyBlock = ADP.IsCatchableExceptionType(e);
3493                         exceptionCaught = true;
3494                         throw;
3495                     }
3496                     finally {
3497                         // Free up the state only for synchronous execution. For asynchronous execution, free only if there was an exception.
3498                         PrepareTransparentEncryptionFinallyBlock(closeDataReader: (processFinallyBlock &&  !async) || exceptionCaught,
3499                                                decrementAsyncCount: decrementAsyncCountInFinallyBlock && exceptionCaught,
3500                                                clearDataStructures: (processFinallyBlock && !async) || exceptionCaught,
3501                                                wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded,
3502                                                describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap,
3503                                                describeParameterEncryptionDataReader: describeParameterEncryptionDataReader);
3504                     }
3505                 }
3506 #if DEBUG
3507                 finally {
3508                     tdsReliabilitySection.Stop();
3509                 }
3510 #endif //DEBUG
3511             }
3512             catch (System.OutOfMemoryException e) {
3513                 _activeConnection.Abort(e);
3514                 throw;
3515             }
3516             catch (System.StackOverflowException e) {
3517                 _activeConnection.Abort(e);
3518                 throw;
3519             }
3520             catch (System.Threading.ThreadAbortException e) {
3521                 _activeConnection.Abort(e);
3522                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
3523                 throw;
3524             }
3525             catch (Exception e) {
3526                 if (cachedAsyncState != null) {
3527                     cachedAsyncState.ResetAsyncState();
3528                 }
3529
3530                 if (ADP.IsCatchableExceptionType(e)) {
3531                     ReliablePutStateObject();
3532                 }
3533
3534                 throw;
3535             }
3536         }
3537
3538         /// <summary>
3539         /// Executes an RPC to fetch param encryption info from SQL Engine. If this method is not done writing
3540         ///  the request to wire, it'll set the "task" parameter which can be used to create continuations.
3541         /// </summary>
3542         /// <param name="timeout"></param>
3543         /// <param name="async"></param>
3544         /// <param name="asyncWrite"></param>
3545         /// <param name="inputParameterEncryptionNeeded"></param>
3546         /// <param name="task"></param>
3547         /// <param name="describeParameterEncryptionRpcOriginalRpcMap"></param>
3548         /// <returns></returns>
3549         private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout,
3550                                                                    bool async,
3551                                                                    bool asyncWrite,
3552                                                                    out bool inputParameterEncryptionNeeded,
3553                                                                    out Task task,
3554                                                                    out ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) {
3555             inputParameterEncryptionNeeded = false;
3556             task = null;
3557             describeParameterEncryptionRpcOriginalRpcMap = null;
3558
3559             if (BatchRPCMode) {
3560                 // Count the rpc requests that need to be transparently encrypted
3561                 // We simply look for any parameters in a request and add the request to be queried for parameter encryption
3562                 Dictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcDictionary = new Dictionary<_SqlRPC, _SqlRPC>();
3563
3564                 for (int i = 0; i < _SqlRPCBatchArray.Length; i++) {
3565                     // In BatchRPCMode, the actual T-SQL query is in the first parameter and not present as the rpcName, as is the case with non-BatchRPCMode.
3566                     // So input parameters start at parameters[1]. parameters[0] is the actual T-SQL Statement. rpcName is sp_executesql.
3567                     if (_SqlRPCBatchArray[i].parameters.Length > 1) {
3568                         _SqlRPCBatchArray[i].needsFetchParameterEncryptionMetadata = true;
3569
3570                         // Since we are going to need multiple RPC objects, allocate a new one here for each command in the batch.
3571                         _SqlRPC rpcDescribeParameterEncryptionRequest = new _SqlRPC();
3572
3573                         // Prepare the describe parameter encryption request.
3574                         PrepareDescribeParameterEncryptionRequest(_SqlRPCBatchArray[i], ref rpcDescribeParameterEncryptionRequest);
3575                         Debug.Assert(rpcDescribeParameterEncryptionRequest != null, "rpcDescribeParameterEncryptionRequest should not be null, after call to PrepareDescribeParameterEncryptionRequest.");
3576
3577                         Debug.Assert(!describeParameterEncryptionRpcOriginalRpcDictionary.ContainsKey(rpcDescribeParameterEncryptionRequest),
3578                             "There should not already be a key referring to the current rpcDescribeParameterEncryptionRequest, in the dictionary describeParameterEncryptionRpcOriginalRpcDictionary.");
3579
3580                         // Add the describe parameter encryption RPC request as the key and its corresponding original rpc request to the dictionary.
3581                         describeParameterEncryptionRpcOriginalRpcDictionary.Add(rpcDescribeParameterEncryptionRequest, _SqlRPCBatchArray[i]);
3582                     }
3583                 }
3584
3585                 describeParameterEncryptionRpcOriginalRpcMap = new ReadOnlyDictionary<_SqlRPC, _SqlRPC>(describeParameterEncryptionRpcOriginalRpcDictionary);
3586
3587                 if (describeParameterEncryptionRpcOriginalRpcMap.Count == 0) {
3588                     // If no parameters are present, nothing to do, simply return.
3589                     return null;
3590                 }
3591                 else {
3592                     inputParameterEncryptionNeeded = true;
3593                 }
3594
3595                 _sqlRPCParameterEncryptionReqArray = describeParameterEncryptionRpcOriginalRpcMap.Keys.ToArray();
3596
3597                 Debug.Assert(_sqlRPCParameterEncryptionReqArray.Length > 0, "There should be at-least 1 describe parameter encryption rpc request.");
3598                 Debug.Assert(_sqlRPCParameterEncryptionReqArray.Length <= _SqlRPCBatchArray.Length,
3599                                 "The number of decribe parameter encryption RPC requests is more than the number of original RPC requests.");
3600             }
3601             else if (0 != GetParameterCount(_parameters)) {
3602                 // Fetch params for a single batch
3603                 inputParameterEncryptionNeeded = true;
3604                 _sqlRPCParameterEncryptionReqArray = new _SqlRPC[1];
3605
3606                 _SqlRPC rpc = null;
3607                 GetRPCObject(_parameters.Count, ref rpc);
3608                 Debug.Assert(rpc != null, "GetRPCObject should not return rpc as null.");
3609
3610                 rpc.rpcName = CommandText;
3611
3612                 int i = 0;
3613                 foreach (SqlParameter sqlParam in _parameters) {
3614                     rpc.parameters[i++] = sqlParam;
3615                 }
3616
3617                 // Prepare the RPC request for describe parameter encryption procedure.
3618                 PrepareDescribeParameterEncryptionRequest(rpc, ref _sqlRPCParameterEncryptionReqArray[0]);
3619                 Debug.Assert(_sqlRPCParameterEncryptionReqArray[0] != null, "_sqlRPCParameterEncryptionReqArray[0] should not be null, after call to PrepareDescribeParameterEncryptionRequest.");
3620             }
3621
3622             if (inputParameterEncryptionNeeded) {
3623                 // Set the flag that indicates that parameter encryption requests are currently in-progress.
3624                 _isDescribeParameterEncryptionRPCCurrentlyInProgress = true;
3625
3626 #if DEBUG
3627                 // Failpoint to force the thread to halt to simulate cancellation of SqlCommand.
3628                 if (_sleepDuringTryFetchInputParameterEncryptionInfo) {
3629                     Thread.Sleep(10000);
3630                 }
3631 #endif
3632
3633                 // Execute the RPC.
3634                 return RunExecuteReaderTds( CommandBehavior.Default,
3635                                             runBehavior: RunBehavior.ReturnImmediately, // Other RunBehavior modes will skip reading rows.
3636                                             returnStream: true,
3637                                             async: async,
3638                                             timeout: timeout,
3639                                             task: out task,
3640                                             asyncWrite: asyncWrite,
3641                                             inRetry: false,
3642                                             ds: null,
3643                                             describeParameterEncryptionRequest: true);
3644             }
3645             else {
3646                 return null;
3647             }
3648         }
3649
3650         /// <summary>
3651         /// Constructs a SqlParameter with a given string value
3652         /// </summary>
3653         /// <param name="queryText"></param>
3654         /// <returns></returns>
3655         private SqlParameter GetSqlParameterWithQueryText(string queryText)
3656         {
3657             SqlParameter sqlParam = new SqlParameter(null, ((queryText.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, queryText.Length);
3658             sqlParam.Value = queryText;
3659
3660             return sqlParam;
3661         }
3662
3663         /// <summary>
3664         /// Constructs the sp_describe_parameter_encryption request with the values from the original RPC call.
3665         /// Prototype for <sp_describe_parameter_encryption> is 
3666         /// exec sp_describe_parameter_encryption @tsql=N'[SQL Statement]', @params=N'@p1 varbinary(256)'
3667         /// </summary>
3668         /// <param name="originalRpcRequest">Original RPC request</param>
3669         /// <param name="describeParameterEncryptionRequest">sp_describe_parameter_encryption request being built</param>
3670         private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcRequest, ref _SqlRPC describeParameterEncryptionRequest) {
3671             Debug.Assert(originalRpcRequest != null);
3672
3673             // Construct the RPC request for sp_describe_parameter_encryption
3674             // sp_describe_parameter_encryption always has 2 parameters (stmt, paramlist).
3675             GetRPCObject(2, ref describeParameterEncryptionRequest, forSpDescribeParameterEncryption:true);
3676             describeParameterEncryptionRequest.rpcName = "sp_describe_parameter_encryption";
3677
3678             // Prepare @tsql parameter
3679             SqlParameter sqlParam;
3680             string text;
3681
3682             // In BatchRPCMode, The actual T-SQL query is in the first parameter and not present as the rpcName, as is the case with non-BatchRPCMode.
3683             if (BatchRPCMode) {
3684                 Debug.Assert(originalRpcRequest.parameters != null && originalRpcRequest.parameters.Length > 0,
3685                     "originalRpcRequest didn't have at-least 1 parameter in BatchRPCMode, in PrepareDescribeParameterEncryptionRequest.");
3686                 text = (string)originalRpcRequest.parameters[0].Value;
3687                 sqlParam = GetSqlParameterWithQueryText(text);
3688             }
3689             else {
3690                 text = originalRpcRequest.rpcName;
3691                 if (CommandType == Data.CommandType.StoredProcedure) {
3692                     // For stored procedures, we need to prepare @tsql in the following format
3693                     // N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN'
3694                     sqlParam = BuildStoredProcedureStatementForColumnEncryption(text, originalRpcRequest.parameters);
3695                 }
3696                 else {
3697                     sqlParam = GetSqlParameterWithQueryText(text);
3698                 }
3699             }
3700
3701             Debug.Assert(text != null, "@tsql parameter is null in PrepareDescribeParameterEncryptionRequest.");
3702
3703             describeParameterEncryptionRequest.parameters[0] = sqlParam;
3704             string parameterList = null;
3705
3706             // In BatchRPCMode, the input parameters start at parameters[1]. parameters[0] is the T-SQL statement. rpcName is sp_executesql.
3707             // And it is already in the format expected out of BuildParamList, which is not the case with Non-BatchRPCMode.
3708             if (BatchRPCMode) {
3709                 if (originalRpcRequest.parameters.Length > 1) {
3710                     parameterList = (string)originalRpcRequest.parameters[1].Value;
3711                 }
3712             }
3713             else {
3714                 // Prepare @params parameter
3715                 // Need to create new parameters as we cannot have the same parameter being part of two SqlCommand objects
3716                 SqlParameter paramCopy;
3717                 SqlParameterCollection tempCollection = new SqlParameterCollection();
3718
3719                 for (int i = 0; i < _parameters.Count; i++) {
3720                     SqlParameter param = originalRpcRequest.parameters[i];
3721                     paramCopy = new SqlParameter(param.ParameterName, param.SqlDbType, param.Size, param.Direction, param.Precision, param.Scale, param.SourceColumn, param.SourceVersion,
3722                         param.SourceColumnNullMapping, param.Value, param.XmlSchemaCollectionDatabase, param.XmlSchemaCollectionOwningSchema, param.XmlSchemaCollectionName);
3723                     paramCopy.CompareInfo = param.CompareInfo;
3724                     paramCopy.TypeName = param.TypeName;
3725                     paramCopy.UdtTypeName = param.UdtTypeName;
3726                     paramCopy.IsNullable = param.IsNullable;
3727                     paramCopy.LocaleId = param.LocaleId;
3728                     paramCopy.Offset = param.Offset;
3729
3730                     tempCollection.Add(paramCopy);
3731                 }
3732
3733                 Debug.Assert(_stateObj == null, "_stateObj should be null at this time, in PrepareDescribeParameterEncryptionRequest.");
3734                 Debug.Assert(_activeConnection != null, "_activeConnection should not be null at this time, in PrepareDescribeParameterEncryptionRequest.");
3735                 TdsParser tdsParser = null;
3736
3737                 if (_activeConnection.Parser != null) {
3738                     tdsParser = _activeConnection.Parser;
3739                     if ((tdsParser == null) || (tdsParser.State == TdsParserState.Broken) || (tdsParser.State == TdsParserState.Closed)) {
3740                         // Connection's parser is null as well, therefore we must be closed
3741                         throw ADP.ClosedConnectionError();
3742                     }
3743                 }
3744
3745                 parameterList = BuildParamList(tdsParser, tempCollection, includeReturnValue:true);
3746             }
3747
3748             Debug.Assert(!string.IsNullOrWhiteSpace(parameterList), "parameterList should not be null or empty or whitespace.");
3749
3750             sqlParam = new SqlParameter(null, ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, parameterList.Length);
3751             sqlParam.Value = parameterList;
3752             describeParameterEncryptionRequest.parameters[1] = sqlParam;
3753         }
3754
3755         /// <summary>
3756         /// Read the output of sp_describe_parameter_encryption
3757         /// </summary>
3758         /// <param name="ds">Resultset from calling to sp_describe_parameter_encryption</param>
3759         /// <param name="describeParameterEncryptionRpcOriginalRpcMap"> Readonly dictionary with the map of parameter encryption rpc requests with the corresponding original rpc requests.</param>
3760         private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap) {
3761             _SqlRPC rpc = null;
3762             int currentOrdinal = -1;
3763             SqlTceCipherInfoEntry cipherInfoEntry;
3764             Dictionary<int, SqlTceCipherInfoEntry> columnEncryptionKeyTable = new Dictionary<int, SqlTceCipherInfoEntry>();
3765
3766             Debug.Assert((describeParameterEncryptionRpcOriginalRpcMap != null) == BatchRPCMode,
3767                 "describeParameterEncryptionRpcOriginalRpcMap should be non-null if and only if it is BatchRPCMode.");
3768
3769             // Indicates the current result set we are reading, used in BatchRPCMode, where we can have more than 1 result set.
3770             int resultSetSequenceNumber = 0;
3771
3772 #if DEBUG
3773             // Keep track of the number of rows in the result sets.
3774             int rowsAffected = 0;
3775 #endif
3776
3777             // A flag that used in BatchRPCMode, to assert the result of lookup in to the dictionary maintaining the map of describe parameter encryption requests
3778             // and the corresponding original rpc requests.
3779             bool lookupDictionaryResult;
3780
3781             do {
3782                 if (BatchRPCMode) {
3783                     // If we got more RPC results from the server than what was requested.
3784                     if (resultSetSequenceNumber >= _sqlRPCParameterEncryptionReqArray.Length) {
3785                         Debug.Assert(false, "Server sent back more results than what was expected for describe parameter encryption requests in BatchRPCMode.");
3786                         // Ignore the rest of the results from the server, if for whatever reason it sends back more than what we expect.
3787                         break;
3788                     }
3789                 }
3790
3791                 // First read the column encryption key list
3792                 while (ds.Read()) {
3793
3794 #if DEBUG
3795                     rowsAffected++;
3796 #endif
3797
3798                     // Column Encryption Key Ordinal.
3799                     currentOrdinal = ds.GetInt32((int)DescribeParameterEncryptionResultSet1.KeyOrdinal);
3800                     Debug.Assert(currentOrdinal >= 0, "currentOrdinal cannot be negative.");
3801
3802                     // Try to see if there was already an entry for the current ordinal.
3803                     if (!columnEncryptionKeyTable.TryGetValue(currentOrdinal, out cipherInfoEntry)) {
3804                         // If an entry for this ordinal was not found, create an entry in the columnEncryptionKeyTable for this ordinal.
3805                         cipherInfoEntry = new SqlTceCipherInfoEntry(currentOrdinal);
3806                         columnEncryptionKeyTable.Add(currentOrdinal, cipherInfoEntry);
3807                     }
3808
3809                     Debug.Assert(!cipherInfoEntry.Equals(default(SqlTceCipherInfoEntry)), "cipherInfoEntry should not be un-initialized.");
3810
3811                     // Read the CEK.
3812                     byte[] encryptedKey = null;
3813                     int encryptedKeyLength = (int)ds.GetBytes((int)DescribeParameterEncryptionResultSet1.EncryptedKey, 0, encryptedKey, 0, 0);
3814                     encryptedKey = new byte[encryptedKeyLength];
3815                     ds.GetBytes((int)DescribeParameterEncryptionResultSet1.EncryptedKey, 0, encryptedKey, 0, encryptedKeyLength);
3816
3817                     // Read the metadata version of the key.
3818                     // It should always be 8 bytes.
3819                     byte[] keyMdVersion = new byte[8];
3820                     ds.GetBytes((int)DescribeParameterEncryptionResultSet1.KeyMdVersion, 0, keyMdVersion, 0, keyMdVersion.Length);
3821
3822                     // Validate the provider name
3823                     string providerName = ds.GetString((int)DescribeParameterEncryptionResultSet1.ProviderName);
3824                     //SqlColumnEncryptionKeyStoreProvider keyStoreProvider;
3825                     //if (!SqlConnection.TryGetColumnEncryptionKeyStoreProvider (providerName, out keyStoreProvider)) {
3826                     //    // unknown provider, skip processing this cek.
3827                     //    Bid.Trace("<sc.SqlCommand.ReadDescribeEncryptionParameterResults|INFO>Unknown provider name recevied %s, skipping\n", providerName);
3828                     //    continue;
3829                     //}
3830
3831                     cipherInfoEntry.Add(encryptedKey: encryptedKey,
3832                                         databaseId: ds.GetInt32((int)DescribeParameterEncryptionResultSet1.DbId),
3833                                         cekId: ds.GetInt32((int)DescribeParameterEncryptionResultSet1.KeyId),
3834                                         cekVersion: ds.GetInt32((int)DescribeParameterEncryptionResultSet1.KeyVersion),
3835                                         cekMdVersion: keyMdVersion,
3836                                         keyPath: ds.GetString((int)DescribeParameterEncryptionResultSet1.KeyPath),
3837                                         keyStoreName: providerName,
3838                                         algorithmName: ds.GetString((int)DescribeParameterEncryptionResultSet1.KeyEncryptionAlgorithm));
3839                 }
3840
3841                 if (!ds.NextResult()) {
3842                     throw SQL.UnexpectedDescribeParamFormat ();
3843                 }
3844
3845                 // Find the RPC command that generated this tce request
3846                 if (BatchRPCMode) {
3847                     Debug.Assert(_sqlRPCParameterEncryptionReqArray[resultSetSequenceNumber] != null, "_sqlRPCParameterEncryptionReqArray[resultSetSequenceNumber] should not be null.");
3848
3849                     // Lookup in the dictionary to get the original rpc request corresponding to the describe parameter encryption request
3850                     // pointed to by _sqlRPCParameterEncryptionReqArray[resultSetSequenceNumber]
3851                     rpc = null;
3852                     lookupDictionaryResult = describeParameterEncryptionRpcOriginalRpcMap.TryGetValue(_sqlRPCParameterEncryptionReqArray[resultSetSequenceNumber++], out rpc);
3853
3854                     Debug.Assert(lookupDictionaryResult,
3855                         "Describe Parameter Encryption RPC request key must be present in the dictionary describeParameterEncryptionRpcOriginalRpcMap");
3856                     Debug.Assert(rpc != null,
3857                         "Describe Parameter Encryption RPC request's corresponding original rpc request must not be null in the dictionary describeParameterEncryptionRpcOriginalRpcMap");
3858                 }
3859                 else {
3860                     rpc = _rpcArrayOf1[0];
3861                 }
3862
3863                 Debug.Assert(rpc != null, "rpc should not be null here.");
3864
3865                 // This is the index in the parameters array where the actual parameters start.
3866                 // In BatchRPCMode, parameters[0] has the t-sql, parameters[1] has the param list
3867                 // and actual parameters of the query start at parameters[2].
3868                 int parameterStartIndex = (BatchRPCMode ? 2 : 0);
3869
3870                 // Iterate over the parameter names to read the encryption type info
3871                 int paramIdx;
3872                 while (ds.Read()) {
3873 #if DEBUG
3874                     rowsAffected++;
3875 #endif
3876                     Debug.Assert(rpc != null, "Describe Parameter Encryption requested for non-tce spec proc");
3877                     string parameterName = ds.GetString((int)DescribeParameterEncryptionResultSet2.ParameterName);
3878
3879                     // When the RPC object gets reused, the parameter array has more parameters that the valid params for the command.
3880                     // Null is used to indicate the end of the valid part of the array. Refer to GetRPCObject().
3881                     for (paramIdx = parameterStartIndex; paramIdx < rpc.parameters.Length && rpc.parameters[paramIdx] != null; paramIdx++) {
3882                         SqlParameter sqlParameter = rpc.parameters[paramIdx];
3883                         Debug.Assert(sqlParameter != null, "sqlParameter should not be null.");
3884
3885                         if (sqlParameter.ParameterNameFixed.Equals(parameterName, StringComparison.Ordinal)) {
3886                             Debug.Assert(sqlParameter.CipherMetadata == null, "param.CipherMetadata should be null.");
3887                             sqlParameter.HasReceivedMetadata = true;
3888
3889                             // Found the param, setup the encryption info.
3890                             byte columnEncryptionType = ds.GetByte((int)DescribeParameterEncryptionResultSet2.ColumnEncrytionType);
3891                             if ((byte)SqlClientEncryptionType.PlainText != columnEncryptionType) {
3892                                 byte cipherAlgorithmId = ds.GetByte((int)DescribeParameterEncryptionResultSet2.ColumnEncryptionAlgorithm);
3893                                 int columnEncryptionKeyOrdinal = ds.GetInt32((int)DescribeParameterEncryptionResultSet2.ColumnEncryptionKeyOrdinal);
3894                                 byte columnNormalizationRuleVersion = ds.GetByte((int)DescribeParameterEncryptionResultSet2.NormalizationRuleVersion);
3895
3896                                 // Lookup the key, failing which throw an exception
3897                                 if (!columnEncryptionKeyTable.TryGetValue(columnEncryptionKeyOrdinal, out cipherInfoEntry)) {
3898                                     throw SQL.InvalidEncryptionKeyOrdinal(columnEncryptionKeyOrdinal, columnEncryptionKeyTable.Count);
3899                                 }
3900
3901                                 sqlParameter.CipherMetadata = new SqlCipherMetadata(sqlTceCipherInfoEntry: cipherInfoEntry,
3902                                                                                     ordinal: unchecked((ushort)-1),
3903                                                                                     cipherAlgorithmId: cipherAlgorithmId,
3904                                                                                     cipherAlgorithmName: null,
3905                                                                                     encryptionType: columnEncryptionType,
3906                                                                                     normalizationRuleVersion: columnNormalizationRuleVersion);
3907
3908                                 // Decrypt the symmetric key.(This will also validate and throw if needed).
3909                                 Debug.Assert(_activeConnection != null, @"_activeConnection should not be null");
3910                                 SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, this._activeConnection.DataSource);
3911
3912                                 // This is effective only for BatchRPCMode even though we set it for non-BatchRPCMode also,
3913                                 // since for non-BatchRPCMode mode, paramoptions gets thrown away and reconstructed in BuildExecuteSql.
3914                                 rpc.paramoptions[paramIdx] |= TdsEnums.RPC_PARAM_ENCRYPTED;
3915                             }
3916
3917                             break;
3918                         }
3919                     }
3920                 }
3921
3922                 // When the RPC object gets reused, the parameter array has more parameters that the valid params for the command.
3923                 // Null is used to indicate the end of the valid part of the array. Refer to GetRPCObject().
3924                 for (paramIdx = parameterStartIndex; paramIdx < rpc.parameters.Length && rpc.parameters[paramIdx] != null; paramIdx++) {
3925                     if (!rpc.parameters[paramIdx].HasReceivedMetadata && rpc.parameters[paramIdx].Direction != ParameterDirection.ReturnValue) {
3926                         // Encryption MD wasn't sent by the server - we expect the metadata to be sent for all the parameters 
3927                         // that were sent in the original sp_describe_parameter_encryption but not necessarily for return values,
3928                         // since there might be multiple return values but server will only send for one of them.
3929                         // For parameters that don't need encryption, the encryption type is set to plaintext.
3930                         throw SQL.ParamEncryptionMetadataMissing(rpc.parameters[paramIdx].ParameterName, rpc.GetCommandTextOrRpcName());
3931                     }
3932                 }
3933
3934 #if DEBUG
3935                 Debug.Assert(rowsAffected == RowsAffectedByDescribeParameterEncryption,
3936                             "number of rows received for describe parameter encryption should be equal to rows affected by describe parameter encryption.");
3937 #endif
3938
3939                  // The server has responded with encryption related information for this rpc request. So clear the needsFetchParameterEncryptionMetadata flag.
3940                 rpc.needsFetchParameterEncryptionMetadata = false;
3941             } while (ds.NextResult());
3942
3943             // Verify that we received response for each rpc call needs tce
3944             if (BatchRPCMode) {
3945                 for (int i = 0; i < _SqlRPCBatchArray.Length; i++) {
3946                     if (_SqlRPCBatchArray[i].needsFetchParameterEncryptionMetadata) {
3947                         throw SQL.ProcEncryptionMetadataMissing(_SqlRPCBatchArray[i].rpcName);
3948                     }
3949                 }
3950             }
3951
3952             // If we are not in Batch RPC mode, update the query cache with the encryption MD.
3953             if (!BatchRPCMode) {
3954                 SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: true);
3955             }
3956         }
3957
3958         internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method) {
3959             Task unused; // sync execution 
3960             bool usedCache;
3961             SqlDataReader reader = RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, completion: null, timeout: CommandTimeout, task: out unused, usedCache: out usedCache);
3962             Debug.Assert(unused == null, "returned task during synchronous execution");
3963             return reader;
3964         }
3965
3966         // task is created in case of pending asynchronous write, returned SqlDataReader should not be utilized until that task is complete 
3967         internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method, TaskCompletionSource<object> completion, int timeout, out Task task, out bool usedCache, bool asyncWrite = false, bool inRetry = false) {
3968             bool async = (null != completion);
3969             usedCache = false;
3970
3971             task = null;
3972
3973             _rowsAffected = -1;
3974             _rowsAffectedBySpDescribeParameterEncryption = -1;
3975             
3976             if (0 != (CommandBehavior.SingleRow & cmdBehavior)) {
3977                 // CommandBehavior.SingleRow implies CommandBehavior.SingleResult
3978                 cmdBehavior |= CommandBehavior.SingleResult;
3979             }
3980
3981             // @devnote: this function may throw for an invalid connection
3982             // @devnote: returns false for empty command text
3983             if (!inRetry) {
3984                 ValidateCommand(method, async);
3985             }
3986
3987             CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection!
3988
3989             TdsParser bestEffortCleanupTarget = null;
3990             // This section needs to occur AFTER ValidateCommand - otherwise it will AV without a connection.
3991             RuntimeHelpers.PrepareConstrainedRegions();
3992             try {
3993 #if DEBUG
3994                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3995
3996                 RuntimeHelpers.PrepareConstrainedRegions();
3997                 try {
3998                     tdsReliabilitySection.Start();
3999 #else
4000                 {
4001 #endif //DEBUG
4002                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
4003                     SqlStatistics statistics = Statistics;
4004                     if (null != statistics) {
4005                         if ((!this.IsDirty && this.IsPrepared && !_hiddenPrepare)
4006                             || (this.IsPrepared && _execType == EXECTYPE.PREPAREPENDING))
4007                         {
4008                             statistics.SafeIncrement(ref statistics._preparedExecs);
4009                         }
4010                         else {
4011                             statistics.SafeIncrement(ref statistics._unpreparedExecs);
4012                         }
4013                     }
4014
4015                     // Reset the encryption related state of the command and its parameters.
4016                     ResetEncryptionState();
4017
4018                     if ( _activeConnection.IsContextConnection ) {
4019                         return RunExecuteReaderSmi( cmdBehavior, runBehavior, returnStream );
4020                     }
4021                     else if (IsColumnEncryptionEnabled) {
4022                         Task returnTask = null;
4023                         PrepareForTransparentEncryption(cmdBehavior, returnStream, async, timeout, completion, out returnTask, asyncWrite && async, out usedCache, inRetry);
4024                         Debug.Assert(usedCache || (async == (returnTask != null)), @"if we didn't use the cache, returnTask should be null if and only if async is false.");
4025
4026                         long firstAttemptStart = ADP.TimerCurrent();
4027
4028                         try {
4029                             return RunExecuteReaderTdsWithTransparentParameterEncryption(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry, ds: null,
4030                                 describeParameterEncryptionRequest: false, describeParameterEncryptionTask: returnTask);
4031                         }
4032                         catch (SqlException ex) {
4033                             // We only want to retry once, so don't retry if we are already in retry.
4034                             // If we didn't use the cache, we don't want to retry.
4035                             // The async retried are handled separately, handle only sync calls here.
4036                             if (inRetry || async || !usedCache) {
4037                                 throw;
4038                             }
4039
4040                             bool shouldRetry = false;
4041
4042                             // Check if we have an error indicating that we can retry.
4043                             for (int i = 0; i < ex.Errors.Count; i++) {
4044                                 if (ex.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY) {
4045                                     shouldRetry = true;
4046                                     break;
4047                                 }
4048                             }
4049
4050                             if (!shouldRetry) {
4051                                 throw;
4052                             }
4053                             else {
4054                                 // Retry if the command failed with appropriate error.
4055                                 // First invalidate the entry from the cache, so that we refresh our encryption MD.
4056                                 SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this);
4057                                 return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true);
4058                             }
4059                         }
4060                     }
4061                     else {
4062                         return RunExecuteReaderTds( cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry);
4063                     }
4064
4065                 }
4066 #if DEBUG
4067                 finally {
4068                     tdsReliabilitySection.Stop();
4069                 }
4070 #endif //DEBUG
4071             }
4072             catch (System.OutOfMemoryException e) {
4073                 _activeConnection.Abort(e);
4074                 throw;
4075             }
4076             catch (System.StackOverflowException e) {
4077                 _activeConnection.Abort(e);
4078                 throw;
4079             }
4080             catch (System.Threading.ThreadAbortException e)  {
4081                 _activeConnection.Abort(e);
4082                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
4083                 throw;
4084             }
4085         }
4086
4087         /// <summary>
4088         /// RunExecuteReaderTds after Transparent Parameter Encryption is complete.
4089         /// </summary>
4090         /// <param name="cmdBehavior"></param>
4091         /// <param name="runBehavior"></param>
4092         /// <param name="returnStream"></param>
4093         /// <param name="async"></param>
4094         /// <param name="timeout"></param>
4095         /// <param name="task"></param>
4096         /// <param name="asyncWrite"></param>
4097         /// <param name="ds"></param>
4098         /// <param name="describeParameterEncryptionRequest"></param>
4099         /// <param name="describeParameterEncryptionTask"></param>
4100         /// <returns></returns>
4101         private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption(CommandBehavior cmdBehavior,
4102                                                                                     RunBehavior runBehavior,
4103                                                                                     bool returnStream,
4104                                                                                     bool async,
4105                                                                                     int timeout,
4106                                                                                     out Task task,
4107                                                                                     bool asyncWrite,
4108                                                                                     bool inRetry,
4109                                                                                     SqlDataReader ds=null,
4110                                                                                     bool describeParameterEncryptionRequest = false,
4111                                                                                     Task describeParameterEncryptionTask = null) {
4112             Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async");
4113
4114             if (ds == null && returnStream) {
4115                 ds = new SqlDataReader(this, cmdBehavior);
4116             }
4117
4118             if (describeParameterEncryptionTask != null) {
4119                 long parameterEncryptionStart = ADP.TimerCurrent();
4120                 TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
4121                 AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion,
4122                     () => {
4123                         Task subTask = null;
4124                         RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds);
4125                         if (subTask == null) {
4126                             completion.SetResult(null);
4127                         }
4128                         else {
4129                             AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null));
4130                         }
4131                     }, connectionToDoom: null,
4132                     onFailure: ((exception) => {
4133                         if (_cachedAsyncState != null) {
4134                             _cachedAsyncState.ResetAsyncState();
4135                         }
4136                         if (exception != null) {
4137                             throw exception;
4138                         }
4139                     }),
4140                     onCancellation: (() => {
4141                         if (_cachedAsyncState != null) {
4142                             _cachedAsyncState.ResetAsyncState();
4143                         }
4144                     }),
4145                     connectionToAbort: _activeConnection);
4146                 task = completion.Task;
4147                 return ds;
4148             }
4149             else {
4150                 // Synchronous execution.
4151                 return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite, inRetry, ds);
4152             }
4153         }
4154
4155         private SqlDataReader RunExecuteReaderTds( CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, bool async, int timeout, out Task task, bool asyncWrite, bool inRetry, SqlDataReader ds=null, bool describeParameterEncryptionRequest = false) {
4156             Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async");
4157
4158             if (ds == null && returnStream) {
4159                 ds = new SqlDataReader(this, cmdBehavior);
4160             }
4161
4162             Task reconnectTask = _activeConnection.ValidateAndReconnect(null, timeout);
4163
4164             if (reconnectTask != null) {
4165                 long reconnectionStart = ADP.TimerCurrent();
4166                 if (async) {                    
4167                     TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
4168                     _activeConnection.RegisterWaitingForReconnect(completion.Task);
4169                     _reconnectionCompletionSource = completion;
4170                     CancellationTokenSource timeoutCTS = new CancellationTokenSource();
4171                     AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token);
4172                     AsyncHelper.ContinueTask(reconnectTask, completion,
4173                         () => {
4174                             if (completion.Task.IsCompleted) {
4175                                 return;
4176                             }
4177                             Interlocked.CompareExchange(ref _reconnectionCompletionSource, null, completion);
4178                             timeoutCTS.Cancel();
4179                             Task subTask;                            
4180                             RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart), out subTask, asyncWrite, inRetry, ds);
4181                             if (subTask == null) {
4182                                 completion.SetResult(null);
4183                             }
4184                             else {
4185                                 AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null));
4186                             }
4187                         }, connectionToAbort: _activeConnection);
4188                     task = completion.Task;
4189                     return ds;
4190                 }
4191                 else {
4192                     AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); });
4193                     timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart);
4194                 }
4195             }
4196
4197             // make sure we have good parameter information
4198             // prepare the command
4199             // execute
4200             Debug.Assert(null != _activeConnection.Parser, "TdsParser class should not be null in Command.Execute!");
4201
4202             bool inSchema =  (0 != (cmdBehavior & CommandBehavior.SchemaOnly));           
4203
4204             // create a new RPC
4205             _SqlRPC rpc=null;
4206
4207             task = null;
4208
4209             string optionSettings = null;
4210             bool processFinallyBlock = true;
4211             bool decrementAsyncCountOnFailure = false;
4212
4213             // If we are in retry, don't increment the Async count. This should have already been set.
4214             if (async && !inRetry) {
4215                 _activeConnection.GetOpenTdsConnection().IncrementAsyncCount();
4216                 decrementAsyncCountOnFailure = true;
4217             }
4218
4219             try {
4220               
4221                 if (asyncWrite) {
4222                     _activeConnection.AddWeakReference(this, SqlReferenceCollection.CommandTag);
4223                 }
4224
4225                 GetStateObject();
4226                 Task writeTask = null;
4227
4228                 if (describeParameterEncryptionRequest) {
4229 #if DEBUG
4230                     if (_sleepDuringRunExecuteReaderTdsForSpDescribeParameterEncryption) {
4231                         Thread.Sleep(10000);
4232                     }
4233 #endif
4234
4235                     Debug.Assert(_sqlRPCParameterEncryptionReqArray != null, "RunExecuteReader rpc array not provided for describe parameter encryption request.");
4236                     writeTask = _stateObj.Parser.TdsExecuteRPC(this, _sqlRPCParameterEncryptionReqArray, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite);
4237                 }
4238                 else if (BatchRPCMode) {
4239                     Debug.Assert(inSchema == false, "Batch RPC does not support schema only command beahvior");
4240                     Debug.Assert(!IsPrepared, "Batch RPC should not be prepared!");
4241                     Debug.Assert(!IsDirty, "Batch RPC should not be marked as dirty!");
4242                     //Currently returnStream is always false, but we may want to return a Reader later.
4243                     //if (returnStream) {
4244                     //    Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as batch RPC.\n", ObjectID);
4245                     //}
4246                     Debug.Assert(_SqlRPCBatchArray != null, "RunExecuteReader rpc array not provided");
4247                     writeTask = _stateObj.Parser.TdsExecuteRPC(this, _SqlRPCBatchArray, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite );                    
4248                 }
4249                 else if ((System.Data.CommandType.Text == this.CommandType) && (0 == GetParameterCount(_parameters))) {
4250                     // Send over SQL Batch command if we are not a stored proc and have no parameters
4251                     // MDAC 
4252                     Debug.Assert(!IsUserPrepared, "CommandType.Text with no params should not be prepared!");
4253                     if (returnStream) {
4254                         Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as SQLBATCH.\n", ObjectID);
4255                     }
4256                     string text = GetCommandText(cmdBehavior) + GetResetOptionsString(cmdBehavior);
4257                     writeTask = _stateObj.Parser.TdsExecuteSQLBatch(text, timeout, this.Notification, _stateObj, sync: !asyncWrite);
4258                 }
4259                 else if (System.Data.CommandType.Text == this.CommandType) {
4260                     if (this.IsDirty) {
4261                         Debug.Assert(_cachedMetaData == null || !_dirty, "dirty query should not have cached metadata!"); // can have cached metadata if dirty because of parameters
4262                         //
4263                         // someone changed the command text or the parameter schema so we must unprepare the command
4264                         //
4265                         // remeber that IsDirty includes test for IsPrepared!
4266                         if(_execType == EXECTYPE.PREPARED) {
4267                             _hiddenPrepare = true;
4268                         }
4269                         Unprepare();
4270                         IsDirty = false;
4271                     }
4272
4273                     if (_execType == EXECTYPE.PREPARED) {
4274                         Debug.Assert(this.IsPrepared && (_prepareHandle != -1), "invalid attempt to call sp_execute without a handle!");
4275                         rpc = BuildExecute(inSchema);
4276                     }
4277                     else if (_execType == EXECTYPE.PREPAREPENDING) {
4278                         Debug.Assert(_activeConnection.IsShiloh, "Invalid attempt to call sp_prepexec on non 7.x server");
4279                         rpc = BuildPrepExec(cmdBehavior);
4280                         // next time through, only do an exec
4281                         _execType = EXECTYPE.PREPARED;
4282                         _preparedConnectionCloseCount = _activeConnection.CloseCount;
4283                         _preparedConnectionReconnectCount = _activeConnection.ReconnectCount;
4284                         // mark ourselves as preparing the command
4285                         _inPrepare = true;
4286                     }
4287                     else {
4288                         Debug.Assert(_execType == EXECTYPE.UNPREPARED, "Invalid execType!");
4289                         BuildExecuteSql(cmdBehavior, null, _parameters, ref rpc);
4290                     }
4291
4292                     // if shiloh, then set NOMETADATA_UNLESSCHANGED flag
4293                     if (_activeConnection.IsShiloh)
4294                         rpc.options = TdsEnums.RPC_NOMETADATA;
4295                     if (returnStream) {
4296                         Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as RPC.\n", ObjectID);
4297                     }
4298
4299                     // 
4300                     Debug.Assert(_rpcArrayOf1[0] == rpc);
4301                     writeTask = _stateObj.Parser.TdsExecuteRPC(this, _rpcArrayOf1, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync:!asyncWrite);
4302                 }
4303                 else {
4304                     Debug.Assert(this.CommandType == System.Data.CommandType.StoredProcedure, "unknown command type!");
4305                     // note: invalid asserts on Shiloh. On 8.0 (Shiloh) and above a command is ALWAYS prepared
4306                     // and IsDirty is always set if there are changes and the command is marked Prepared!
4307                     Debug.Assert(IsShiloh || !IsPrepared, "RPC should not be prepared!");
4308                     Debug.Assert(IsShiloh || !IsDirty, "RPC should not be marked as dirty!");
4309
4310                     BuildRPC(inSchema, _parameters, ref rpc);
4311
4312                     // if we need to augment the command because a user has changed the command behavior (e.g. FillSchema)
4313                     // then batch sql them over.  This is inefficient (3 round trips) but the only way we can get metadata only from
4314                     // a stored proc
4315                     optionSettings = GetSetOptionsString(cmdBehavior);
4316                     if (returnStream) {
4317                         Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as RPC.\n", ObjectID);
4318                     }
4319                     // turn set options ON
4320                     if (null != optionSettings) {
4321                         Task executeTask = _stateObj.Parser.TdsExecuteSQLBatch(optionSettings, timeout, this.Notification, _stateObj, sync: true);
4322                         Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes");
4323                         bool dataReady;
4324                         Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
4325                         bool result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, null, null, _stateObj, out dataReady);
4326                         if (!result) { throw SQL.SynchronousCallMayNotPend(); }
4327                         // and turn OFF when the ds exhausts the stream on Close()
4328                         optionSettings = GetResetOptionsString(cmdBehavior);
4329                     }
4330
4331                     // turn debugging on
4332                     _activeConnection.CheckSQLDebug();
4333
4334                     // execute sp
4335                     Debug.Assert(_rpcArrayOf1[0] == rpc);
4336                     writeTask=_stateObj.Parser.TdsExecuteRPC(this, _rpcArrayOf1, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync:!asyncWrite); 
4337                 }
4338
4339                 Debug.Assert(writeTask == null || async, "Returned task in sync mode");
4340
4341                 if (async) {
4342                     decrementAsyncCountOnFailure = false; 
4343                     if (writeTask != null) {
4344                         task = AsyncHelper.CreateContinuationTask(writeTask, () => {
4345                                      _activeConnection.GetOpenTdsConnection(); // it will throw if connection is closed
4346                                      cachedAsyncState.SetAsyncReaderState(ds, runBehavior, optionSettings);
4347                                  },
4348                                  onFailure: (exc) => {
4349                                      _activeConnection.GetOpenTdsConnection().DecrementAsyncCount(); 
4350                                  } );
4351                     }
4352                     else {
4353                         cachedAsyncState.SetAsyncReaderState(ds, runBehavior, optionSettings);
4354                     }
4355                 }
4356                 else {
4357                     // Always execute - even if no reader!
4358                     FinishExecuteReader(ds, runBehavior, optionSettings, isInternal: false, forDescribeParameterEncryption: false);
4359                 }
4360             }
4361             catch (Exception e) {                
4362                 processFinallyBlock = ADP.IsCatchableExceptionType (e);
4363                 if (decrementAsyncCountOnFailure) {
4364                     SqlInternalConnectionTds innerConnectionTds = (_activeConnection.InnerConnection as SqlInternalConnectionTds);
4365                     if (null != innerConnectionTds) { // it may be closed 
4366                         innerConnectionTds.DecrementAsyncCount();
4367                     }
4368                 }
4369                 throw;
4370             }
4371             finally {
4372                 TdsParser.ReliabilitySection.Assert("unreliable call to RunExecuteReaderTds");  // you need to setup for a thread abort somewhere before you call this method
4373                 if (processFinallyBlock && !async) {
4374                     // When executing async, we need to keep the _stateObj alive...
4375                     PutStateObject();
4376                 }
4377             }
4378
4379             Debug.Assert(async || null == _stateObj, "non-null state object in RunExecuteReader");
4380             return ds;
4381         }
4382
4383         private SqlDataReader RunExecuteReaderSmi( CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream ) {
4384             SqlInternalConnectionSmi innerConnection = InternalSmiConnection;
4385
4386             SmiEventStream eventStream = null;
4387             SqlDataReader ds = null;
4388             SmiRequestExecutor requestExecutor = null;
4389             try {
4390                 // Set it up, process all of the events, and we're done!
4391                 requestExecutor = SetUpSmiRequest( innerConnection );
4392
4393                 long transactionId;
4394                 SysTx.Transaction transaction;
4395                 innerConnection.GetCurrentTransactionPair(out transactionId, out transaction);
4396
4397                 if (Bid.AdvancedOn) {
4398                     Bid.Trace("<sc.SqlCommand.RunExecuteReaderSmi|ADV> %d#, innerConnection=%d#, transactionId=0x%I64x, commandBehavior=%d.\n", ObjectID, innerConnection.ObjectID, transactionId, (int)cmdBehavior);
4399                 }
4400
4401                 if (SmiContextFactory.Instance.NegotiatedSmiVersion >= SmiContextFactory.KatmaiVersion) {
4402                     eventStream = requestExecutor.Execute(
4403                                                     innerConnection.SmiConnection,
4404                                                     transactionId,
4405                                                     transaction,
4406                                                     cmdBehavior,
4407                                                     SmiExecuteType.Reader
4408                                                     );
4409                 }
4410                 else {
4411                     eventStream = requestExecutor.Execute(
4412                                                     innerConnection.SmiConnection,
4413                                                     transactionId,
4414                                                     cmdBehavior,
4415                                                     SmiExecuteType.Reader
4416                                                     );
4417                 }
4418
4419                 if ( ( runBehavior & RunBehavior.UntilDone ) != 0 ) {
4420
4421                     // Consume the results
4422                     while( eventStream.HasEvents ) {
4423                         eventStream.ProcessEvent( EventSink );
4424                     }
4425                     eventStream.Close( EventSink );
4426                 }
4427
4428                 if ( returnStream ) {
4429                     ds = new SqlDataReaderSmi( eventStream, this, cmdBehavior, innerConnection, EventSink, requestExecutor );
4430                     ds.NextResult();    // Position on first set of results
4431                     _activeConnection.AddWeakReference(ds, SqlReferenceCollection.DataReaderTag);
4432                 }
4433
4434                 EventSink.ProcessMessagesAndThrow();
4435             }
4436             catch (Exception e) {
4437                 // VSTS 159716 - we do not want to handle ThreadAbort, OutOfMemory or similar critical exceptions
4438                 // because the state of used objects might remain invalid in this case
4439                 if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
4440                     throw;
4441                 }
4442
4443                 if (null != eventStream) {
4444                     eventStream.Close( EventSink );     // 
4445                 }
4446
4447                 if (requestExecutor != null) {
4448                     requestExecutor.Close(EventSink);
4449                     EventSink.ProcessMessagesAndThrow(ignoreNonFatalMessages: true);
4450                 }                
4451
4452                 throw;
4453             }
4454
4455         return ds;
4456         }
4457
4458         private SqlDataReader CompleteAsyncExecuteReader(bool isInternal = false, bool forDescribeParameterEncryption = false) {
4459             SqlDataReader ds = cachedAsyncState.CachedAsyncReader; // should not be null
4460             bool processFinallyBlock = true;
4461             try {
4462                 FinishExecuteReader(ds, cachedAsyncState.CachedRunBehavior, cachedAsyncState.CachedSetOptions, isInternal, forDescribeParameterEncryption);
4463             }
4464             catch (Exception e) {
4465                 processFinallyBlock = ADP.IsCatchableExceptionType(e);
4466                 throw;
4467             }
4468             finally {
4469                 TdsParser.ReliabilitySection.Assert("unreliable call to CompleteAsyncExecuteReader");  // you need to setup for a thread abort somewhere before you call this method
4470                 if (processFinallyBlock) {
4471                     // Don't reset the state for internal End. The user End will do that eventually.
4472                     if (!isInternal) {
4473                         cachedAsyncState.ResetAsyncState();
4474                     }
4475
4476                     PutStateObject();
4477                 }
4478             }
4479
4480             return ds;
4481         }
4482
4483         private void FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, string resetOptionsString, bool isInternal, bool forDescribeParameterEncryption) {
4484             // always wrap with a try { FinishExecuteReader(...) } finally { PutStateObject(); }
4485
4486             // If this is not for internal usage, notify the dependency. If we have already initiated the end internally, the reader should be ready, so just return.
4487             if (!isInternal && !forDescribeParameterEncryption) {
4488                 NotifyDependency();
4489
4490                 if (_internalEndExecuteInitiated) {
4491                     Debug.Assert(_stateObj == null);
4492                     return;
4493                 }
4494             }
4495
4496             if (runBehavior == RunBehavior.UntilDone) {
4497                 try {
4498                     bool dataReady;
4499                     Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
4500                     bool result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, ds, null, _stateObj, out dataReady);
4501                     if (!result) { throw SQL.SynchronousCallMayNotPend(); }
4502                 }
4503                 catch (Exception e) {
4504                     // 
4505                     if (ADP.IsCatchableExceptionType(e)) {
4506                         if (_inPrepare) {
4507                             // The flag is expected to be reset by OnReturnValue.  We should receive
4508                             // the handle unless command execution failed.  If fail, move back to pending
4509                             // state.
4510                             _inPrepare = false;                  // reset the flag
4511                             IsDirty = true;                      // mark command as dirty so it will be prepared next time we're comming through
4512                             _execType = EXECTYPE.PREPAREPENDING; // reset execution type to pending
4513                         }
4514
4515                         if (null != ds) {
4516                             ds.Close();
4517                         }
4518                     }
4519                     throw;
4520                 }
4521             }
4522
4523             // bind the parser to the reader if we get this far
4524             if (ds != null) {
4525                 ds.Bind(_stateObj);
4526                 _stateObj = null;   // the reader now owns this...
4527                 ds.ResetOptionsString = resetOptionsString;
4528
4529                 // 
4530
4531
4532
4533                 // bind this reader to this connection now
4534                 _activeConnection.AddWeakReference(ds, SqlReferenceCollection.DataReaderTag);
4535
4536                 // force this command to start reading data off the wire.
4537                 // this will cause an error to be reported at Execute() time instead of Read() time
4538                 // if the command is not set.
4539                 try {
4540                     _cachedMetaData = ds.MetaData;
4541                     ds.IsInitialized = true; // Webdata 104560
4542                 }
4543                 catch (Exception e) {
4544                     // 
4545                     if (ADP.IsCatchableExceptionType(e)) {
4546                         if (_inPrepare) {
4547                             // The flag is expected to be reset by OnReturnValue.  We should receive
4548                             // the handle unless command execution failed.  If fail, move back to pending
4549                             // state.
4550                             _inPrepare = false;                  // reset the flag
4551                             IsDirty = true;                      // mark command as dirty so it will be prepared next time we're comming through
4552                             _execType = EXECTYPE.PREPAREPENDING; // reset execution type to pending
4553                         }
4554
4555                         ds.Close();
4556                     }
4557
4558                     throw;
4559                 }
4560             }
4561         }
4562
4563         private void NotifyDependency() {
4564             if (_sqlDep != null) {
4565                 _sqlDep.StartTimer(Notification);
4566             }
4567         }
4568
4569         public SqlCommand Clone() {
4570             SqlCommand clone = new SqlCommand(this);
4571             Bid.Trace("<sc.SqlCommand.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
4572             return clone;
4573         }
4574
4575         object ICloneable.Clone() {
4576             return Clone();
4577         }
4578
4579         private void RegisterForConnectionCloseNotification<T>(ref Task<T> outterTask) {
4580             SqlConnection connection = _activeConnection;
4581             if (connection == null) {
4582                 // No connection
4583                 throw ADP.ClosedConnectionError();
4584             }
4585
4586             connection.RegisterForConnectionCloseNotification<T>(ref outterTask, this, SqlReferenceCollection.CommandTag);
4587         }
4588
4589         // validates that a command has commandText and a non-busy open connection
4590         // throws exception for error case, returns false if the commandText is empty
4591         private void ValidateCommand(string method, bool async) {
4592             if (null == _activeConnection) {
4593                 throw ADP.ConnectionRequired(method);
4594             }
4595
4596             // Ensure that the connection is open and that the Parser is in the correct state
4597             SqlInternalConnectionTds tdsConnection = _activeConnection.InnerConnection as SqlInternalConnectionTds;
4598
4599             // Ensure that if column encryption override was used then server supports its
4600             if (((SqlCommandColumnEncryptionSetting.UseConnectionSetting == ColumnEncryptionSetting  && _activeConnection.IsColumnEncryptionSettingEnabled)
4601                 || (ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled || ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.ResultSetOnly))
4602                    && null != tdsConnection
4603                    && null != tdsConnection.Parser
4604                    && !tdsConnection.Parser.IsColumnEncryptionSupported) {
4605                 throw SQL.TceNotSupported ();
4606             }
4607
4608             if (tdsConnection != null) {
4609                 var parser = tdsConnection.Parser;
4610                 if ((parser == null) || (parser.State == TdsParserState.Closed)) {
4611                     throw ADP.OpenConnectionRequired(method, ConnectionState.Closed);
4612                 }
4613                 else if (parser.State != TdsParserState.OpenLoggedIn) {
4614                     throw ADP.OpenConnectionRequired(method, ConnectionState.Broken);
4615                 }
4616             }
4617             else if (_activeConnection.State == ConnectionState.Closed) {
4618                 throw ADP.OpenConnectionRequired(method, ConnectionState.Closed);
4619             }
4620             else if (_activeConnection.State == ConnectionState.Broken) {
4621                 throw ADP.OpenConnectionRequired(method, ConnectionState.Broken);
4622             }
4623
4624             ValidateAsyncCommand();
4625
4626             TdsParser bestEffortCleanupTarget = null;
4627             RuntimeHelpers.PrepareConstrainedRegions();
4628             try {
4629 #if DEBUG
4630                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
4631
4632                 RuntimeHelpers.PrepareConstrainedRegions();
4633                 try {
4634                     tdsReliabilitySection.Start();
4635 #else
4636                 {
4637 #endif //DEBUG
4638                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
4639                     // close any non MARS dead readers, if applicable, and then throw if still busy.
4640                     // Throw if we have a live reader on this command
4641                     _activeConnection.ValidateConnectionForExecute(method, this);
4642
4643                 }
4644 #if DEBUG
4645                 finally {
4646                     tdsReliabilitySection.Stop();
4647                 }
4648 #endif //DEBUG
4649             }
4650             catch (System.OutOfMemoryException e)
4651             {
4652                 _activeConnection.Abort(e);
4653                 throw;
4654             }
4655             catch (System.StackOverflowException e)
4656             {
4657                 _activeConnection.Abort(e);
4658                 throw;
4659             }
4660             catch (System.Threading.ThreadAbortException e)
4661             {
4662                 _activeConnection.Abort(e);
4663                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
4664                 throw;
4665             }
4666             // Check to see if the currently set transaction has completed.  If so,
4667             // null out our local reference.
4668             if (null != _transaction && _transaction.Connection == null)
4669                 _transaction = null;
4670
4671             // throw if the connection is in a transaction but there is no
4672             // locally assigned transaction object
4673             if (_activeConnection.HasLocalTransactionFromAPI && (null == _transaction))
4674                 throw ADP.TransactionRequired(method);
4675
4676             // if we have a transaction, check to ensure that the active
4677             // connection property matches the connection associated with
4678             // the transaction
4679             if (null != _transaction && _activeConnection != _transaction.Connection)
4680                 throw ADP.TransactionConnectionMismatch();
4681
4682             if (ADP.IsEmpty(this.CommandText))
4683                 throw ADP.CommandTextRequired(method);
4684
4685             // Notification property must be null for pre-Yukon connections
4686             if ((Notification != null) && !_activeConnection.IsYukonOrNewer) {
4687                 throw SQL.NotificationsRequireYukon();
4688             }
4689
4690             if ((async) && (_activeConnection.IsContextConnection)) {
4691                 // Async not supported on Context Connections
4692                 throw SQL.NotAvailableOnContextConnection();
4693             }
4694         }
4695
4696         private void ValidateAsyncCommand() {
4697             // 
4698             if (cachedAsyncState.PendingAsyncOperation) { // Enforce only one pending async execute at a time.
4699                 if (cachedAsyncState.IsActiveConnectionValid(_activeConnection)) {
4700                     throw SQL.PendingBeginXXXExists();
4701                 }
4702                 else {
4703                     _stateObj = null; // Session was re-claimed by session pool upon connection close.
4704                     cachedAsyncState.ResetAsyncState();
4705                 }
4706             }
4707         }
4708
4709         private void GetStateObject(TdsParser parser = null) {
4710             Debug.Assert (null == _stateObj,"StateObject not null on GetStateObject");
4711             Debug.Assert (null != _activeConnection, "no active connection?");
4712
4713             if (_pendingCancel) {
4714                 _pendingCancel = false; // Not really needed, but we'll reset anyways.
4715
4716                 // If a pendingCancel exists on the object, we must have had a Cancel() call
4717                 // between the point that we entered an Execute* API and the point in Execute* that
4718                 // we proceeded to call this function and obtain a stateObject.  In that case,
4719                 // we now throw a cancelled error.
4720                 throw SQL.OperationCancelled();
4721             }
4722
4723             if (parser == null) {
4724                 parser = _activeConnection.Parser;
4725                 if ((parser == null) || (parser.State == TdsParserState.Broken) || (parser.State == TdsParserState.Closed)) {
4726                     // Connection's parser is null as well, therefore we must be closed
4727                     throw ADP.ClosedConnectionError();
4728                 }
4729             }
4730
4731             TdsParserStateObject stateObj = parser.GetSession(this);            
4732             stateObj.StartSession(ObjectID);
4733
4734             _stateObj = stateObj;
4735
4736             if (_pendingCancel) {
4737                 _pendingCancel = false; // Not really needed, but we'll reset anyways.
4738
4739                 // If a pendingCancel exists on the object, we must have had a Cancel() call
4740                 // between the point that we entered this function and the point where we obtained
4741                 // and actually assigned the stateObject to the local member.  It is possible
4742                 // that the flag is set as well as a call to stateObj.Cancel - though that would
4743                 // be a no-op.  So - throw.
4744                 throw SQL.OperationCancelled();
4745             }
4746          }
4747
4748         private void ReliablePutStateObject() {
4749             TdsParser bestEffortCleanupTarget = null;
4750             RuntimeHelpers.PrepareConstrainedRegions();
4751             try {
4752 #if DEBUG
4753                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
4754
4755                 RuntimeHelpers.PrepareConstrainedRegions();
4756                 try {
4757                     tdsReliabilitySection.Start();
4758 #else
4759                 {
4760 #endif //DEBUG
4761                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
4762                     PutStateObject();
4763
4764                 }
4765 #if DEBUG
4766                 finally {
4767                     tdsReliabilitySection.Stop();
4768                 }
4769 #endif //DEBUG
4770             }
4771             catch (System.OutOfMemoryException e)
4772             {
4773                 _activeConnection.Abort(e);
4774                 throw;
4775             }
4776             catch (System.StackOverflowException e)
4777             {
4778                 _activeConnection.Abort(e);
4779                 throw;
4780             }
4781             catch (System.Threading.ThreadAbortException e)
4782             {
4783                 _activeConnection.Abort(e);
4784                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
4785                 throw;
4786             }
4787         }
4788
4789         private void PutStateObject() {
4790             TdsParserStateObject stateObj = _stateObj;
4791             _stateObj = null;
4792
4793             if (null != stateObj) {
4794                 stateObj.CloseSession();
4795             }
4796         }
4797
4798         /// <summary>
4799         /// IMPORTANT NOTE: This is created as a copy of OnDoneProc below for Transparent Column Encryption improvement
4800         /// as there is not much time, to address regressions. Will revisit removing the duplication, when we have time again.
4801         /// </summary>
4802         internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateObj) {
4803             // called per rpc batch complete
4804             if (BatchRPCMode) {
4805                 // track the records affected for the just completed rpc batch
4806                 // _rowsAffected is cumulative for ExecuteNonQuery across all rpc batches
4807                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].cumulativeRecordsAffected = _rowsAffected;
4808
4809                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].recordsAffected =
4810                     (((0 < _currentlyExecutingDescribeParameterEncryptionRPC) && (0 <= _rowsAffected))
4811                         ? (_rowsAffected - Math.Max(_sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC - 1].cumulativeRecordsAffected, 0))
4812                         : _rowsAffected);
4813
4814                 // track the error collection (not available from TdsParser after ExecuteNonQuery)
4815                 // and the which errors are associated with the just completed rpc batch
4816                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].errorsIndexStart =
4817                     ((0 < _currentlyExecutingDescribeParameterEncryptionRPC)
4818                         ? _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC - 1].errorsIndexEnd
4819                         : 0);
4820                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].errorsIndexEnd = stateObj.ErrorCount;
4821                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].errors = stateObj._errors;
4822
4823                 // track the warning collection (not available from TdsParser after ExecuteNonQuery)
4824                 // and the which warnings are associated with the just completed rpc batch
4825                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].warningsIndexStart =
4826                     ((0 < _currentlyExecutingDescribeParameterEncryptionRPC)
4827                         ? _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC - 1].warningsIndexEnd
4828                         : 0);
4829                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].warningsIndexEnd = stateObj.WarningCount;
4830                 _sqlRPCParameterEncryptionReqArray[_currentlyExecutingDescribeParameterEncryptionRPC].warnings = stateObj._warnings;
4831
4832                 _currentlyExecutingDescribeParameterEncryptionRPC++;
4833             }
4834         }
4835
4836         /// <summary>
4837         /// IMPORTANT NOTE: There is a copy of this function above in OnDoneDescribeParameterEncryptionProc.
4838         /// Please consider the changes being done in this function for the above function as well.
4839         /// </summary>
4840         internal void OnDoneProc() { // called per rpc batch complete
4841             if (BatchRPCMode) {
4842
4843                 // track the records affected for the just completed rpc batch
4844                 // _rowsAffected is cumulative for ExecuteNonQuery across all rpc batches
4845                 _SqlRPCBatchArray[_currentlyExecutingBatch].cumulativeRecordsAffected = _rowsAffected;
4846
4847                 _SqlRPCBatchArray[_currentlyExecutingBatch].recordsAffected =
4848                     (((0 < _currentlyExecutingBatch) && (0 <= _rowsAffected))
4849                         ? (_rowsAffected - Math.Max(_SqlRPCBatchArray[_currentlyExecutingBatch-1].cumulativeRecordsAffected, 0))
4850                         : _rowsAffected);
4851
4852                 // track the error collection (not available from TdsParser after ExecuteNonQuery)
4853                 // and the which errors are associated with the just completed rpc batch
4854                 _SqlRPCBatchArray[_currentlyExecutingBatch].errorsIndexStart =
4855                     ((0 < _currentlyExecutingBatch)
4856                         ? _SqlRPCBatchArray[_currentlyExecutingBatch-1].errorsIndexEnd
4857                         : 0);
4858                 _SqlRPCBatchArray[_currentlyExecutingBatch].errorsIndexEnd = _stateObj.ErrorCount;
4859                 _SqlRPCBatchArray[_currentlyExecutingBatch].errors = _stateObj._errors;
4860                 
4861                 // track the warning collection (not available from TdsParser after ExecuteNonQuery)
4862                 // and the which warnings are associated with the just completed rpc batch
4863                 _SqlRPCBatchArray[_currentlyExecutingBatch].warningsIndexStart =
4864                     ((0 < _currentlyExecutingBatch)
4865                         ? _SqlRPCBatchArray[_currentlyExecutingBatch-1].warningsIndexEnd
4866                         : 0);
4867                 _SqlRPCBatchArray[_currentlyExecutingBatch].warningsIndexEnd = _stateObj.WarningCount;
4868                 _SqlRPCBatchArray[_currentlyExecutingBatch].warnings = _stateObj._warnings;
4869
4870                 _currentlyExecutingBatch++;
4871                 Debug.Assert(_parameterCollectionList.Count >= _currentlyExecutingBatch, "OnDoneProc: Too many DONEPROC events");
4872             }
4873         }
4874
4875         //
4876         // 
4877
4878
4879         internal void OnReturnStatus(int status) {
4880             if (_inPrepare)
4881                 return;
4882
4883             // Don't set the return status if this is the status for sp_describe_parameter_encryption.
4884             if (IsDescribeParameterEncryptionRPCCurrentlyInProgress)
4885                 return;
4886
4887             SqlParameterCollection parameters = _parameters;
4888             if (BatchRPCMode) {
4889                 if (_parameterCollectionList.Count > _currentlyExecutingBatch) {
4890                     parameters = _parameterCollectionList[_currentlyExecutingBatch];
4891                 }
4892                 else {
4893                     Debug.Assert(false, "OnReturnStatus: SqlCommand got too many DONEPROC events");
4894                     parameters = null;
4895                 }
4896             }
4897             // see if a return value is bound
4898             int count = GetParameterCount(parameters);
4899             for (int i = 0; i < count; i++) {
4900                 SqlParameter parameter = parameters[i];
4901                 if (parameter.Direction == ParameterDirection.ReturnValue) {
4902                     object v = parameter.Value;
4903
4904                 // if the user bound a sqlint32 (the only valid one for status, use it)
4905                 if ( (null != v) && (v.GetType() == typeof(SqlInt32)) ) {
4906                         parameter.Value = new SqlInt32(status); // value type
4907                 }
4908                 else {
4909                         parameter.Value = status;
4910
4911                     }
4912
4913                 // If we are not in Batch RPC mode, update the query cache with the encryption MD.
4914                 // We can do this now that we have distinguished between ReturnValue and ReturnStatus.
4915                 // Read comment in AddQueryMetadata() for more details.
4916                 if (!BatchRPCMode && CachingQueryMetadataPostponed) {
4917                     SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: false);
4918                 }
4919
4920                     break;
4921                 }
4922             }
4923         }
4924
4925         //
4926         // Move the return value to the corresponding output parameter.
4927         // Return parameters are sent in the order in which they were defined in the procedure.
4928         // If named, match the parameter name, otherwise fill in based on ordinal position.
4929         // If the parameter is not bound, then ignore the return value.
4930         //
4931         internal void OnReturnValue(SqlReturnValue rec, TdsParserStateObject stateObj) {
4932
4933             if (_inPrepare) {
4934                 if (!rec.value.IsNull) {
4935                     _prepareHandle = rec.value.Int32;
4936                 }
4937                 _inPrepare = false;
4938                 return;
4939             }
4940
4941             SqlParameterCollection parameters = GetCurrentParameterCollection();
4942             int  count      = GetParameterCount(parameters);
4943
4944
4945             SqlParameter thisParam = GetParameterForOutputValueExtraction(parameters, rec.parameter, count);
4946
4947             if (null != thisParam) {
4948                 // If the parameter's direction is InputOutput, Output or ReturnValue and it needs to be transparently encrypted/decrypted
4949                 // then simply decrypt, deserialize and set the value.
4950                 if (rec.cipherMD != null &&
4951                     thisParam.CipherMetadata != null && 
4952                     (thisParam.Direction == ParameterDirection.Output || 
4953                     thisParam.Direction == ParameterDirection.InputOutput || 
4954                     thisParam.Direction == ParameterDirection.ReturnValue)) {
4955                     if(rec.tdsType != TdsEnums.SQLBIGVARBINARY) {
4956                         throw SQL.InvalidDataTypeForEncryptedParameter(thisParam.ParameterNameFixed, rec.tdsType, TdsEnums.SQLBIGVARBINARY);
4957                     }
4958
4959                     // Decrypt the ciphertext
4960                     TdsParser parser = _activeConnection.Parser;
4961                     if ((parser == null) || (parser.State == TdsParserState.Closed) || (parser.State == TdsParserState.Broken)) {
4962                         throw ADP.ClosedConnectionError();
4963                     }
4964
4965                     if (!rec.value.IsNull) {
4966                         try {
4967                             Debug.Assert(_activeConnection != null, @"_activeConnection should not be null");
4968
4969                             // Get the key information from the parameter and decrypt the value.
4970                             rec.cipherMD.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo;
4971                             byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection.DataSource);
4972
4973                             if (unencryptedBytes != null) {
4974                                 // Denormalize the value and convert it to the parameter type.
4975                                 SqlBuffer buffer = new SqlBuffer();
4976                                 parser.DeserializeUnencryptedValue(buffer, unencryptedBytes, rec, stateObj, rec.NormalizationRuleVersion);
4977                                 thisParam.SetSqlBuffer(buffer);
4978                             }
4979                         }
4980                         catch (Exception e) {
4981                             throw SQL.ParamDecryptionFailed(thisParam.ParameterNameFixed, null, e);
4982                         }
4983                     }
4984                     else {
4985                         // Create a new SqlBuffer and set it to null
4986                         // Note: We can't reuse the SqlBuffer in "rec" below since it's already been set (to varbinary)
4987                         // in previous call to TryProcessReturnValue(). 
4988                         // Note 2: We will be coming down this code path only if the Command Setting is set to use TCE.
4989                         // We pass the command setting as TCE enabled in the below call for this reason.
4990                         SqlBuffer buff = new SqlBuffer();
4991                         TdsParser.GetNullSqlValue(buff, rec, SqlCommandColumnEncryptionSetting.Enabled, parser.Connection);
4992                         thisParam.SetSqlBuffer(buff);
4993                     }
4994                 }
4995                 else {
4996                     // copy over data
4997
4998                     // if the value user has supplied a SqlType class, then just copy over the SqlType, otherwise convert
4999                     // to the com type
5000                     object val = thisParam.Value;
5001
5002                     //set the UDT value as typed object rather than bytes
5003                     if (SqlDbType.Udt == thisParam.SqlDbType) {
5004                         object data = null;
5005                         try {
5006                             Connection.CheckGetExtendedUDTInfo(rec, true);
5007
5008                             //extract the byte array from the param value
5009                             if (rec.value.IsNull)
5010                                 data = DBNull.Value;
5011                             else {
5012                                 data = rec.value.ByteArray; //should work for both sql and non-sql values
5013                             }
5014
5015                             //call the connection to instantiate the UDT object
5016                             thisParam.Value = Connection.GetUdtValue(data, rec, false);
5017                         }
5018                         catch (FileNotFoundException e) {
5019                             // SQL BU DT 329981
5020                             // Assign Assembly.Load failure in case where assembly not on client.
5021                             // This allows execution to complete and failure on SqlParameter.Value.
5022                             thisParam.SetUdtLoadError(e);
5023                         }
5024                         catch (FileLoadException e) {
5025                             // SQL BU DT 329981
5026                             // Assign Assembly.Load failure in case where assembly cannot be loaded on client.
5027                             // This allows execution to complete and failure on SqlParameter.Value.
5028                             thisParam.SetUdtLoadError(e);
5029                         }
5030
5031                         return;
5032                     } else {
5033                         thisParam.SetSqlBuffer(rec.value);
5034                     }
5035
5036                     MetaType mt = MetaType.GetMetaTypeFromSqlDbType(rec.type, rec.isMultiValued);
5037
5038                     if (rec.type == SqlDbType.Decimal) {
5039                         thisParam.ScaleInternal = rec.scale;
5040                         thisParam.PrecisionInternal = rec.precision;
5041                     }
5042                     else if (mt.IsVarTime) {
5043                         thisParam.ScaleInternal = rec.scale;
5044                     }
5045                     else if (rec.type == SqlDbType.Xml) {
5046                         SqlCachedBuffer cachedBuffer = (thisParam.Value as SqlCachedBuffer);
5047                         if (null != cachedBuffer) {
5048                             thisParam.Value = cachedBuffer.ToString();
5049                         }
5050                     }
5051
5052                     if (rec.collation != null) {
5053                         Debug.Assert(mt.IsCharType, "Invalid collation structure for non-char type");
5054                         thisParam.Collation = rec.collation;
5055                     }
5056                 }
5057             }
5058
5059             return;
5060         }
5061
5062         internal void OnParametersAvailableSmi( SmiParameterMetaData[] paramMetaData, ITypedGettersV3 parameterValues ) {
5063             Debug.Assert(null != paramMetaData);
5064
5065             for(int index=0; index < paramMetaData.Length; index++) {
5066                 OnParameterAvailableSmi(paramMetaData[index], parameterValues, index);
5067             }
5068         }
5069
5070         internal void OnParameterAvailableSmi(SmiParameterMetaData metaData, ITypedGettersV3 parameterValues, int ordinal) {
5071             if ( ParameterDirection.Input != metaData.Direction ) {
5072                 string name = null;
5073                 if (ParameterDirection.ReturnValue != metaData.Direction) {
5074                     name = metaData.Name;
5075                 }
5076
5077                 SqlParameterCollection parameters = GetCurrentParameterCollection();
5078                 int  count      = GetParameterCount(parameters);
5079                 SqlParameter param = GetParameterForOutputValueExtraction(parameters, name, count);
5080
5081                 if ( null != param ) {
5082                     param.LocaleId = (int)metaData.LocaleId;
5083                     param.CompareInfo = metaData.CompareOptions;
5084                     SqlBuffer buffer = new SqlBuffer();
5085                     object result;
5086                     if (_activeConnection.IsKatmaiOrNewer) {
5087                         result = ValueUtilsSmi.GetOutputParameterV200Smi(
5088                                 OutParamEventSink, (SmiTypedGetterSetter)parameterValues, ordinal, metaData, _smiRequestContext, buffer );
5089                     }
5090                     else {
5091                         result = ValueUtilsSmi.GetOutputParameterV3Smi( 
5092                                     OutParamEventSink, parameterValues, ordinal, metaData, _smiRequestContext, buffer );
5093                     }
5094                     if ( null != result ) {
5095                         param.Value = result;
5096                     }
5097                     else {
5098                         param.SetSqlBuffer( buffer );
5099                     }
5100                 }
5101             }
5102         }
5103
5104         private SqlParameterCollection GetCurrentParameterCollection() {
5105             if (BatchRPCMode) {
5106                 if (_parameterCollectionList.Count > _currentlyExecutingBatch) {
5107                     return _parameterCollectionList[_currentlyExecutingBatch];
5108                 }
5109                 else {
5110                     Debug.Assert(false, "OnReturnValue: SqlCommand got too many DONEPROC events");
5111                     return null;
5112                 }
5113             }
5114             else {
5115                 return _parameters;
5116             }
5117         }
5118
5119         private SqlParameter GetParameterForOutputValueExtraction( SqlParameterCollection parameters,
5120                         string paramName, int paramCount ) {
5121             SqlParameter thisParam = null;
5122             bool foundParam = false;
5123
5124             if (null == paramName) {
5125                 // rec.parameter should only be null for a return value from a function
5126                 for (int i = 0; i < paramCount; i++) {
5127                     thisParam = parameters[i];
5128                     // searching for ReturnValue
5129                     if (thisParam.Direction == ParameterDirection.ReturnValue) {
5130                                 foundParam = true;
5131                             break; // found it
5132                     }
5133                 }
5134             }
5135             else {
5136                 for (int i = 0; i < paramCount; i++) {
5137                     thisParam = parameters[i];
5138                     // searching for Output or InputOutput or ReturnValue with matching name
5139                     if (thisParam.Direction != ParameterDirection.Input && thisParam.Direction != ParameterDirection.ReturnValue  && paramName == thisParam.ParameterNameFixed) {
5140                                 foundParam = true;
5141                             break; // found it
5142                         }
5143                     }
5144             }
5145             if (foundParam)
5146                 return thisParam;
5147             else
5148                 return null;
5149         }
5150
5151         private void GetRPCObject(int paramCount, ref _SqlRPC rpc, bool forSpDescribeParameterEncryption = false) {
5152             // Designed to minimize necessary allocations
5153             int ii;
5154             if (rpc == null) {
5155                 if (!forSpDescribeParameterEncryption) {
5156                     if (_rpcArrayOf1 == null) {
5157                         _rpcArrayOf1 = new _SqlRPC[1];
5158                         _rpcArrayOf1[0] = new _SqlRPC();
5159                     }
5160
5161                     rpc = _rpcArrayOf1[0];
5162                 }
5163                 else {
5164                     if (_rpcForEncryption == null) {
5165                         _rpcForEncryption = new _SqlRPC();
5166                     }
5167
5168                     rpc = _rpcForEncryption;
5169                 }
5170             }
5171
5172             rpc.ProcID = 0;
5173             rpc.rpcName = null;
5174             rpc.options = 0;
5175
5176             rpc.recordsAffected = default(int?);
5177             rpc.cumulativeRecordsAffected = -1;
5178
5179             rpc.errorsIndexStart = 0;
5180             rpc.errorsIndexEnd = 0;
5181             rpc.errors = null;
5182             
5183             rpc.warningsIndexStart = 0;
5184             rpc.warningsIndexEnd = 0;
5185             rpc.warnings = null;
5186             rpc.needsFetchParameterEncryptionMetadata = false;
5187
5188             // Make sure there is enough space in the parameters and paramoptions arrays
5189             if(rpc.parameters == null || rpc.parameters.Length < paramCount) {
5190                 rpc.parameters = new SqlParameter[paramCount];
5191             }
5192             else if (rpc.parameters.Length > paramCount) {
5193                         rpc.parameters[paramCount]=null;    // Terminator
5194             }
5195             if(rpc.paramoptions == null || (rpc.paramoptions.Length < paramCount)) {
5196                 rpc.paramoptions = new byte[paramCount];
5197             }
5198             else {
5199                 for (ii = 0 ; ii < paramCount ; ii++)
5200                     rpc.paramoptions[ii] = 0;
5201             }
5202         }
5203
5204         private void SetUpRPCParameters (_SqlRPC rpc, int startCount, bool inSchema, SqlParameterCollection parameters) {
5205             int ii;
5206             int paramCount = GetParameterCount(parameters) ;
5207             int j = startCount;
5208             TdsParser parser = _activeConnection.Parser;
5209             bool yukonOrNewer = parser.IsYukonOrNewer;
5210
5211             for (ii = 0;  ii < paramCount; ii++) {
5212                 SqlParameter parameter = parameters[ii];
5213                 parameter.Validate(ii, CommandType.StoredProcedure == CommandType);
5214
5215                 // func will change type to that with a 4 byte length if the type has a two
5216                 // byte length and a parameter length > than that expressable in 2 bytes
5217                 if ((!parameter.ValidateTypeLengths(yukonOrNewer).IsPlp) && (parameter.Direction != ParameterDirection.Output)) {
5218                     parameter.FixStreamDataForNonPLP();
5219                 }
5220
5221                 if (ShouldSendParameter(parameter)) {
5222                     rpc.parameters[j] = parameter;
5223
5224                     // set output bit
5225                     if (parameter.Direction == ParameterDirection.InputOutput ||
5226                         parameter.Direction == ParameterDirection.Output)
5227                         rpc.paramoptions[j] = TdsEnums.RPC_PARAM_BYREF;
5228
5229                     // Set the encryped bit, if the parameter is to be encrypted.
5230                     if (parameter.CipherMetadata != null) {
5231                         rpc.paramoptions[j] |= TdsEnums.RPC_PARAM_ENCRYPTED;
5232                     }
5233
5234                     // set default value bit
5235                     if (parameter.Direction != ParameterDirection.Output) {
5236                         // remember that null == Convert.IsEmpty, DBNull.Value is a database null!
5237
5238                         // MDAC 62117, don't assume a default value exists for parameters in the case when
5239                         // the user is simply requesting schema
5240                         // SQLBUVSTS 179488 TVPs use DEFAULT and do not allow NULL, even for schema only.
5241                         if (null == parameter.Value && (!inSchema || SqlDbType.Structured == parameter.SqlDbType)) {
5242                             rpc.paramoptions[j] |= TdsEnums.RPC_PARAM_DEFAULT;
5243                         }
5244                     }
5245
5246                     // Must set parameter option bit for LOB_COOKIE if unfilled LazyMat blob
5247                     j++;
5248                 }
5249             }
5250
5251         }
5252
5253         //
5254         // 7.5
5255         // prototype for sp_prepexec is:
5256         // sp_prepexec(@handle int IN/OUT, @batch_params ntext, @batch_text ntext, param1value,param2value...)
5257         //
5258         private _SqlRPC  BuildPrepExec(CommandBehavior behavior) {
5259             Debug.Assert(System.Data.CommandType.Text == this.CommandType, "invalid use of sp_prepexec for stored proc invocation!");
5260             SqlParameter sqlParam;
5261             int j = 3;
5262
5263             int count = CountSendableParameters(_parameters);
5264
5265             _SqlRPC rpc = null;
5266             GetRPCObject(count + j, ref rpc);
5267
5268             rpc.ProcID = TdsEnums.RPC_PROCID_PREPEXEC;
5269             rpc.rpcName = TdsEnums.SP_PREPEXEC;
5270
5271             //@handle
5272             sqlParam = new SqlParameter(null, SqlDbType.Int);
5273             sqlParam.Direction = ParameterDirection.InputOutput;
5274             sqlParam.Value = _prepareHandle;
5275             rpc.parameters[0] = sqlParam;
5276             rpc.paramoptions[0] = TdsEnums.RPC_PARAM_BYREF;
5277
5278             //@batch_params
5279             string paramList = BuildParamList(_stateObj.Parser, _parameters);
5280             sqlParam = new SqlParameter(null, ((paramList.Length<<1)<=TdsEnums.TYPE_SIZE_LIMIT)?SqlDbType.NVarChar:SqlDbType.NText, paramList.Length);
5281             sqlParam.Value = paramList;
5282             rpc.parameters[1] = sqlParam;
5283
5284             //@batch_text
5285             string text = GetCommandText(behavior);
5286             sqlParam = new SqlParameter(null, ((text.Length<<1)<=TdsEnums.TYPE_SIZE_LIMIT)?SqlDbType.NVarChar:SqlDbType.NText, text.Length);
5287             sqlParam.Value = text;
5288             rpc.parameters[2] = sqlParam;
5289
5290             SetUpRPCParameters (rpc,  j, false, _parameters);
5291             return rpc;
5292         }
5293
5294
5295         //
5296         // returns true if the parameter is not a return value
5297         // and it's value is not DBNull (for a nullable parameter)
5298         //
5299         private static bool ShouldSendParameter(SqlParameter p, bool includeReturnValue = false) {
5300             switch (p.Direction) {
5301             case ParameterDirection.ReturnValue:
5302                 // return value parameters are not sent, except for the parameter list of sp_describe_parameter_encryption
5303                 return includeReturnValue;
5304             case ParameterDirection.Output:
5305             case ParameterDirection.InputOutput:
5306             case ParameterDirection.Input:
5307                 // InputOutput/Output parameters are aways sent
5308                 return true;
5309             default:
5310                 Debug.Assert(false, "Invalid ParameterDirection!");
5311                 return false;
5312             }
5313         }
5314
5315         private int CountSendableParameters(SqlParameterCollection parameters) {
5316             int cParams = 0;
5317
5318             if (parameters != null) {
5319                 int count = parameters.Count;
5320                 for (int i = 0; i < count; i++) {
5321                     if (ShouldSendParameter(parameters[i]))
5322                         cParams++;
5323                 }
5324             }
5325             return cParams;
5326         }
5327
5328         // Returns total number of parameters
5329         private int GetParameterCount(SqlParameterCollection parameters) {
5330             return ((null != parameters) ? parameters.Count : 0);
5331         }
5332
5333         //
5334         // build the RPC record header for this stored proc and add parameters
5335         //
5336         private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _SqlRPC rpc) {
5337             Debug.Assert(this.CommandType == System.Data.CommandType.StoredProcedure, "Command must be a stored proc to execute an RPC");
5338             int count = CountSendableParameters(parameters);
5339             GetRPCObject(count, ref rpc);
5340
5341             rpc.rpcName = this.CommandText; // just get the raw command text
5342
5343             SetUpRPCParameters ( rpc, 0, inSchema, parameters);
5344         }
5345
5346         //
5347         // build the RPC record header for sp_unprepare
5348         //
5349         // prototype for sp_unprepare is:
5350         // sp_unprepare(@handle)
5351         //
5352         // 
5353         private _SqlRPC BuildUnprepare() {
5354             Debug.Assert(_prepareHandle != 0, "Invalid call to sp_unprepare without a valid handle!");
5355
5356             _SqlRPC rpc = null;
5357             GetRPCObject(1, ref rpc);
5358             SqlParameter sqlParam;
5359
5360             rpc.ProcID = TdsEnums.RPC_PROCID_UNPREPARE;
5361             rpc.rpcName = TdsEnums.SP_UNPREPARE;
5362
5363             //@handle
5364             sqlParam = new SqlParameter(null, SqlDbType.Int);
5365             sqlParam.Value = _prepareHandle;
5366             rpc.parameters[0] = sqlParam;
5367
5368             return rpc;
5369         }
5370
5371         //
5372         // build the RPC record header for sp_execute
5373         //
5374         // prototype for sp_execute is:
5375         // sp_execute(@handle int,param1value,param2value...)
5376         //
5377         private _SqlRPC BuildExecute(bool inSchema) {
5378             Debug.Assert(_prepareHandle != -1, "Invalid call to sp_execute without a valid handle!");
5379             int j = 1;
5380
5381             int count = CountSendableParameters(_parameters);
5382
5383             _SqlRPC rpc = null;
5384             GetRPCObject(count + j, ref rpc);
5385
5386             SqlParameter sqlParam;
5387
5388             rpc.ProcID = TdsEnums.RPC_PROCID_EXECUTE;
5389             rpc.rpcName = TdsEnums.SP_EXECUTE;
5390
5391             //@handle
5392             sqlParam = new SqlParameter(null, SqlDbType.Int);
5393             sqlParam.Value = _prepareHandle;
5394             rpc.parameters[0] = sqlParam;
5395
5396             SetUpRPCParameters (rpc, j, inSchema, _parameters);
5397             return rpc;
5398         }
5399
5400         //
5401         // build the RPC record header for sp_executesql and add the parameters
5402         //
5403         // prototype for sp_executesql is:
5404         // sp_executesql(@batch_text nvarchar(4000),@batch_params nvarchar(4000), param1,.. paramN)
5405         private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlParameterCollection parameters, ref _SqlRPC rpc) {
5406
5407             Debug.Assert(_prepareHandle == -1, "This command has an existing handle, use sp_execute!");
5408             Debug.Assert(System.Data.CommandType.Text == this.CommandType, "invalid use of sp_executesql for stored proc invocation!");
5409             int j;
5410             SqlParameter sqlParam;
5411
5412             int cParams = CountSendableParameters(parameters);
5413             if (cParams > 0) {
5414                 j = 2;
5415             }
5416             else {
5417                 j =1;
5418             }
5419
5420             GetRPCObject(cParams + j, ref rpc);
5421             rpc.ProcID = TdsEnums.RPC_PROCID_EXECUTESQL;
5422             rpc.rpcName = TdsEnums.SP_EXECUTESQL;
5423
5424             // @sql
5425             if (commandText == null) {
5426                 commandText = GetCommandText(behavior);
5427             }
5428             sqlParam = new SqlParameter(null, ((commandText.Length<<1)<=TdsEnums.TYPE_SIZE_LIMIT)?SqlDbType.NVarChar:SqlDbType.NText, commandText.Length);
5429             sqlParam.Value = commandText;
5430             rpc.parameters[0] = sqlParam;
5431
5432             if (cParams > 0) {
5433                 string paramList = BuildParamList(_stateObj.Parser, BatchRPCMode  ? parameters : _parameters);
5434                 sqlParam = new SqlParameter(null, ((paramList.Length<<1)<=TdsEnums.TYPE_SIZE_LIMIT)?SqlDbType.NVarChar:SqlDbType.NText, paramList.Length);
5435                 sqlParam.Value = paramList;
5436                 rpc.parameters[1] = sqlParam;
5437
5438                 bool inSchema =  (0 != (behavior & CommandBehavior.SchemaOnly));
5439                 SetUpRPCParameters (rpc, j,  inSchema, parameters);
5440             }
5441         }
5442
5443         /// <summary>
5444         /// This function constructs a string parameter containing the exec statement in the following format
5445         /// N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN'
5446         /// 
5447
5448
5449
5450
5451         private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string storedProcedureName, SqlParameter[] parameters) {
5452             Debug.Assert(CommandType == CommandType.StoredProcedure, "BuildStoredProcedureStatementForColumnEncryption() should only be called for stored procedures");
5453             Debug.Assert(!string.IsNullOrWhiteSpace(storedProcedureName), "storedProcedureName cannot be null or empty in BuildStoredProcedureStatementForColumnEncryption");
5454             Debug.Assert(parameters != null, "parameters cannot be null in BuildStoredProcedureStatementForColumnEncryption");
5455
5456             StringBuilder execStatement = new StringBuilder();
5457             execStatement.Append(@"EXEC ");
5458
5459             // Find the return value parameter (if any).
5460             SqlParameter returnValueParameter = null;
5461             foreach (SqlParameter parameter in parameters) {
5462                 if (parameter.Direction == ParameterDirection.ReturnValue) {
5463                     returnValueParameter = parameter;
5464                     break;
5465                 }
5466             }
5467
5468             // If there is a return value parameter we need to assign the result to it.
5469             // EXEC @returnValue = moduleName [parameters]
5470             if (returnValueParameter != null) {
5471                 execStatement.AppendFormat(@"{0}=", returnValueParameter.ParameterNameFixed);
5472             }
5473
5474             execStatement.Append(ParseAndQuoteIdentifier(storedProcedureName, false));
5475
5476             // Build parameter list in the format
5477             // @param1=@param1, @param1=@param2, ..., @paramn=@paramn
5478
5479             // Append the first parameter
5480             int i = 0;
5481
5482             if(parameters.Count() > 0) {
5483                 // Skip the return value parameters.
5484                 while (i < parameters.Count() && parameters[i].Direction == ParameterDirection.ReturnValue) {
5485                     i++;
5486                 }
5487
5488                 if (i < parameters.Count()) {
5489                     // Possibility of a SQL Injection issue through parameter names and how to construct valid identifier for parameters.
5490                     // Since the parameters comes from application itself, there should not be a security vulnerability.
5491                     // Also since the query is not executed, but only analyzed there is no possibility for elevation of priviledge, but only for 
5492                     // incorrect results which would only affect the user that attempts the injection.
5493                     execStatement.AppendFormat(@" {0}={0}", parameters[i].ParameterNameFixed);
5494                 
5495                     // InputOutput and Output parameters need to be marked as such.
5496                     if (parameters[i].Direction == ParameterDirection.Output ||
5497                         parameters[i].Direction == ParameterDirection.InputOutput) {
5498                         execStatement.AppendFormat(@" OUTPUT");
5499                     }
5500                 }
5501             }
5502
5503             // Move to the next parameter.
5504             i++;
5505
5506             // Append the rest of parameters
5507             for (; i < parameters.Count(); i++) {
5508                 if (parameters[i].Direction != ParameterDirection.ReturnValue) {
5509                     execStatement.AppendFormat(@", {0}={0}", parameters[i].ParameterNameFixed);
5510
5511                     // InputOutput and Output parameters need to be marked as such.
5512                     if (parameters[i].Direction == ParameterDirection.Output ||
5513                     parameters[i].Direction == ParameterDirection.InputOutput) {
5514                         execStatement.AppendFormat(@" OUTPUT");
5515                     }
5516                 }
5517             }
5518
5519             // Construct @tsql SqlParameter to be returned
5520             SqlParameter tsqlParameter = new SqlParameter(null, ((execStatement.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, execStatement.Length);
5521             tsqlParameter.Value = execStatement.ToString();
5522
5523             return tsqlParameter;
5524         }
5525
5526         // paramList parameter for sp_executesql, sp_prepare, and sp_prepexec
5527         internal string BuildParamList(TdsParser parser, SqlParameterCollection parameters, bool includeReturnValue = false) {
5528             StringBuilder paramList = new StringBuilder();
5529             bool fAddSeperator = false;
5530
5531             bool yukonOrNewer = parser.IsYukonOrNewer;
5532
5533             int count = 0;
5534
5535             count = parameters.Count;
5536             for (int i = 0; i < count; i++) {
5537                 SqlParameter sqlParam = parameters[i];
5538                 sqlParam.Validate(i, CommandType.StoredProcedure == CommandType);
5539                 // skip ReturnValue parameters; we never send them to the server
5540                 if (!ShouldSendParameter(sqlParam, includeReturnValue))
5541                     continue;
5542
5543                 // add our separator for the ith parmeter
5544                 if (fAddSeperator)
5545                     paramList.Append(',');
5546
5547                 paramList.Append(sqlParam.ParameterNameFixed);
5548
5549                 MetaType mt = sqlParam.InternalMetaType;
5550
5551                 //for UDTs, get the actual type name. Get only the typename, omitt catalog and schema names.
5552                 //in TSQL you should only specify the unqualified type name
5553
5554                 // paragraph above doesn't seem to be correct. Server won't find the type
5555                 // if we don't provide a fully qualified name
5556                 paramList.Append(" ");
5557                 if (mt.SqlDbType == SqlDbType.Udt) {
5558                     string fullTypeName = sqlParam.UdtTypeName;
5559                     if(ADP.IsEmpty(fullTypeName))
5560                         throw SQL.MustSetUdtTypeNameForUdtParams();
5561                     // DEVNOTE: do we need to escape the full type name?
5562                     paramList.Append(ParseAndQuoteIdentifier(fullTypeName, true /* is UdtTypeName */));
5563                 }
5564                 else if (mt.SqlDbType == SqlDbType.Structured) {
5565                     string typeName = sqlParam.TypeName;
5566                     if (ADP.IsEmpty(typeName)) {
5567                         throw SQL.MustSetTypeNameForParam(mt.TypeName, sqlParam.ParameterNameFixed);
5568                     }
5569                     paramList.Append(ParseAndQuoteIdentifier(typeName, false /* is not UdtTypeName*/));
5570
5571                     // TVPs currently are the only Structured type and must be read only, so add that keyword
5572                     paramList.Append(" READONLY");
5573                 }
5574                 else {
5575                     // func will change type to that with a 4 byte length if the type has a two
5576                     // byte length and a parameter length > than that expressable in 2 bytes
5577                     mt  = sqlParam.ValidateTypeLengths(yukonOrNewer);
5578                     if ((!mt.IsPlp) && (sqlParam.Direction != ParameterDirection.Output)) {
5579                         sqlParam.FixStreamDataForNonPLP();
5580                     }
5581                     paramList.Append(mt.TypeName);
5582                 }
5583
5584                 fAddSeperator = true;
5585
5586                 if (mt.SqlDbType == SqlDbType.Decimal) {
5587                     byte precision = sqlParam.GetActualPrecision();
5588                     byte scale = sqlParam.GetActualScale();
5589
5590                     paramList.Append('(');
5591
5592                     if (0 == precision) {
5593                         if (IsShiloh) {
5594                             precision = TdsEnums.DEFAULT_NUMERIC_PRECISION;
5595                         } else {
5596                             precision = TdsEnums.SPHINX_DEFAULT_NUMERIC_PRECISION;
5597                         }
5598                     }
5599
5600                     paramList.Append(precision);
5601                     paramList.Append(',');
5602                     paramList.Append(scale);
5603                     paramList.Append(')');
5604                 }
5605                 else if (mt.IsVarTime) {
5606                     byte scale = sqlParam.GetActualScale();
5607
5608                     paramList.Append('(');
5609                     paramList.Append(scale);
5610                     paramList.Append(')');
5611                 }
5612                 else if (false == mt.IsFixed && false == mt.IsLong && mt.SqlDbType != SqlDbType.Timestamp && mt.SqlDbType != SqlDbType.Udt && SqlDbType.Structured != mt.SqlDbType) {
5613                     int size = sqlParam.Size;
5614
5615                     paramList.Append('(');
5616
5617                     // if using non unicode types, obtain the actual byte length from the parser, with it's associated code page
5618                     if (mt.IsAnsiType) {
5619                         object val = sqlParam.GetCoercedValue();
5620                         string s = null;
5621
5622                         // deal with the sql types
5623                         if ((null != val) && (DBNull.Value != val)) {
5624                             s = (val as string);
5625                             if (null == s) {
5626                                 SqlString sval = val is SqlString ? (SqlString)val : SqlString.Null;
5627                                 if (!sval.IsNull) {
5628                                     s = sval.Value;
5629                                 }
5630                             }
5631                         }
5632
5633                         if (null != s) {
5634                             int actualBytes = parser.GetEncodingCharLength(s, sqlParam.GetActualSize(), sqlParam.Offset, null);
5635                             // if actual number of bytes is greater than the user given number of chars, use actual bytes
5636                             if (actualBytes > size)
5637                                 size = actualBytes;
5638                         }
5639                     }
5640
5641                     // bug 49497, if the user specifies a 0-sized parameter for a variable len field
5642                     // pass over max size (8000 bytes or 4000 characters for wide types)
5643                     if (0 == size)
5644                         size = mt.IsSizeInCharacters ? (TdsEnums.MAXSIZE >> 1) : TdsEnums.MAXSIZE;
5645
5646                     paramList.Append(size);
5647                     paramList.Append(')');
5648                 }
5649                 else if (mt.IsPlp && (mt.SqlDbType != SqlDbType.Xml) && (mt.SqlDbType != SqlDbType.Udt)) {
5650                     paramList.Append("(max) ");
5651                 }
5652
5653                 // set the output bit for Output or InputOutput parameters
5654                 if (sqlParam.Direction != ParameterDirection.Input)
5655                     paramList.Append(" " + TdsEnums.PARAM_OUTPUT);
5656             }
5657
5658             return paramList.ToString();
5659         }
5660
5661         // Adds quotes to each part of a SQL identifier that may be multi-part, while leaving
5662         //  the result as a single composite name.
5663         private string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) {
5664             string[] strings = SqlParameter.ParseTypeName(identifier, isUdtTypeName);
5665             StringBuilder bld = new StringBuilder();
5666
5667             // Stitching back together is a little tricky. Assume we want to build a full multi-part name
5668             //  with all parts except trimming separators for leading empty names (null or empty strings,
5669             //  but not whitespace). Separators in the middle should be added, even if the name part is 
5670             //  null/empty, to maintain proper location of the parts.
5671             for (int i = 0; i < strings.Length; i++ ) {
5672                 if (0 < bld.Length) {
5673                     bld.Append('.');
5674                 }
5675                 if (null != strings[i] && 0 != strings[i].Length) {
5676                     bld.Append(ADP.BuildQuotedString("[", "]", strings[i]));
5677                 }
5678             }
5679
5680             return bld.ToString();
5681         }
5682
5683         // returns set option text to turn on format only and key info on and off
5684         // @devnote:  When we are executing as a text command, then we never need
5685         // to turn off the options since they command text is executed in the scope of sp_executesql.
5686         // For a stored proc command, however, we must send over batch sql and then turn off
5687         // the set options after we read the data.  See the code in Command.Execute()
5688         private string GetSetOptionsString(CommandBehavior behavior) {
5689             string s = null;
5690
5691             if ((System.Data.CommandBehavior.SchemaOnly == (behavior & CommandBehavior.SchemaOnly)) ||
5692                (System.Data.CommandBehavior.KeyInfo == (behavior & CommandBehavior.KeyInfo))) {
5693
5694                 // MDAC 56898 - SET FMTONLY ON will cause the server to ignore other SET OPTIONS, so turn
5695                 // it off before we ask for browse mode metadata
5696                 s = TdsEnums.FMTONLY_OFF;
5697
5698                 if (System.Data.CommandBehavior.KeyInfo == (behavior & CommandBehavior.KeyInfo)) {
5699                     s = s + TdsEnums.BROWSE_ON;
5700                 }
5701
5702                 if (System.Data.CommandBehavior.SchemaOnly == (behavior & CommandBehavior.SchemaOnly)) {
5703                     s = s + TdsEnums.FMTONLY_ON;
5704                 }
5705             }
5706
5707             return s;
5708         }
5709
5710         private string GetResetOptionsString(CommandBehavior behavior) {
5711             string s = null;
5712
5713             // SET FMTONLY ON OFF
5714             if (System.Data.CommandBehavior.SchemaOnly == (behavior & CommandBehavior.SchemaOnly)) {
5715                 s = s + TdsEnums.FMTONLY_OFF;
5716             }
5717
5718             // SET NO_BROWSETABLE OFF
5719             if (System.Data.CommandBehavior.KeyInfo == (behavior & CommandBehavior.KeyInfo)) {
5720                 s = s + TdsEnums.BROWSE_OFF;
5721             }
5722
5723             return s;
5724         }
5725
5726         private String GetCommandText(CommandBehavior behavior) {
5727             // build the batch string we send over, since we execute within a stored proc (sp_executesql), the SET options never need to be
5728             // turned off since they are scoped to the sproc
5729             Debug.Assert(System.Data.CommandType.Text == this.CommandType, "invalid call to GetCommandText for stored proc!");
5730             return GetSetOptionsString(behavior) + this.CommandText;
5731         }
5732
5733         //
5734         // build the RPC record header for sp_executesql and add the parameters
5735         //
5736         // the prototype for sp_prepare is:
5737         // sp_prepare(@handle int OUTPUT, @batch_params ntext, @batch_text ntext, @options int default 0x1)
5738         private _SqlRPC BuildPrepare(CommandBehavior behavior) {
5739             Debug.Assert(System.Data.CommandType.Text == this.CommandType, "invalid use of sp_prepare for stored proc invocation!");
5740
5741             _SqlRPC rpc = null;
5742             GetRPCObject(3, ref rpc);
5743             SqlParameter sqlParam;
5744
5745             rpc.ProcID = TdsEnums.RPC_PROCID_PREPARE;
5746             rpc.rpcName = TdsEnums.SP_PREPARE;
5747
5748             //@handle
5749             sqlParam = new SqlParameter(null, SqlDbType.Int);
5750             sqlParam.Direction = ParameterDirection.Output;
5751             rpc.parameters[0] = sqlParam;
5752             rpc.paramoptions[0] = TdsEnums.RPC_PARAM_BYREF;
5753
5754             //@batch_params
5755             string paramList = BuildParamList(_stateObj.Parser, _parameters);
5756             sqlParam = new SqlParameter(null, ((paramList.Length<<1)<=TdsEnums.TYPE_SIZE_LIMIT)?SqlDbType.NVarChar:SqlDbType.NText, paramList.Length);
5757             sqlParam.Value = paramList;
5758             rpc.parameters[1] = sqlParam;
5759
5760             //@batch_text
5761             string text = GetCommandText(behavior);
5762             sqlParam = new SqlParameter(null, ((text.Length<<1)<=TdsEnums.TYPE_SIZE_LIMIT)?SqlDbType.NVarChar:SqlDbType.NText, text.Length);
5763             sqlParam.Value = text;
5764             rpc.parameters[2] = sqlParam;
5765
5766 /*
5767             //@options
5768             sqlParam = new SqlParameter(null, SqlDbType.Int);
5769             rpc.Parameters[3] = sqlParam;
5770 */
5771             return rpc;
5772         }
5773
5774         internal void CheckThrowSNIException() {
5775             var stateObj = _stateObj;
5776             if (stateObj != null) {
5777                 stateObj.CheckThrowSNIException();
5778             }
5779         }
5780
5781         // We're being notified that the underlying connection has closed
5782         internal void OnConnectionClosed() {
5783             
5784             var stateObj = _stateObj;
5785             if (stateObj != null) {
5786                 stateObj.OnConnectionClosed();
5787             }
5788         }
5789
5790
5791         internal TdsParserStateObject StateObject {
5792             get {
5793                 return _stateObj;
5794             }
5795         }
5796
5797         private bool IsPrepared {
5798             get { return(_execType != EXECTYPE.UNPREPARED);}
5799         }
5800
5801         private bool IsUserPrepared {
5802             get { return IsPrepared && !_hiddenPrepare && !IsDirty; }
5803         }
5804
5805         internal bool IsDirty {
5806             get {
5807                 // only dirty if prepared
5808                 var activeConnection = _activeConnection;
5809                 return (IsPrepared && 
5810                     (_dirty || 
5811                     ((_parameters != null) && (_parameters.IsDirty)) || 
5812                     ((activeConnection != null) && ((activeConnection.CloseCount != _preparedConnectionCloseCount) || (activeConnection.ReconnectCount != _preparedConnectionReconnectCount)))));
5813             }
5814             set {
5815                 // only mark the command as dirty if it is already prepared
5816                 // but always clear the value if it we are clearing the dirty flag
5817                 _dirty = value ? IsPrepared : false;
5818                 if (null != _parameters) {
5819                     _parameters.IsDirty = _dirty;
5820                 }
5821                 _cachedMetaData = null;
5822             }
5823         }
5824
5825         /// <summary>
5826         /// Get or set the number of records affected by SpDescribeParameterEncryption.
5827         /// The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
5828         /// </summary>
5829         internal int RowsAffectedByDescribeParameterEncryption
5830         {
5831             get {
5832                 return _rowsAffectedBySpDescribeParameterEncryption;
5833             }
5834             set {
5835                 if (-1 == _rowsAffectedBySpDescribeParameterEncryption) {
5836                     _rowsAffectedBySpDescribeParameterEncryption = value;
5837                 }
5838                 else if (0 < value) {
5839                     _rowsAffectedBySpDescribeParameterEncryption += value;
5840                 }
5841             }
5842         }
5843
5844         internal int InternalRecordsAffected {
5845             get {
5846                 return _rowsAffected;
5847             }
5848             set {
5849                 if (-1 == _rowsAffected) {
5850                     _rowsAffected = value;
5851                 }
5852                 else if (0 < value) {
5853                     _rowsAffected += value;
5854                 }
5855             }
5856         }
5857
5858         internal bool BatchRPCMode {
5859             get {
5860                 return _batchRPCMode;
5861             }
5862             set {
5863                 _batchRPCMode = value;
5864
5865                 if (_batchRPCMode == false) {
5866                     ClearBatchCommand();
5867                 } else {
5868                     if (_RPCList == null) {
5869                         _RPCList = new List<_SqlRPC>();
5870                     }
5871                     if (_parameterCollectionList == null) {
5872                         _parameterCollectionList = new List<SqlParameterCollection>();
5873                     }
5874                 }
5875             }
5876         }
5877
5878         /// <summary>
5879         /// Clear the state in sqlcommand related to describe parameter encryption RPC requests.
5880         /// </summary>
5881         private void ClearDescribeParameterEncryptionRequests() {
5882             _sqlRPCParameterEncryptionReqArray = null;
5883             _currentlyExecutingDescribeParameterEncryptionRPC = 0;
5884             _isDescribeParameterEncryptionRPCCurrentlyInProgress = false;
5885             _rowsAffectedBySpDescribeParameterEncryption = -1;
5886         }
5887
5888         internal void ClearBatchCommand() {
5889             List<_SqlRPC> rpcList = _RPCList;
5890             if (null != rpcList) {
5891                 rpcList.Clear();
5892             }
5893             if (null != _parameterCollectionList) {
5894                 _parameterCollectionList.Clear();
5895             }
5896             _SqlRPCBatchArray = null;
5897             _currentlyExecutingBatch = 0;
5898         }
5899
5900         /// <summary>
5901         /// Set the column encryption setting to the new one.
5902         /// Do not allow conflicting column encryption settings.
5903         /// </summary>
5904         private void SetColumnEncryptionSetting(SqlCommandColumnEncryptionSetting newColumnEncryptionSetting) {
5905             if (!this._wasBatchModeColumnEncryptionSettingSetOnce) {
5906                 this._columnEncryptionSetting = newColumnEncryptionSetting;
5907                 this._wasBatchModeColumnEncryptionSettingSetOnce = true;
5908             }
5909             else {
5910                 if (this._columnEncryptionSetting != newColumnEncryptionSetting) {
5911                     throw SQL.BatchedUpdateColumnEncryptionSettingMismatch();
5912                 }
5913             }
5914         }
5915
5916             internal void AddBatchCommand(string commandText, SqlParameterCollection parameters, CommandType cmdType, SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
5917             Debug.Assert(BatchRPCMode, "Command is not in batch RPC Mode");
5918             Debug.Assert(_RPCList != null);
5919             Debug.Assert(_parameterCollectionList != null);
5920
5921             _SqlRPC  rpc = new _SqlRPC();
5922
5923             this.CommandText = commandText;
5924             this.CommandType = cmdType;
5925
5926             // Set the column encryption setting.
5927             SetColumnEncryptionSetting(columnEncryptionSetting);
5928
5929             GetStateObject();
5930             if (cmdType == CommandType.StoredProcedure) {
5931                 BuildRPC(false, parameters, ref rpc);
5932             }
5933             else {
5934                 // All batch sql statements must be executed inside sp_executesql, including those without parameters
5935                 BuildExecuteSql(CommandBehavior.Default, commandText, parameters, ref rpc);
5936             }
5937              _RPCList.Add(rpc);
5938              // Always add a parameters collection per RPC, even if there are no parameters.
5939              _parameterCollectionList.Add(parameters);
5940
5941             ReliablePutStateObject();
5942         }
5943
5944         internal int ExecuteBatchRPCCommand() {
5945
5946             Debug.Assert(BatchRPCMode, "Command is not in batch RPC Mode");
5947             Debug.Assert(_RPCList != null, "No batch commands specified");
5948             _SqlRPCBatchArray = _RPCList.ToArray();
5949             _currentlyExecutingBatch = 0;
5950             return ExecuteNonQuery();       // Check permissions, execute, return output params
5951
5952         }
5953
5954         internal int? GetRecordsAffected(int commandIndex) {
5955             Debug.Assert(BatchRPCMode, "Command is not in batch RPC Mode");
5956             Debug.Assert(_SqlRPCBatchArray != null, "batch command have been cleared");
5957             return _SqlRPCBatchArray[commandIndex].recordsAffected;
5958         }
5959
5960         internal SqlException GetErrors(int commandIndex) {
5961             SqlException result = null;
5962             int length = (_SqlRPCBatchArray[commandIndex].errorsIndexEnd - _SqlRPCBatchArray[commandIndex].errorsIndexStart);
5963             if (0 < length) {
5964                 SqlErrorCollection errors = new SqlErrorCollection();
5965                 for(int i = _SqlRPCBatchArray[commandIndex].errorsIndexStart; i < _SqlRPCBatchArray[commandIndex].errorsIndexEnd; ++i) {
5966                     errors.Add(_SqlRPCBatchArray[commandIndex].errors[i]);
5967                 }
5968                 for(int i = _SqlRPCBatchArray[commandIndex].warningsIndexStart; i < _SqlRPCBatchArray[commandIndex].warningsIndexEnd; ++i) {
5969                     errors.Add(_SqlRPCBatchArray[commandIndex].warnings[i]);
5970                 }
5971                 result = SqlException.CreateException(errors, Connection.ServerVersion, Connection.ClientConnectionId);
5972             }
5973             return result;
5974         }
5975         
5976         // Allocates and initializes a new SmiRequestExecutor based on the current command state
5977         private SmiRequestExecutor SetUpSmiRequest( SqlInternalConnectionSmi innerConnection ) {
5978
5979             // General Approach To Ensure Security of Marshalling:
5980             //        Only touch each item in the command once
5981             //        (i.e. only grab a reference to each param once, only
5982             //        read the type from that param once, etc.).  The problem is
5983             //        that if the user changes something on the command in the
5984             //        middle of marshaling, it can overwrite the native buffers
5985             //        set up.  For example, if max length is used to allocate
5986             //        buffers, but then re-read from the parameter to truncate
5987             //        strings, the user could extend the length and overwrite
5988             //        the buffer.
5989
5990             if (null != Notification){
5991                 throw SQL.NotificationsNotAvailableOnContextConnection();
5992             }
5993
5994             SmiParameterMetaData[] requestMetaData = null;
5995             ParameterPeekAheadValue[] peekAheadValues = null;
5996
5997             //    Length of rgMetadata becomes *the* official count of parameters to use,
5998             //      don't rely on Parameters.Count after this point, as the user could change it.
5999             int count = GetParameterCount( Parameters );
6000             if ( 0 < count ) {
6001                 requestMetaData = new SmiParameterMetaData[count];
6002                 peekAheadValues = new ParameterPeekAheadValue[count];
6003
6004                 // set up the metadata
6005                 for ( int index=0; index<count; index++ ) {
6006                     SqlParameter param = Parameters[index];
6007                     param.Validate(index, CommandType.StoredProcedure == CommandType);
6008                     requestMetaData[index] = param.MetaDataForSmi(out peekAheadValues[index]);
6009
6010                     // Check for valid type for version negotiated
6011                     if (!innerConnection.IsKatmaiOrNewer) {
6012                         MetaType mt = MetaType.GetMetaTypeFromSqlDbType(requestMetaData[index].SqlDbType, requestMetaData[index].IsMultiValued);
6013                         if (!mt.Is90Supported) {
6014                             throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
6015                         }
6016                     }
6017                 }
6018             }
6019
6020             // Allocate the new request
6021             CommandType cmdType = CommandType;
6022             _smiRequestContext = innerConnection.InternalContext;
6023             SmiRequestExecutor requestExecutor = _smiRequestContext.CreateRequestExecutor(
6024                                     CommandText,
6025                                     cmdType,
6026                                     requestMetaData,
6027                                     EventSink
6028                                 );
6029
6030             // deal with errors
6031             EventSink.ProcessMessagesAndThrow();
6032
6033             // Now assign param values
6034             for ( int index=0; index<count; index++ ) {
6035                 if ( ParameterDirection.Output != requestMetaData[index].Direction &&
6036                         ParameterDirection.ReturnValue != requestMetaData[index].Direction ) {
6037                     SqlParameter param = Parameters[index];
6038                     // going back to command for parameter is ok, since we'll only pick up values now.
6039                     object value = param.GetCoercedValue();
6040                     if (value is XmlDataFeed && requestMetaData[index].SqlDbType != SqlDbType.Xml) {
6041                         value = MetaType.GetStringFromXml(((XmlDataFeed)value)._source);
6042                     }
6043                     ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType(requestMetaData[index].SqlDbType, requestMetaData[index].IsMultiValued, value, null /* parameters don't use CLR Type for UDTs */, SmiContextFactory.Instance.NegotiatedSmiVersion);
6044
6045                     // Handle null reference as special case for parameters
6046                     if ( CommandType.StoredProcedure == cmdType && 
6047                                 ExtendedClrTypeCode.Empty == typeCode ) {
6048                         requestExecutor.SetDefault( index );
6049                     }
6050                     else {
6051                         // SQLBU 402391 & 403631: Exception to prevent Parameter.Size data corruption cases from working.
6052                         //  This should be temporary until changing to correct behavior can be safely implemented.
6053                         // initial size criteria is the same for all affected types
6054                         // NOTE: assumes size < -1 is handled by SqlParameter.Size setter
6055                         int size = param.Size;
6056                         if (size != 0 && size != SmiMetaData.UnlimitedMaxLengthIndicator && !param.SizeInferred) {
6057                             switch(requestMetaData[index].SqlDbType) {
6058                                 case SqlDbType.Image:
6059                                 case SqlDbType.Text:
6060                                     if (size != Int32.MaxValue) {
6061                                         throw SQL.ParameterSizeRestrictionFailure(index);
6062                                     }
6063                                     break;
6064
6065                                 case SqlDbType.NText:
6066                                     if (size != Int32.MaxValue/2) {
6067                                         throw SQL.ParameterSizeRestrictionFailure(index);
6068                                     }
6069                                     break;
6070
6071                                 case SqlDbType.VarBinary:
6072                                 case SqlDbType.VarChar:
6073                                     // Allow size==Int32.MaxValue because of DeriveParameters
6074                                     if (size > 0 && size != Int32.MaxValue && requestMetaData[index].MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator) {
6075                                         throw SQL.ParameterSizeRestrictionFailure(index);
6076                                     }
6077                                     break;
6078
6079                                 case SqlDbType.NVarChar:
6080                                     // Allow size==Int32.MaxValue/2 because of DeriveParameters
6081                                     if (size > 0 && size != Int32.MaxValue/2 && requestMetaData[index].MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator) {
6082                                         throw SQL.ParameterSizeRestrictionFailure(index);
6083                                     }
6084                                     break;
6085
6086                                 case SqlDbType.Timestamp:
6087                                     // Size limiting for larger values will happen due to MaxLength
6088                                     if (size < SmiMetaData.DefaultTimestamp.MaxLength) {
6089                                         throw SQL.ParameterSizeRestrictionFailure(index);
6090                                     }
6091                                     break;
6092
6093                                 case SqlDbType.Variant:
6094                                     // Variant problems happen when Size is less than maximums for character and binary values
6095                                     // Size limiting for larger values will happen due to MaxLength
6096                                     // NOTE: assumes xml and udt types are handled in parameter value coercion
6097                                     //      since server does not allow these types in a variant
6098                                     if (null != value) {
6099                                         MetaType mt = MetaType.GetMetaTypeFromValue(value);
6100
6101                                         if ((mt.IsNCharType && size < SmiMetaData.MaxUnicodeCharacters) ||
6102                                                 (mt.IsBinType && size < SmiMetaData.MaxBinaryLength) ||
6103                                                 (mt.IsAnsiType && size < SmiMetaData.MaxANSICharacters)) {
6104                                             throw SQL.ParameterSizeRestrictionFailure(index);
6105                                         }
6106                                     }
6107                                     break;
6108
6109                                  case SqlDbType.Xml:
6110                                     // Xml is an issue for non-SqlXml types
6111                                     if (null != value && ExtendedClrTypeCode.SqlXml != typeCode) {
6112                                         throw SQL.ParameterSizeRestrictionFailure(index);
6113                                     }
6114                                     break;
6115
6116                                  // NOTE: Char, NChar, Binary and UDT do not need restricting because they are always 8k or less, 
6117                                  //         so the metadata MaxLength will match the Size setting.
6118
6119                                 default:
6120                                     break;
6121                             }
6122                         }
6123
6124                         if (innerConnection.IsKatmaiOrNewer) {
6125                             ValueUtilsSmi.SetCompatibleValueV200(EventSink, requestExecutor, index, requestMetaData[index], value, typeCode, param.Offset, param.Size, peekAheadValues[index]);
6126                         }
6127                         else {
6128                             ValueUtilsSmi.SetCompatibleValue( EventSink, requestExecutor, index, requestMetaData[index], value, typeCode, param.Offset );
6129                         }
6130                     }
6131                 }
6132             }
6133
6134             return requestExecutor;
6135         }
6136
6137         private void WriteBeginExecuteEvent()
6138         {
6139             if (SqlEventSource.Log.IsEnabled() && Connection != null)
6140             {
6141                 string commandText = CommandType == CommandType.StoredProcedure ? CommandText : string.Empty;
6142                 SqlEventSource.Log.BeginExecute(GetHashCode(), Connection.DataSource, Connection.Database, commandText);
6143             }
6144         }
6145
6146         /// <summary>
6147         /// Writes and end execute event in Event Source.
6148         /// </summary>
6149         /// <param name="success">True if SQL command finished successfully, otherwise false.</param>
6150         /// <param name="sqlExceptionNumber">Gets a number that identifies the type of error.</param>
6151         /// <param name="synchronous">True if SQL command was executed synchronously, otherwise false.</param>
6152         private void WriteEndExecuteEvent(bool success, int? sqlExceptionNumber, bool synchronous)
6153         {
6154             if (SqlEventSource.Log.IsEnabled())
6155             {
6156                 // SqlEventSource.WriteEvent(int, int, int, int) is faster than provided overload SqlEventSource.WriteEvent(int, object[]).
6157                 // that's why trying to fit several booleans in one integer value
6158
6159                 // success state is stored the first bit in compositeState 0x01
6160                 int successFlag = success ? 1 : 0;
6161
6162                 // isSqlException is stored in the 2nd bit in compositeState 0x100
6163                 int isSqlExceptionFlag = sqlExceptionNumber.HasValue ? 2 : 0;
6164
6165                 // synchronous state is stored in the second bit in compositeState 0x10
6166                 int synchronousFlag = synchronous ? 4 : 0;
6167
6168                 int compositeState = successFlag | isSqlExceptionFlag | synchronousFlag;
6169
6170                 SqlEventSource.Log.EndExecute(GetHashCode(), compositeState, sqlExceptionNumber.GetValueOrDefault());
6171             }
6172         }
6173
6174 #if DEBUG
6175         internal void CompletePendingReadWithSuccess(bool resetForcePendingReadsToWait) {
6176             var stateObj = _stateObj;
6177             if (stateObj != null) {
6178                 stateObj.CompletePendingReadWithSuccess(resetForcePendingReadsToWait);
6179             }
6180             else {
6181                 var tempCachedAsyncState = cachedAsyncState;
6182                 if (tempCachedAsyncState != null) {
6183                     var reader = tempCachedAsyncState.CachedAsyncReader;
6184                     if (reader != null) {
6185                         reader.CompletePendingReadWithSuccess(resetForcePendingReadsToWait);
6186                     }
6187                 }
6188             }
6189         }
6190
6191         internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendingReadsToWait) {
6192             var stateObj = _stateObj;
6193             if (stateObj != null) {
6194                 stateObj.CompletePendingReadWithFailure(errorCode, resetForcePendingReadsToWait);
6195             }
6196             else {
6197                 var tempCachedAsyncState = _cachedAsyncState;
6198                 if (tempCachedAsyncState != null) {
6199                     var reader = tempCachedAsyncState.CachedAsyncReader;
6200                     if (reader != null) {
6201                         reader.CompletePendingReadWithFailure(errorCode, resetForcePendingReadsToWait);
6202                     }
6203                 }
6204             }
6205         }
6206 #endif
6207     }
6208 }
6209
6210