Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data / System / Data / OleDb / OleDbCommand.cs
blob34c26ef0f0fdf91d3f3228f8f7ac98c88868b678
1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbCommand.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 //------------------------------------------------------------------------------
9 namespace System.Data.OleDb {
11 using System;
12 using System.ComponentModel;
13 using System.Data;
14 using System.Data.Common;
15 using System.Data.ProviderBase;
16 using System.Diagnostics;
17 using System.IO;
18 using System.Runtime.CompilerServices;
19 using System.Runtime.InteropServices;
20 using System.Security;
21 using System.Security.Permissions;
22 using System.Threading;
23 using System.Text;
26 DefaultEvent("RecordsAffected"),
27 ToolboxItem(true),
28 Designer("Microsoft.VSDesigner.Data.VS.OleDbCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner)
30 public sealed class OleDbCommand : DbCommand, ICloneable, IDbCommand {
32 // command data
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;
46 // native information
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 {
100 get {
101 return _dbBindings;
103 set {
104 Bindings bindings = _dbBindings;
105 _dbBindings = value;
106 if ((null != bindings) && (value != bindings)) {
107 bindings.Dispose();
113 DefaultValue(""),
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 {
120 get {
121 string value = _commandText;
122 return ((null != value) ? value : ADP.StrEmpty);
124 set {
125 if (Bid.TraceOn) {
126 Bid.Trace("<oledb.OleDbCommand.set_CommandText|API> %d#, '", ObjectID);
127 Bid.PutStr(value); // Use PutStr to write out entire string
128 Bid.Trace("'\n");
130 if (0 != ADP.SrcCompare(_commandText, value)) {
131 PropertyChanging();
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
142 get {
143 return _commandTimeout;
145 set {
146 Bid.Trace("<oledb.OleDbCommand.set_CommandTimeout|API> %d#, %d\n", ObjectID, value);
147 if (value < 0) {
148 throw ADP.InvalidCommandTimeout(value);
150 if (value != _commandTimeout) {
151 PropertyChanging();
152 _commandTimeout = value;
157 public void ResetCommandTimeout() { // V1.2.3300
158 if (ADP.DefaultCommandTimeout != _commandTimeout) {
159 PropertyChanging();
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 {
175 get {
176 CommandType cmdType = _commandType;
177 return ((0 != cmdType) ? cmdType : CommandType.Text);
179 set {
180 switch(value) { // @perfnote: Enum.IsDefined
181 case CommandType.Text:
182 case CommandType.StoredProcedure:
183 case CommandType.TableDirect:
184 PropertyChanging();
185 _commandType = value;
186 break;
187 default:
188 throw ADP.InvalidCommandType(value);
194 DefaultValue(null),
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 {
200 get {
201 return _connection;
203 set {
204 OleDbConnection connection = _connection;
205 if (value != connection) {
206 PropertyChanging();
207 ResetConnection();
209 _connection = value;
210 Bid.Trace("<oledb.OleDbCommand.set_Connection|API> %d#\n", ObjectID);
212 if (null != value) {
213 _transaction = OleDbTransaction.TransactionUpdate(_transaction); // MDAC 63226
219 private void ResetConnection() {
220 OleDbConnection connection = _connection;
221 if (null != connection) {
222 PropertyChanging();
223 CloseInternal();
224 if (_trackingForClose) {
225 connection.RemoveWeakReference(this);
226 _trackingForClose = false;
229 _connection = null;
232 override protected DbConnection DbConnection { // V1.2.3300
233 get {
234 return Connection;
236 set {
237 Connection = (OleDbConnection)value;
241 override protected DbParameterCollection DbParameterCollection { // V1.2.3300
242 get {
243 return Parameters;
247 override protected DbTransaction DbTransaction { // V1.2.3300
248 get {
249 return Transaction;
251 set {
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
261 DefaultValue(true),
262 DesignOnly(true),
263 Browsable(false),
264 EditorBrowsableAttribute(EditorBrowsableState.Never),
266 public override bool DesignTimeVisible { // V1.2.3300, XXXCommand V1.0.5000
267 get {
268 return !_designTimeInvisible;
270 set {
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 {
282 get {
283 OleDbParameterCollection value = _parameters;
284 if (null == value) {
285 // delay the creation of the OleDbParameterCollection
286 // until user actually uses the Parameters property
287 value = new OleDbParameterCollection();
288 _parameters = value;
290 return value;
294 private bool HasParameters() { // MDAC 65548
295 OleDbParameterCollection value = _parameters;
296 return (null != value) && (0 < value.Count); // VS 300569
300 Browsable(false),
301 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
302 ResDescriptionAttribute(Res.DbCommand_Transaction),
304 new public OleDbTransaction Transaction {
305 get {
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;
313 return transaction;
315 set {
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
327 get {
328 return _updatedRowSource;
330 set {
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;
337 break;
338 default:
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);
370 if (null == value) {
371 throw ODB.NoProviderSupportForParameters(_connection.Provider, (Exception)null);
373 return value;
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);
401 if (hr < 0) {
402 ProcessResults(hr);
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);
417 if (hr < 0) {
418 ProcessResults(hr);
422 override public void Cancel() {
423 IntPtr hscp;
424 Bid.ScopeEnter(out hscp, "<oledb.OleDbCommand.Cancel|API> %d#\n", ObjectID);
425 try {
426 unchecked { _changeID++; }
428 UnsafeNativeMethods.ICommandText icmdtxt = _icommandText;
429 if (null != icmdtxt) {
430 OleDbHResult hr = OleDbHResult.S_OK;
432 lock(icmdtxt) {
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
451 else {
452 this.canceling = true;
455 finally {
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);
463 return clone;
466 object ICloneable.Clone() {
467 return Clone();
470 // Connection.Close & Connection.Dispose(true) notification
471 internal void CloseCommandFromConnection(bool canceling) {
472 this.canceling = canceling; // MDAC 71435
473 CloseInternal();
474 _trackingForClose = false;
475 _transaction = null;
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) {
490 if (canceling) {
491 bindings.Dispose();
492 Debug.Assert(_dbBindings == bindings, "bindings with two owners");
494 else {
495 bindings.ApplyOutputParameters();
496 ParameterBindings = bindings;
499 _hasDataReader = false;
502 private void CloseInternalCommand() {
503 unchecked { _changeID++; }
504 this.commandBehavior = CommandBehavior.Default;
505 _isPrepared = false;
509 UnsafeNativeMethods.ICommandText ict = Interlocked.Exchange<UnsafeNativeMethods.ICommandText>(ref _icommandText, null);
510 if (null != ict) {
511 lock(ict) {
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;
520 _dbBindings = null;
521 if (null != bindings) {
522 bindings.Dispose();
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
543 ResetConnection();
544 _transaction = null;
545 _parameters = null;
546 CommandText = null;
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();
564 IntPtr hscp;
565 Bid.ScopeEnter(out hscp, "<oledb.OleDbCommand.ExecuteReader|API> %d#, behavior=%d{ds.CommandBehavior}\n", ObjectID, (int)behavior);
566 try {
567 _executeQuery = true;
568 return ExecuteReaderInternal(behavior, ADP.ExecuteReader);
570 finally {
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;
587 try {
588 ValidateConnectionAndTransaction(method);
590 if (0 != (CommandBehavior.SingleRow & behavior)) {
591 // CommandBehavior.SingleRow implies CommandBehavior.SingleResult
592 behavior |= CommandBehavior.SingleResult;
595 object executeResult;
596 int resultType;
598 switch(CommandType) {
599 case 0: // uninitialized CommandType.Text
600 case CommandType.Text:
601 case CommandType.StoredProcedure:
602 resultType = ExecuteCommand(behavior, out executeResult);
603 break;
605 case CommandType.TableDirect:
606 resultType = ExecuteTableDirect(behavior, out executeResult);
607 break;
609 default:
610 throw ADP.InvalidCommandType(CommandType);
613 if (_executeQuery) {
614 try {
615 dataReader = new OleDbDataReader(_connection, this, 0, this.commandBehavior);
617 switch(resultType) {
618 case ODB.ExecutedIMultipleResults:
619 dataReader.InitializeIMultipleResults(executeResult);
620 dataReader.NextResult();
621 break;
622 case ODB.ExecutedIRowset:
623 dataReader.InitializeIRowset(executeResult, ChapterHandle.DB_NULL_HCHAPTER, _recordsAffected);
624 dataReader.BuildMetaInfo();
625 dataReader.HasRowsRead();
626 break;
627 case ODB.ExecutedIRow:
628 dataReader.InitializeIRow(executeResult, _recordsAffected);
629 dataReader.BuildMetaInfo();
630 break;
631 case ODB.PrepareICommandText:
632 if (!_isPrepared) {
633 PrepareCommandText(2);
635 OleDbDataReader.GenerateSchemaTable(dataReader, _icommandText, behavior);
636 break;
637 default:
638 Debug.Assert(false, "ExecuteReaderInternal: unknown result type");
639 break;
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
649 finally {
650 if (ODB.InternalStateOpen != state) {
651 this.canceling = true;
652 if (null != dataReader) {
653 ((IDisposable) dataReader).Dispose();
654 dataReader = null;
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
661 try {
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);
669 finally {
670 try {
671 if (null != executeResult) {
672 Marshal.ReleaseComObject(executeResult);
673 executeResult = null;
675 CloseFromDataReader(ParameterBindings);
677 catch(Exception e) {
679 if (!ADP.IsCatchableExceptionType(e)) {
680 throw;
682 if (null != nextResultsFailure) {
683 nextResultsFailure = new OleDbException(nextResultsFailure, e);
685 else {
686 throw;
692 finally { // finally clear executing state
693 try {
694 if ((null == dataReader) && (ODB.InternalStateOpen != state)) { // MDAC 67218
695 ParameterCleanup();
698 catch(Exception e) {
700 if (!ADP.IsCatchableExceptionType(e)) {
701 throw;
703 if (null != nextResultsFailure) {
704 nextResultsFailure = new OleDbException(nextResultsFailure, e);
706 else {
707 throw;
710 if (null != nextResultsFailure) {
711 throw nextResultsFailure;
714 return dataReader;
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) {
732 int retcode;
733 tagDBPARAMS dbParams = null;
734 RowBinding rowbinding = null;
735 Bindings bindings = ParameterBindings;
736 bool mustRelease = false;
738 RuntimeHelpers.PrepareConstrainedRegions();
739 try {
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);
761 else {
762 retcode = ExecuteCommandTextForSingleRow(dbParams, out executeResult);
765 finally {
766 if (mustRelease) {
767 rowbinding.DangerousRelease();
770 return retcode;
773 private int ExecuteCommandTextForMultpleResults(tagDBPARAMS dbParams, out object executeResult) {
774 Debug.Assert(0 == (CommandBehavior.SingleRow & this.commandBehavior), "SingleRow implies SingleResult");
775 OleDbHResult hr;
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) {
790 OleDbHResult hr;
792 // MDAC 64465 (Microsoft.Jet.OLEDB.4.0 returns 0 for recordsAffected instead of -1)
793 if (_executeQuery) {
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);
798 else {
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)) {
811 OleDbHResult hr;
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);
832 if (null != e) {
833 e = ExecuteCommandTextSpecialErrorHandling(hr, e);
834 throw 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);
851 return e;
854 override public int ExecuteNonQuery() {
855 OleDbConnection.ExecutePermission.Demand();
857 IntPtr hscp;
858 Bid.ScopeEnter(out hscp, "<oledb.OleDbCommand.ExecuteNonQuery|API> %d#\n", ObjectID);
859 try {
860 _executeQuery = false;
861 ExecuteReaderInternal(CommandBehavior.Default, ADP.ExecuteNonQuery);
862 return ADP.IntPtrToInt32(_recordsAffected);
864 finally {
865 Bid.ScopeLeave(ref hscp);
869 override public object ExecuteScalar() {
870 OleDbConnection.ExecutePermission.Demand();
872 IntPtr hscp;
873 Bid.ScopeEnter(out hscp, "<oledb.OleDbCommand.ExecuteScalar|API> %d#\n", ObjectID);
874 try {
875 object value = null;
876 _executeQuery = true;
877 using(OleDbDataReader reader = ExecuteReaderInternal(CommandBehavior.Default, ADP.ExecuteScalar)) {
878 if (reader.Read() && (0 < reader.FieldCount)) {
879 value = reader.GetValue(0);
882 return value;
884 finally {
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();
899 try {
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) {
914 // MDAC 65279
915 Bid.Trace("<oledb.IOpenRowset.OpenRowset|API|OLEDB> %d#, IID_IRowset\n", ObjectID);
916 bool mustRelease = false;
917 RuntimeHelpers.PrepareConstrainedRegions();
918 try {
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);
922 finally {
923 if (mustRelease) {
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);
936 else {
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);
945 finally {
946 if (mustReleaseStringHandle) {
947 sptr.DangerousRelease();
950 ProcessResults(hr);
951 _recordsAffected = ADP.RecordsUnaffected;
952 return ODB.ExecutedIRowset;
955 private string ExpandCommandText() {
956 string cmdtxt = CommandText;
957 if (ADP.IsEmpty(cmdtxt)) {
958 return ADP.StrEmpty;
960 CommandType cmdtype = CommandType;
961 switch(cmdtype) {
962 case System.Data.CommandType.Text:
963 // do nothing, already expanded by user
964 return cmdtxt;
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
975 // without quoting.
976 return cmdtxt;
978 default:
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)) {
986 parameterCount--;
987 builder.Append("{ ? = CALL ");
989 else {
990 builder.Append("{ CALL ");
992 builder.Append(sproctext); // WebData 95634
994 switch(parameterCount) {
995 case 0:
996 builder.Append(" }");
997 break;
998 case 1:
999 builder.Append("( ? ) }");
1000 break;
1001 default:
1002 builder.Append("( ?, ?");
1003 for (int i = 2; i < parameterCount; ++i) {
1004 builder.Append(", ?");
1006 builder.Append(" ) }");
1007 break;
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
1069 CreateAccessor();
1072 if (_lastChangeID != changeid) {
1073 OleDbHResult hr;
1075 String commandText = ExpandCommandText();
1077 if (Bid.TraceOn) {
1078 Bid.Trace("<oledb.ICommandText.SetCommandText|API|OLEDB> %d#, DBGUID_DEFAULT, CommandText='", ObjectID);
1079 Bid.PutStr(commandText); // Use PutStr to write out entire string
1080 Bid.Trace("'\n");
1082 hr = _icommandText.SetCommandText(ref ODB.DBGUID_DEFAULT, commandText);
1083 Bid.Trace("<oledb.ICommandText.SetCommandText|API|OLEDB|RET> %08X{HRESULT}\n", hr);
1085 if (hr < 0) {
1086 ProcessResults(hr);
1090 _lastChangeID = changeid;
1092 return true;
1095 private void PropertyChanging() {
1096 unchecked { _changeID++; }
1099 override public void Prepare() {
1100 OleDbConnection.ExecutePermission.Demand();
1102 IntPtr hscp;
1103 Bid.ScopeEnter(out hscp, "<oledb.OleDbCommand.Prepare|API> %d#\n", ObjectID);
1104 try {
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);
1115 finally {
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) {
1134 OleDbHResult hr;
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);
1140 ProcessResults(hr);
1143 // don't recompute bindings on prepared statements
1144 _isPrepared = true;
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) {
1159 OleDbHResult hr;
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)) {
1166 if (hr < 0) {
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) {
1184 return true;
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);
1213 if (hr < 0) {
1214 SafeNativeMethods.Wrapper.ClearErrorInfo();
1218 return true;
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);
1229 if (0 < count) {
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
1240 if (keyInfo) {
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);
1250 return propSet;
1253 internal Bindings TakeBindingOwnerShip() {
1254 Bindings bindings = _dbBindings;
1255 _dbBindings = null;
1256 return bindings;
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;