1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbCommand.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System
.Data
.OleDb
{
12 using System
.ComponentModel
;
14 using System
.Data
.Common
;
15 using System
.Data
.ProviderBase
;
16 using System
.Diagnostics
;
18 using System
.Runtime
.CompilerServices
;
19 using System
.Runtime
.InteropServices
;
20 using System
.Security
;
21 using System
.Security
.Permissions
;
22 using System
.Threading
;
26 DefaultEvent("RecordsAffected"),
28 Designer("Microsoft.VSDesigner.Data.VS.OleDbCommandDesigner, " + AssemblyRef
.MicrosoftVSDesigner
)
30 public sealed class OleDbCommand
: DbCommand
, ICloneable
, IDbCommand
{
33 private string _commandText
;
34 private CommandType _commandType
;
35 private int _commandTimeout
= ADP
.DefaultCommandTimeout
;
36 private UpdateRowSource _updatedRowSource
= UpdateRowSource
.Both
;
37 private bool _designTimeInvisible
;
38 private OleDbConnection _connection
;
39 private OleDbTransaction _transaction
;
41 private static int _objectTypeCount
; // Bid counter
42 internal readonly int ObjectID
= System
.Threading
.Interlocked
.Increment(ref _objectTypeCount
);
44 private OleDbParameterCollection _parameters
;
47 private UnsafeNativeMethods
.ICommandText _icommandText
;
49 // if executing with a different CommandBehavior.KeyInfo behavior
50 // original ICommandText must be released and a new ICommandText generated
51 private CommandBehavior commandBehavior
;
53 private Bindings _dbBindings
;
55 internal bool canceling
; // MDAC 68964
56 private bool _isPrepared
;
57 private bool _executeQuery
;
58 private bool _trackingForClose
;
59 private bool _hasDataReader
;
61 private IntPtr _recordsAffected
;
62 private int _changeID
;
63 private int _lastChangeID
;
65 public OleDbCommand() : base() {
66 GC
.SuppressFinalize(this);
69 public OleDbCommand(string cmdText
) : this() {
70 CommandText
= cmdText
;
73 public OleDbCommand(string cmdText
, OleDbConnection connection
) : this() {
74 CommandText
= cmdText
;
75 Connection
= connection
;
78 public OleDbCommand(string cmdText
, OleDbConnection connection
, OleDbTransaction transaction
) : this() {
79 CommandText
= cmdText
;
80 Connection
= connection
;
81 Transaction
= transaction
;
84 private OleDbCommand(OleDbCommand
from) : this() { // Clone
85 CommandText
= from.CommandText
;
86 CommandTimeout
= from.CommandTimeout
;
87 CommandType
= from.CommandType
;
88 Connection
= from.Connection
;
89 DesignTimeVisible
= from.DesignTimeVisible
;
90 UpdatedRowSource
= from.UpdatedRowSource
;
91 Transaction
= from.Transaction
;
93 OleDbParameterCollection parameters
= Parameters
;
94 foreach(object parameter
in from.Parameters
) {
95 parameters
.Add((parameter
is ICloneable
) ? (parameter
as ICloneable
).Clone() : parameter
);
99 private Bindings ParameterBindings
{
104 Bindings bindings
= _dbBindings
;
106 if ((null != bindings
) && (value != bindings
)) {
114 Editor("Microsoft.VSDesigner.Data.ADO.Design.OleDbCommandTextEditor, " + AssemblyRef
.MicrosoftVSDesigner
, "System.Drawing.Design.UITypeEditor, " + AssemblyRef
.SystemDrawing
),
115 RefreshProperties(RefreshProperties
.All
), // MDAC 67707
116 ResCategoryAttribute(Res
.DataCategory_Data
),
117 ResDescriptionAttribute(Res
.DbCommand_CommandText
),
119 override public string CommandText
{
121 string value = _commandText
;
122 return ((null != value) ? value : ADP
.StrEmpty
);
126 Bid
.Trace("<oledb.OleDbCommand.set_CommandText|API> %d#, '", ObjectID
);
127 Bid
.PutStr(value); // Use PutStr to write out entire string
130 if (0 != ADP
.SrcCompare(_commandText
, value)) {
132 _commandText
= value;
138 ResCategoryAttribute(Res
.DataCategory_Data
),
139 ResDescriptionAttribute(Res
.DbCommand_CommandTimeout
),
141 override public int CommandTimeout
{ // V1.2.3300, XXXCommand V1.0.5000
143 return _commandTimeout
;
146 Bid
.Trace("<oledb.OleDbCommand.set_CommandTimeout|API> %d#, %d\n", ObjectID
, value);
148 throw ADP
.InvalidCommandTimeout(value);
150 if (value != _commandTimeout
) {
152 _commandTimeout
= value;
157 public void ResetCommandTimeout() { // V1.2.3300
158 if (ADP
.DefaultCommandTimeout
!= _commandTimeout
) {
160 _commandTimeout
= ADP
.DefaultCommandTimeout
;
164 private bool ShouldSerializeCommandTimeout() { // V1.2.3300
165 return (ADP
.DefaultCommandTimeout
!= _commandTimeout
);
169 DefaultValue(System
.Data
.CommandType
.Text
),
170 RefreshProperties(RefreshProperties
.All
),
171 ResCategoryAttribute(Res
.DataCategory_Data
),
172 ResDescriptionAttribute(Res
.DbCommand_CommandType
),
174 override public CommandType CommandType
{
176 CommandType cmdType
= _commandType
;
177 return ((0 != cmdType
) ? cmdType
: CommandType
.Text
);
180 switch(value) { // @perfnote: Enum.IsDefined
181 case CommandType
.Text
:
182 case CommandType
.StoredProcedure
:
183 case CommandType
.TableDirect
:
185 _commandType
= value;
188 throw ADP
.InvalidCommandType(value);
195 Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + AssemblyRef
.MicrosoftVSDesigner
, "System.Drawing.Design.UITypeEditor, " + AssemblyRef
.SystemDrawing
),
196 ResCategoryAttribute(Res
.DataCategory_Data
),
197 ResDescriptionAttribute(Res
.DbCommand_Connection
),
199 new public OleDbConnection Connection
{
204 OleDbConnection connection
= _connection
;
205 if (value != connection
) {
210 Bid
.Trace("<oledb.OleDbCommand.set_Connection|API> %d#\n", ObjectID
);
213 _transaction
= OleDbTransaction
.TransactionUpdate(_transaction
); // MDAC 63226
219 private void ResetConnection() {
220 OleDbConnection connection
= _connection
;
221 if (null != connection
) {
224 if (_trackingForClose
) {
225 connection
.RemoveWeakReference(this);
226 _trackingForClose
= false;
232 override protected DbConnection DbConnection
{ // V1.2.3300
237 Connection
= (OleDbConnection
)value;
241 override protected DbParameterCollection DbParameterCollection
{ // V1.2.3300
247 override protected DbTransaction DbTransaction
{ // V1.2.3300
252 Transaction
= (OleDbTransaction
)value;
256 // @devnote: By default, the cmd object is visible on the design surface (i.e. VS7 Server Tray)
257 // to limit the number of components that clutter the design surface,
258 // when the DataAdapter design wizard generates the insert/update/delete commands it will
259 // set the DesignTimeVisible property to false so that cmds won't appear as individual objects
264 EditorBrowsableAttribute(EditorBrowsableState
.Never
),
266 public override bool DesignTimeVisible
{ // V1.2.3300, XXXCommand V1.0.5000
268 return !_designTimeInvisible
;
271 _designTimeInvisible
= !value;
272 TypeDescriptor
.Refresh(this); // VS7 208845
277 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
278 ResCategoryAttribute(Res
.DataCategory_Data
),
279 ResDescriptionAttribute(Res
.DbCommand_Parameters
),
281 new public OleDbParameterCollection Parameters
{
283 OleDbParameterCollection
value = _parameters
;
285 // delay the creation of the OleDbParameterCollection
286 // until user actually uses the Parameters property
287 value = new OleDbParameterCollection();
294 private bool HasParameters() { // MDAC 65548
295 OleDbParameterCollection
value = _parameters
;
296 return (null != value) && (0 < value.Count
); // VS 300569
301 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
302 ResDescriptionAttribute(Res
.DbCommand_Transaction
),
304 new public OleDbTransaction Transaction
{
306 // find the last non-zombied local transaction object, but not transactions
307 // that may have been started after the current local transaction
308 OleDbTransaction transaction
= _transaction
;
309 while ((null != transaction
) && (null == transaction
.Connection
)) {
310 transaction
= transaction
.Parent
;
311 _transaction
= transaction
;
316 _transaction
= value;
317 Bid
.Trace("<oledb.OleDbCommand.set_Transaction|API> %d#\n", ObjectID
);
322 DefaultValue(System
.Data
.UpdateRowSource
.Both
),
323 ResCategoryAttribute(Res
.DataCategory_Update
),
324 ResDescriptionAttribute(Res
.DbCommand_UpdatedRowSource
),
326 override public UpdateRowSource UpdatedRowSource
{ // V1.2.3300, XXXCommand V1.0.5000
328 return _updatedRowSource
;
331 switch(value) { // @perfnote: Enum.IsDefined
332 case UpdateRowSource
.None
:
333 case UpdateRowSource
.OutputParameters
:
334 case UpdateRowSource
.FirstReturnedRecord
:
335 case UpdateRowSource
.Both
:
336 _updatedRowSource
= value;
339 throw ADP
.InvalidUpdateRowSource(value);
344 // required interface, safe cast
345 private UnsafeNativeMethods
.IAccessor
IAccessor() {
346 Bid
.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|command> %d#, IAccessor\n", ObjectID
);
347 Debug
.Assert(null != _icommandText
, "IAccessor: null ICommandText");
348 return (UnsafeNativeMethods
.IAccessor
) _icommandText
;
351 // required interface, safe cast
352 internal UnsafeNativeMethods
.ICommandProperties
ICommandProperties() {
353 Bid
.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|command> %d#, ICommandProperties\n", ObjectID
);
354 Debug
.Assert(null != _icommandText
, "ICommandProperties: null ICommandText");
355 return (UnsafeNativeMethods
.ICommandProperties
) _icommandText
;
358 // optional interface, unsafe cast
359 private UnsafeNativeMethods
.ICommandPrepare
ICommandPrepare() {
360 Bid
.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|command> %d#, ICommandPrepare\n", ObjectID
);
361 Debug
.Assert(null != _icommandText
, "ICommandPrepare: null ICommandText");
362 return (_icommandText
as UnsafeNativeMethods
.ICommandPrepare
);
365 // optional interface, unsafe cast
366 private UnsafeNativeMethods
.ICommandWithParameters
ICommandWithParameters() {
367 Bid
.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|command> %d#, ICommandWithParameters\n", ObjectID
);
368 Debug
.Assert(null != _icommandText
, "ICommandWithParameters: null ICommandText");
369 UnsafeNativeMethods
.ICommandWithParameters
value = (_icommandText
as UnsafeNativeMethods
.ICommandWithParameters
);
371 throw ODB
.NoProviderSupportForParameters(_connection
.Provider
, (Exception
)null);
376 private void CreateAccessor() {
377 Debug
.Assert(System
.Data
.CommandType
.Text
== CommandType
|| System
.Data
.CommandType
.StoredProcedure
== CommandType
, "CreateAccessor: incorrect CommandType");
378 Debug
.Assert(null == _dbBindings
, "CreateAccessor: already has dbBindings");
379 Debug
.Assert(HasParameters(), "CreateAccessor: unexpected, no parameter collection");
381 // do this first in-case the command doesn't support parameters
382 UnsafeNativeMethods
.ICommandWithParameters commandWithParameters
= ICommandWithParameters();
384 OleDbParameterCollection collection
= _parameters
;
385 OleDbParameter
[] parameters
= new OleDbParameter
[collection
.Count
];
386 collection
.CopyTo(parameters
, 0);
388 // _dbBindings is used as a switch during ExecuteCommand, so don't set it until everything okay
389 Bindings bindings
= new Bindings(parameters
, collection
.ChangeID
);
391 for (int i
= 0; i
< parameters
.Length
; ++i
) {
392 bindings
.ForceRebind
|= parameters
[i
].BindParameter(i
, bindings
);
395 bindings
.AllocateForAccessor(null, 0, 0);
397 ApplyParameterBindings(commandWithParameters
, bindings
.BindInfo
);
399 UnsafeNativeMethods
.IAccessor iaccessor
= IAccessor();
400 OleDbHResult hr
= bindings
.CreateAccessor(iaccessor
, ODB
.DBACCESSOR_PARAMETERDATA
);
404 _dbBindings
= bindings
;
407 private void ApplyParameterBindings(UnsafeNativeMethods
.ICommandWithParameters commandWithParameters
, tagDBPARAMBINDINFO
[] bindInfo
) {
408 IntPtr
[] ordinals
= new IntPtr
[bindInfo
.Length
];
409 for (int i
= 0; i
< ordinals
.Length
; ++i
) {
410 ordinals
[i
] = (IntPtr
)(i
+1);
413 Bid
.Trace("<oledb.ICommandWithParameters.SetParameterInfo|API|OLEDB> %d#\n", ObjectID
);
414 OleDbHResult hr
= commandWithParameters
.SetParameterInfo((IntPtr
)bindInfo
.Length
, ordinals
, bindInfo
);
415 Bid
.Trace("<oledb.ICommandWithParameters.SetParameterInfo|API|OLEDB|RET> %08X{HRESULT}\n", hr
);
422 override public void Cancel() {
424 Bid
.ScopeEnter(out hscp
, "<oledb.OleDbCommand.Cancel|API> %d#\n", ObjectID
);
426 unchecked { _changeID++; }
428 UnsafeNativeMethods
.ICommandText icmdtxt
= _icommandText
;
429 if (null != icmdtxt
) {
430 OleDbHResult hr
= OleDbHResult
.S_OK
;
433 // lock the object to avoid race conditions between using the object and releasing the object
434 // after we acquire the lock, if the class has moved on don't actually call Cancel
435 if (icmdtxt
== _icommandText
) {
436 Bid
.Trace("<oledb.ICommandText.Cancel|API|OLEDB> %d#\n", ObjectID
);
437 hr
= icmdtxt
.Cancel();
438 Bid
.Trace("<oledb.ICommandText.Cancel|API|OLEDB|RET> %08X{HRESULT}\n", hr
);
441 if (OleDbHResult
.DB_E_CANTCANCEL
!= hr
) {
442 // if the provider can't cancel the command - don't cancel the DataReader
443 this.canceling
= true;
446 // since cancel is allowed to occur at anytime we can't check the connection status
447 // since if it returns as closed then the connection will close causing the reader to close
448 // and that would introduce the possilbility of one thread reading and one thread closing at the same time
449 ProcessResultsNoReset(hr
); // MDAC 72667
452 this.canceling
= true;
456 Bid
.ScopeLeave(ref hscp
);
460 public OleDbCommand
Clone() {
461 OleDbCommand clone
= new OleDbCommand(this);
462 Bid
.Trace("<oledb.OleDbCommand.Clone|API> %d#, clone=%d#\n", ObjectID
, clone
.ObjectID
);
466 object ICloneable
.Clone() {
470 // Connection.Close & Connection.Dispose(true) notification
471 internal void CloseCommandFromConnection(bool canceling
) {
472 this.canceling
= canceling
; // MDAC 71435
474 _trackingForClose
= false;
476 //GC.SuppressFinalize(this);
479 internal void CloseInternal() {
480 Debug
.Assert(null != _connection
, "no connection, CloseInternal");
481 CloseInternalParameters();
482 CloseInternalCommand();
485 // may be called from either
486 // OleDbDataReader.Close/Dispose
487 // via OleDbCommand.Dispose or OleDbConnection.Close
488 internal void CloseFromDataReader(Bindings bindings
) {
489 if (null != bindings
) {
492 Debug
.Assert(_dbBindings
== bindings
, "bindings with two owners");
495 bindings
.ApplyOutputParameters();
496 ParameterBindings
= bindings
;
499 _hasDataReader
= false;
502 private void CloseInternalCommand() {
503 unchecked { _changeID++; }
504 this.commandBehavior
= CommandBehavior
.Default
;
509 UnsafeNativeMethods
.ICommandText ict
= Interlocked
.Exchange
<UnsafeNativeMethods
.ICommandText
>(ref _icommandText
, null);
512 // lock the object to avoid race conditions between using the object and releasing the object
513 Marshal
.ReleaseComObject(ict
);
517 private void CloseInternalParameters() {
518 Debug
.Assert(null != _connection
, "no connection, CloseInternalParameters");
519 Bindings bindings
= _dbBindings
;
521 if (null != bindings
) {
526 new public OleDbParameter
CreateParameter() {
527 return new OleDbParameter();
530 override protected DbParameter
CreateDbParameter() {
531 return CreateParameter();
534 override protected void Dispose(bool disposing
) { // MDAC 65459
535 if (disposing
) { // release mananged objects
536 // the DataReader takes ownership of the parameter Bindings
537 // this way they don't get destroyed when user calls OleDbCommand.Dispose
538 // when there is an open DataReader
540 unchecked { _changeID++; }
542 // in V1.0, V1.1 the Connection,Parameters,CommandText,Transaction where reset
548 // release unmanaged objects
549 base.Dispose(disposing
); // notify base classes
553 new public OleDbDataReader
ExecuteReader() {
554 return ExecuteReader(CommandBehavior
.Default
);
557 IDataReader IDbCommand
.ExecuteReader() {
558 return ExecuteReader(CommandBehavior
.Default
);
561 new public OleDbDataReader
ExecuteReader(CommandBehavior behavior
) {
562 OleDbConnection
.ExecutePermission
.Demand();
565 Bid
.ScopeEnter(out hscp
, "<oledb.OleDbCommand.ExecuteReader|API> %d#, behavior=%d{ds.CommandBehavior}\n", ObjectID
, (int)behavior
);
567 _executeQuery
= true;
568 return ExecuteReaderInternal(behavior
, ADP
.ExecuteReader
);
571 Bid
.ScopeLeave(ref hscp
);
575 IDataReader IDbCommand
.ExecuteReader(CommandBehavior behavior
) {
576 return ExecuteReader(behavior
);
579 override protected DbDataReader
ExecuteDbDataReader(CommandBehavior behavior
) {
580 return ExecuteReader(behavior
);
583 private OleDbDataReader
ExecuteReaderInternal(CommandBehavior behavior
, string method
) {
584 OleDbDataReader dataReader
= null;
585 OleDbException nextResultsFailure
= null;
586 int state
= ODB
.InternalStateClosed
;
588 ValidateConnectionAndTransaction(method
);
590 if (0 != (CommandBehavior
.SingleRow
& behavior
)) {
591 // CommandBehavior.SingleRow implies CommandBehavior.SingleResult
592 behavior
|= CommandBehavior
.SingleResult
;
595 object executeResult
;
598 switch(CommandType
) {
599 case 0: // uninitialized CommandType.Text
600 case CommandType
.Text
:
601 case CommandType
.StoredProcedure
:
602 resultType
= ExecuteCommand(behavior
, out executeResult
);
605 case CommandType
.TableDirect
:
606 resultType
= ExecuteTableDirect(behavior
, out executeResult
);
610 throw ADP
.InvalidCommandType(CommandType
);
615 dataReader
= new OleDbDataReader(_connection
, this, 0, this.commandBehavior
);
618 case ODB
.ExecutedIMultipleResults
:
619 dataReader
.InitializeIMultipleResults(executeResult
);
620 dataReader
.NextResult();
622 case ODB
.ExecutedIRowset
:
623 dataReader
.InitializeIRowset(executeResult
, ChapterHandle
.DB_NULL_HCHAPTER
, _recordsAffected
);
624 dataReader
.BuildMetaInfo();
625 dataReader
.HasRowsRead();
627 case ODB
.ExecutedIRow
:
628 dataReader
.InitializeIRow(executeResult
, _recordsAffected
);
629 dataReader
.BuildMetaInfo();
631 case ODB
.PrepareICommandText
:
633 PrepareCommandText(2);
635 OleDbDataReader
.GenerateSchemaTable(dataReader
, _icommandText
, behavior
);
638 Debug
.Assert(false, "ExecuteReaderInternal: unknown result type");
641 executeResult
= null;
642 _hasDataReader
= true;
643 _connection
.AddWeakReference(dataReader
, OleDbReferenceCollection
.DataReaderTag
);
645 // command stays in the executing state until the connection
646 // has a datareader to track for it being closed
647 state
= ODB
.InternalStateOpen
; // MDAC 72655
650 if (ODB
.InternalStateOpen
!= state
) {
651 this.canceling
= true;
652 if (null != dataReader
) {
653 ((IDisposable
) dataReader
).Dispose();
658 Debug
.Assert(null != dataReader
, "ExecuteReader should never return a null DataReader");
660 else { // optimized code path for ExecuteNonQuery to not create a OleDbDataReader object
662 if (ODB
.ExecutedIMultipleResults
== resultType
) {
663 UnsafeNativeMethods
.IMultipleResults multipleResults
= (UnsafeNativeMethods
.IMultipleResults
) executeResult
;
665 // may cause a Connection.ResetState which closes connection
666 nextResultsFailure
= OleDbDataReader
.NextResults(multipleResults
, _connection
, this, out _recordsAffected
);
671 if (null != executeResult
) {
672 Marshal
.ReleaseComObject(executeResult
);
673 executeResult
= null;
675 CloseFromDataReader(ParameterBindings
);
679 if (!ADP
.IsCatchableExceptionType(e
)) {
682 if (null != nextResultsFailure
) {
683 nextResultsFailure
= new OleDbException(nextResultsFailure
, e
);
692 finally { // finally clear executing state
694 if ((null == dataReader
) && (ODB
.InternalStateOpen
!= state
)) { // MDAC 67218
700 if (!ADP
.IsCatchableExceptionType(e
)) {
703 if (null != nextResultsFailure
) {
704 nextResultsFailure
= new OleDbException(nextResultsFailure
, e
);
710 if (null != nextResultsFailure
) {
711 throw nextResultsFailure
;
717 private int ExecuteCommand(CommandBehavior behavior
, out object executeResult
) {
718 if (InitializeCommand(behavior
, false)) {
719 if (0 != (CommandBehavior
.SchemaOnly
& this.commandBehavior
)) {
720 executeResult
= null;
721 return ODB
.PrepareICommandText
;
723 return ExecuteCommandText(out executeResult
);
725 return ExecuteTableDirect(behavior
, out executeResult
); // MDAC 57856
728 // dbindings handle can't be freed until the output parameters
729 // have been filled in which occurs after the last rowset is released
730 // dbbindings.FreeDataHandle occurs in Cloe
731 private int ExecuteCommandText(out object executeResult
) {
733 tagDBPARAMS dbParams
= null;
734 RowBinding rowbinding
= null;
735 Bindings bindings
= ParameterBindings
;
736 bool mustRelease
= false;
738 RuntimeHelpers
.PrepareConstrainedRegions();
740 if (null != bindings
) { // parameters may be suppressed
741 rowbinding
= bindings
.RowBinding();
743 rowbinding
.DangerousAddRef(ref mustRelease
);
745 // bindings can't be released until after last rowset is released
746 // that is when output parameters are populated
747 // initialize the input parameters to the input databuffer
748 bindings
.ApplyInputParameters();
750 dbParams
= new tagDBPARAMS();
751 dbParams
.pData
= rowbinding
.DangerousGetDataPtr();
752 dbParams
.cParamSets
= 1;
753 dbParams
.hAccessor
= rowbinding
.DangerousGetAccessorHandle();
755 if ((0 == (CommandBehavior
.SingleResult
& this.commandBehavior
)) && _connection
.SupportMultipleResults()) {
756 retcode
= ExecuteCommandTextForMultpleResults(dbParams
, out executeResult
);
758 else if (0 == (CommandBehavior
.SingleRow
& this.commandBehavior
) || !_executeQuery
) {
759 retcode
= ExecuteCommandTextForSingleResult(dbParams
, out executeResult
);
762 retcode
= ExecuteCommandTextForSingleRow(dbParams
, out executeResult
);
767 rowbinding
.DangerousRelease();
773 private int ExecuteCommandTextForMultpleResults(tagDBPARAMS dbParams
, out object executeResult
) {
774 Debug
.Assert(0 == (CommandBehavior
.SingleRow
& this.commandBehavior
), "SingleRow implies SingleResult");
777 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB> %d#, IID_IMultipleResults\n", ObjectID
);
778 hr
= _icommandText
.Execute(ADP
.PtrZero
, ref ODB
.IID_IMultipleResults
, dbParams
, out _recordsAffected
, out executeResult
);
779 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB|RET> %08X{HRESULT}, RecordsAffected=%Id\n", hr
, _recordsAffected
);
781 if (OleDbHResult
.E_NOINTERFACE
!= hr
) {
782 ExecuteCommandTextErrorHandling(hr
);
783 return ODB
.ExecutedIMultipleResults
;
785 SafeNativeMethods
.Wrapper
.ClearErrorInfo();
786 return ExecuteCommandTextForSingleResult(dbParams
, out executeResult
);
789 private int ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams
, out object executeResult
) {
792 // MDAC 64465 (Microsoft.Jet.OLEDB.4.0 returns 0 for recordsAffected instead of -1)
794 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB> %d#, IID_IRowset\n", ObjectID
);
795 hr
= _icommandText
.Execute(ADP
.PtrZero
, ref ODB
.IID_IRowset
, dbParams
, out _recordsAffected
, out executeResult
);
796 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB|RET> %08X{HRESULT}, RecordsAffected=%Id\n", hr
, _recordsAffected
);
799 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB> %d#, IID_NULL\n", ObjectID
);
800 hr
= _icommandText
.Execute(ADP
.PtrZero
, ref ODB
.IID_NULL
, dbParams
, out _recordsAffected
, out executeResult
);
801 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB|RET> %08X{HRESULT}, RecordsAffected=%Id\n", hr
, _recordsAffected
);
803 ExecuteCommandTextErrorHandling(hr
);
804 return ODB
.ExecutedIRowset
;
807 private int ExecuteCommandTextForSingleRow(tagDBPARAMS dbParams
, out object executeResult
) {
808 Debug
.Assert(_executeQuery
, "ExecuteNonQuery should always use ExecuteCommandTextForSingleResult");
810 if (_connection
.SupportIRow(this)) {
813 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB> %d#, IID_IRow\n", ObjectID
);
814 hr
= _icommandText
.Execute(ADP
.PtrZero
, ref ODB
.IID_IRow
, dbParams
, out _recordsAffected
, out executeResult
);
815 Bid
.Trace("<oledb.ICommandText.Execute|API|OLEDB|RET> %08X{HRESULT}, RecordsAffected=%Id\n", hr
, _recordsAffected
);
817 if (OleDbHResult
.DB_E_NOTFOUND
== hr
) { // MDAC 76110
818 SafeNativeMethods
.Wrapper
.ClearErrorInfo();
819 return ODB
.ExecutedIRow
;
821 else if (OleDbHResult
.E_NOINTERFACE
!= hr
) {
822 ExecuteCommandTextErrorHandling(hr
);
823 return ODB
.ExecutedIRow
;
826 SafeNativeMethods
.Wrapper
.ClearErrorInfo();
827 return ExecuteCommandTextForSingleResult(dbParams
, out executeResult
);
830 private void ExecuteCommandTextErrorHandling(OleDbHResult hr
) {
831 Exception e
= OleDbConnection
.ProcessResults(hr
, _connection
, this);
833 e
= ExecuteCommandTextSpecialErrorHandling(hr
, e
);
838 private Exception
ExecuteCommandTextSpecialErrorHandling(OleDbHResult hr
, Exception e
) {
839 if (((OleDbHResult
.DB_E_ERRORSOCCURRED
== hr
) || (OleDbHResult
.DB_E_BADBINDINFO
== hr
)) && (null != _dbBindings
)) { // MDAC 66026, 67039
841 // this code exist to try for a better user error message by post-morten detection
842 // of invalid parameter types being passed to a provider that doesn't understand
843 // the user specified parameter OleDbType
845 Debug
.Assert(null != e
, "missing inner exception");
847 StringBuilder builder
= new StringBuilder();
848 ParameterBindings
.ParameterStatus(builder
);
849 e
= ODB
.CommandParameterStatus(builder
.ToString(), e
);
854 override public int ExecuteNonQuery() {
855 OleDbConnection
.ExecutePermission
.Demand();
858 Bid
.ScopeEnter(out hscp
, "<oledb.OleDbCommand.ExecuteNonQuery|API> %d#\n", ObjectID
);
860 _executeQuery
= false;
861 ExecuteReaderInternal(CommandBehavior
.Default
, ADP
.ExecuteNonQuery
);
862 return ADP
.IntPtrToInt32(_recordsAffected
);
865 Bid
.ScopeLeave(ref hscp
);
869 override public object ExecuteScalar() {
870 OleDbConnection
.ExecutePermission
.Demand();
873 Bid
.ScopeEnter(out hscp
, "<oledb.OleDbCommand.ExecuteScalar|API> %d#\n", ObjectID
);
876 _executeQuery
= true;
877 using(OleDbDataReader reader
= ExecuteReaderInternal(CommandBehavior
.Default
, ADP
.ExecuteScalar
)) {
878 if (reader
.Read() && (0 < reader
.FieldCount
)) {
879 value = reader
.GetValue(0);
885 Bid
.ScopeLeave(ref hscp
);
889 private int ExecuteTableDirect(CommandBehavior behavior
, out object executeResult
) {
890 this.commandBehavior
= behavior
;
891 executeResult
= null;
893 OleDbHResult hr
= OleDbHResult
.S_OK
;
895 StringMemHandle sptr
= null;
896 bool mustReleaseStringHandle
= false;
898 RuntimeHelpers
.PrepareConstrainedRegions();
900 sptr
= new StringMemHandle(ExpandCommandText());
902 sptr
.DangerousAddRef(ref mustReleaseStringHandle
);
904 if (mustReleaseStringHandle
) {
905 tagDBID tableID
= new tagDBID();
906 tableID
.uGuid
= Guid
.Empty
;
907 tableID
.eKind
= ODB
.DBKIND_NAME
;
908 tableID
.ulPropid
= sptr
.DangerousGetHandle();
910 using(IOpenRowsetWrapper iopenRowset
= _connection
.IOpenRowset()) {
911 using(DBPropSet propSet
= CommandPropertySets()) {
912 if (null != propSet
) {
915 Bid
.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB> %d#, IID_IRowset\n", ObjectID
);
916 bool mustRelease
= false;
917 RuntimeHelpers
.PrepareConstrainedRegions();
919 propSet
.DangerousAddRef(ref mustRelease
);
920 hr
= iopenRowset
.Value
.OpenRowset(ADP
.PtrZero
, tableID
, ADP
.PtrZero
, ref ODB
.IID_IRowset
, propSet
.PropertySetCount
, propSet
.DangerousGetHandle(), out executeResult
);
924 propSet
.DangerousRelease();
928 Bid
.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB|RET> %08X{HRESULT}", hr
);
930 if (OleDbHResult
.DB_E_ERRORSOCCURRED
== hr
) {
931 Bid
.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB> %d#, IID_IRowset\n", ObjectID
);
932 hr
= iopenRowset
.Value
.OpenRowset(ADP
.PtrZero
, tableID
, ADP
.PtrZero
, ref ODB
.IID_IRowset
, 0, IntPtr
.Zero
, out executeResult
);
933 Bid
.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB|RET> %08X{HRESULT}", hr
);
937 Bid
.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB> %d#, IID_IRowset\n", ObjectID
);
938 hr
= iopenRowset
.Value
.OpenRowset(ADP
.PtrZero
, tableID
, ADP
.PtrZero
, ref ODB
.IID_IRowset
, 0, IntPtr
.Zero
, out executeResult
);
939 Bid
.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB|RET> %08X{HRESULT}", hr
);
946 if (mustReleaseStringHandle
) {
947 sptr
.DangerousRelease();
951 _recordsAffected
= ADP
.RecordsUnaffected
;
952 return ODB
.ExecutedIRowset
;
955 private string ExpandCommandText() {
956 string cmdtxt
= CommandText
;
957 if (ADP
.IsEmpty(cmdtxt
)) {
960 CommandType cmdtype
= CommandType
;
962 case System
.Data
.CommandType
.Text
:
963 // do nothing, already expanded by user
966 case System
.Data
.CommandType
.StoredProcedure
:
967 // { ? = CALL SPROC (? ?) }, { ? = CALL SPROC }, { CALL SPRC (? ?) }, { CALL SPROC }
968 return ExpandStoredProcedureToText(cmdtxt
);
970 case System
.Data
.CommandType
.TableDirect
:
971 // @devnote: Provider=Jolt4.0 doesn't like quoted table names, SQOLEDB requires them
972 // Providers should not require table names to be quoted and should guarantee that
973 // unquoted table names correctly open the specified table, even if the table name
974 // contains special characters, as long as the table can be unambiguously identified
979 throw ADP
.InvalidCommandType(cmdtype
);
983 private string ExpandOdbcMaximumToText(string sproctext
, int parameterCount
) {
984 StringBuilder builder
= new StringBuilder();
985 if ((0 < parameterCount
) && (ParameterDirection
.ReturnValue
== Parameters
[0].Direction
)) {
987 builder
.Append("{ ? = CALL ");
990 builder
.Append("{ CALL ");
992 builder
.Append(sproctext
); // WebData 95634
994 switch(parameterCount
) {
996 builder
.Append(" }");
999 builder
.Append("( ? ) }");
1002 builder
.Append("( ?, ?");
1003 for (int i
= 2; i
< parameterCount
; ++i
) {
1004 builder
.Append(", ?");
1006 builder
.Append(" ) }");
1009 return builder
.ToString();
1012 private string ExpandOdbcMinimumToText(string sproctext
, int parameterCount
) {
1013 //if ((0 < parameterCount) && (ParameterDirection.ReturnValue == Parameters[0].Direction)) {
1014 // Debug.Assert("doesn't support ReturnValue parameters");
1016 StringBuilder builder
= new StringBuilder();
1017 builder
.Append("exec ");
1018 builder
.Append(sproctext
);
1019 if (0 < parameterCount
) {
1020 builder
.Append(" ?");
1021 for(int i
= 1; i
< parameterCount
; ++i
) {
1022 builder
.Append(", ?");
1025 return builder
.ToString();
1028 private string ExpandStoredProcedureToText(string sproctext
) {
1029 Debug
.Assert(null != _connection
, "ExpandStoredProcedureToText: null Connection");
1031 int parameterCount
= (null != _parameters
) ? _parameters
.Count
: 0;
1032 if (0 == (ODB
.DBPROPVAL_SQL_ODBC_MINIMUM
& _connection
.SqlSupport())) {
1033 return ExpandOdbcMinimumToText(sproctext
, parameterCount
);
1035 return ExpandOdbcMaximumToText(sproctext
, parameterCount
);
1038 private void ParameterCleanup() {
1039 Bindings bindings
= ParameterBindings
;
1040 if (null != bindings
) {
1041 bindings
.CleanupBindings();
1045 private bool InitializeCommand(CommandBehavior behavior
, bool throwifnotsupported
) {
1046 Debug
.Assert(null != _connection
, "InitializeCommand: null OleDbConnection");
1048 int changeid
= _changeID
;
1049 if ((0 != (CommandBehavior
.KeyInfo
& (this.commandBehavior ^ behavior
))) || (_lastChangeID
!= changeid
)) {
1050 CloseInternalParameters(); // could optimize out
1051 CloseInternalCommand();
1053 this.commandBehavior
= behavior
;
1054 changeid
= _changeID
;
1056 if (!PropertiesOnCommand(false)) {
1057 return false; // MDAC 57856
1060 if ((null != _dbBindings
) && _dbBindings
.AreParameterBindingsInvalid(_parameters
)) {
1061 CloseInternalParameters();
1064 // if we already having bindings - don't create the accessor
1065 // if _parameters is null - no parameters exist - don't create the collection
1066 // do we actually have parameters since the collection exists
1067 if ((null == _dbBindings
) && HasParameters()) {
1068 // if we setup the parameters before setting cmdtxt then named parameters can happen
1072 if (_lastChangeID
!= changeid
) {
1075 String commandText
= ExpandCommandText();
1078 Bid
.Trace("<oledb.ICommandText.SetCommandText|API|OLEDB> %d#, DBGUID_DEFAULT, CommandText='", ObjectID
);
1079 Bid
.PutStr(commandText
); // Use PutStr to write out entire string
1082 hr
= _icommandText
.SetCommandText(ref ODB
.DBGUID_DEFAULT
, commandText
);
1083 Bid
.Trace("<oledb.ICommandText.SetCommandText|API|OLEDB|RET> %08X{HRESULT}\n", hr
);
1090 _lastChangeID
= changeid
;
1095 private void PropertyChanging() {
1096 unchecked { _changeID++; }
1099 override public void Prepare() {
1100 OleDbConnection
.ExecutePermission
.Demand();
1103 Bid
.ScopeEnter(out hscp
, "<oledb.OleDbCommand.Prepare|API> %d#\n", ObjectID
);
1105 if (CommandType
.TableDirect
!= CommandType
) { // MDAC 70946, 71194
1106 ValidateConnectionAndTransaction(ADP
.Prepare
);
1108 _isPrepared
= false;
1109 if (CommandType
.TableDirect
!= CommandType
) {
1110 InitializeCommand(0, true);
1111 PrepareCommandText(1);
1116 Bid
.ScopeLeave(ref hscp
);
1120 private void PrepareCommandText(int expectedExecutionCount
) {
1121 OleDbParameterCollection parameters
= _parameters
;
1122 if (null != parameters
) {
1123 foreach(OleDbParameter parameter
in parameters
) {
1124 if (parameter
.IsParameterComputed()) {
1125 // @devnote: use IsParameterComputed which is called in the normal case
1126 // only to call Prepare to throw the specialized error message
1127 // reducing the overall number of methods to actually jit
1128 parameter
.Prepare(this); // MDAC 70232
1132 UnsafeNativeMethods
.ICommandPrepare icommandPrepare
= ICommandPrepare();
1133 if (null != icommandPrepare
) {
1136 Bid
.Trace("<oledb.ICommandPrepare.Prepare|API|OLEDB> %d#, expectedExecutionCount=%d\n", ObjectID
, expectedExecutionCount
);
1137 hr
= icommandPrepare
.Prepare(expectedExecutionCount
);
1138 Bid
.Trace("<oledb.ICommandPrepare.Prepare|API|OLEDB|RET> %08X{HRESULT}\n", hr
);
1143 // don't recompute bindings on prepared statements
1147 private void ProcessResults(OleDbHResult hr
) {
1148 Exception e
= OleDbConnection
.ProcessResults(hr
, _connection
, this);
1149 if (null != e
) { throw e; }
1152 private void ProcessResultsNoReset(OleDbHResult hr
) {
1153 Exception e
= OleDbConnection
.ProcessResults(hr
, null, this);
1154 if (null != e
) { throw e; }
1157 internal object GetPropertyValue(Guid propertySet
, int propertyID
) {
1158 if (null != _icommandText
) {
1160 tagDBPROP
[] dbprops
;
1161 UnsafeNativeMethods
.ICommandProperties icommandProperties
= ICommandProperties();
1163 using(PropertyIDSet propidset
= new PropertyIDSet(propertySet
, propertyID
)) {
1165 using(DBPropSet propset
= new DBPropSet(icommandProperties
, propidset
, out hr
)) {
1167 // VSDD 621427: OLEDB Data Reader masks provider specific errors by raising "Internal .Net Framework Data Provider error 30."
1168 // DBPropSet c-tor will register the exception and it will be raised at GetPropertySet call in case of failure
1169 SafeNativeMethods
.Wrapper
.ClearErrorInfo();
1171 dbprops
= propset
.GetPropertySet(0, out propertySet
);
1174 if (OleDbPropertyStatus
.Ok
== dbprops
[0].dwStatus
) {
1175 return dbprops
[0].vValue
;
1177 return dbprops
[0].dwStatus
;
1179 return OleDbPropertyStatus
.NotSupported
;
1182 private bool PropertiesOnCommand(bool throwNotSupported
) {
1183 if (null != _icommandText
) {
1186 Debug
.Assert(!_isPrepared
, "null command isPrepared");
1188 OleDbConnection connection
= _connection
;
1189 if (null == connection
) {
1190 connection
.CheckStateOpen(ODB
.Properties
);
1192 if (!_trackingForClose
) {
1193 _trackingForClose
= true;
1194 connection
.AddWeakReference(this, OleDbReferenceCollection
.CommandTag
);
1196 _icommandText
= connection
.ICommandText();
1198 if (null == _icommandText
) {
1199 if (throwNotSupported
|| HasParameters()) {
1200 throw ODB
.CommandTextNotSupported(connection
.Provider
, null);
1202 return false; // MDAC 57856
1205 using(DBPropSet propSet
= CommandPropertySets()) {
1206 if (null != propSet
) {
1207 UnsafeNativeMethods
.ICommandProperties icommandProperties
= ICommandProperties();
1209 Bid
.Trace("<oledb.ICommandProperties.SetProperties|API|OLEDB> %d#\n", ObjectID
);
1210 OleDbHResult hr
= icommandProperties
.SetProperties(propSet
.PropertySetCount
, propSet
);
1211 Bid
.Trace("<oledb.ICommandProperties.SetProperties|API|OLEDB|RET> %08X{HRESULT}\n", hr
);
1214 SafeNativeMethods
.Wrapper
.ClearErrorInfo();
1221 private DBPropSet
CommandPropertySets() {
1222 DBPropSet propSet
= null;
1224 bool keyInfo
= (0 != (CommandBehavior
.KeyInfo
& this.commandBehavior
));
1226 // always set the CommandTimeout value?
1227 int count
= (_executeQuery
? (keyInfo
? 4 : 2) : 1);
1230 propSet
= new DBPropSet(1);
1232 tagDBPROP
[] dbprops
= new tagDBPROP
[count
];
1234 dbprops
[0] = new tagDBPROP(ODB
.DBPROP_COMMANDTIMEOUT
, false, CommandTimeout
);
1236 if (_executeQuery
) {
1237 // 'Microsoft.Jet.OLEDB.4.0' default is DBPROPVAL_AO_SEQUENTIAL
1238 dbprops
[1] = new tagDBPROP(ODB
.DBPROP_ACCESSORDER
, false, ODB
.DBPROPVAL_AO_RANDOM
); // MDAC 73030
1241 // 'Unique Rows' property required for SQLOLEDB to retrieve things like 'BaseTableName'
1242 dbprops
[2] = new tagDBPROP(ODB
.DBPROP_UNIQUEROWS
, false, keyInfo
);
1244 // otherwise 'Microsoft.Jet.OLEDB.4.0' doesn't support IColumnsRowset
1245 dbprops
[3] = new tagDBPROP(ODB
.DBPROP_IColumnsRowset
, false, true);
1248 propSet
.SetPropertySet(0, OleDbPropertySetGuid
.Rowset
, dbprops
);
1253 internal Bindings
TakeBindingOwnerShip() {
1254 Bindings bindings
= _dbBindings
;
1259 private void ValidateConnection(string method
) {
1260 if (null == _connection
) {
1261 throw ADP
.ConnectionRequired(method
);
1263 _connection
.CheckStateOpen(method
);
1265 // user attempting to execute the command while the first dataReader hasn't returned
1266 // use the connection reference collection to see if the dataReader referencing this
1267 // command has been garbage collected or not.
1268 if (_hasDataReader
) {
1269 if (_connection
.HasLiveReader(this)) {
1270 throw ADP
.OpenReaderExists();
1272 _hasDataReader
= false;
1276 private void ValidateConnectionAndTransaction(string method
) {
1277 ValidateConnection(method
);
1278 _transaction
= _connection
.ValidateTransaction(Transaction
, method
);
1279 this.canceling
= false;