1 //------------------------------------------------------------------------------
2 // <copyright file="OdbcCommand.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 //------------------------------------------------------------------------------
10 using System.ComponentModel; //Component
12 using System.Data.Common;
13 using System.Data.ProviderBase;
14 using System.Diagnostics;
15 using System.Runtime.CompilerServices;
16 using System.Runtime.InteropServices;
17 using System.Threading;
20 // There may be two ways to improve performance:
21 // 1. pool statements on the connection object
22 // 2. Do not create a datareader object for non-datareader returning command execution.
24 // We do not want to do the effort unless we have to squeze performance.
28 namespace System.Data.Odbc {
31 DefaultEvent("RecordsAffected"),
33 Designer("Microsoft.VSDesigner.Data.VS.OdbcCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner)
35 public sealed class OdbcCommand : DbCommand, ICloneable {
36 private static int _objectTypeCount; // Bid counter
37 internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
39 private string _commandText;
40 private CommandType _commandType;
41 private int _commandTimeout = ADP.DefaultCommandTimeout;
42 private UpdateRowSource _updatedRowSource = UpdateRowSource.Both;
43 private bool _designTimeInvisible;
44 private bool _isPrepared; // true if the command is prepared
46 private OdbcConnection _connection;
47 private OdbcTransaction _transaction;
49 private WeakReference weakDataReaderReference;
51 private CMDWrapper _cmdWrapper;
53 private OdbcParameterCollection _parameterCollection; // Parameter collection
55 private ConnectionState cmdState;
57 public OdbcCommand() : base() {
58 GC.SuppressFinalize(this);
61 public OdbcCommand(string cmdText) : this() {
62 // note: arguments are assigned to properties so we do not have to trace them.
63 // We still need to include them into the argument list of the definition!
64 CommandText = cmdText;
67 public OdbcCommand(string cmdText, OdbcConnection connection) : this() {
68 CommandText = cmdText;
69 Connection = connection;
72 public OdbcCommand(string cmdText, OdbcConnection connection, OdbcTransaction transaction) : this() {
73 CommandText = cmdText;
74 Connection = connection;
75 Transaction = transaction;
78 private void DisposeDeadDataReader() {
79 if (ConnectionState.Fetching == cmdState) {
80 if (null != this.weakDataReaderReference && !this.weakDataReaderReference.IsAlive) {
81 if (_cmdWrapper != null) {
82 _cmdWrapper.FreeKeyInfoStatementHandle(ODBC32.STMT.CLOSE);
83 _cmdWrapper.FreeStatementHandle(ODBC32.STMT.CLOSE);
85 CloseFromDataReader();
90 private void DisposeDataReader() {
91 if (null != this.weakDataReaderReference) {
92 IDisposable reader = (IDisposable) this.weakDataReaderReference.Target;
93 if ((null != reader) && this.weakDataReaderReference.IsAlive) {
94 ((IDisposable) reader).Dispose();
96 CloseFromDataReader();
100 internal void DisconnectFromDataReaderAndConnection () {
101 // get a reference to the datareader if it is alive
102 OdbcDataReader liveReader = null;
103 if (this.weakDataReaderReference != null){
104 OdbcDataReader reader;
105 reader = (OdbcDataReader)this.weakDataReaderReference.Target;
106 if (this.weakDataReaderReference.IsAlive) {
111 // remove reference to this from the live datareader
112 if (liveReader != null) {
113 liveReader.Command = null;
118 if (null != _connection) {
119 _connection.RemoveWeakReference(this);
123 // if the reader is dead we have to dismiss the statement
124 if (liveReader == null){
125 CloseCommandWrapper();
127 // else DataReader now has exclusive ownership
131 override protected void Dispose(bool disposing) { // MDAC 65459
133 // release mananged objects
134 // in V1.0, V1.1 the Connection,Parameters,CommandText,Transaction where reset
135 this.DisconnectFromDataReaderAndConnection ();
136 _parameterCollection = null;
139 _cmdWrapper = null; // let go of the CommandWrapper
142 base.Dispose(disposing); // notify base classes
145 internal bool Canceling {
147 return _cmdWrapper.Canceling;
152 ResCategoryAttribute(Res.DataCategory_Data),
154 RefreshProperties(RefreshProperties.All), // MDAC 67707
155 ResDescriptionAttribute(Res.DbCommand_CommandText),
156 Editor("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing)
158 override public string CommandText {
160 string value = _commandText;
161 return ((null != value) ? value : ADP.StrEmpty);
165 Bid.Trace("<odbc.OdbcCommand.set_CommandText|API> %d#, '", ObjectID);
166 Bid.PutStr(value); // Use PutStr to write out entire string
169 if (0 != ADP.SrcCompare(_commandText, value)) {
171 _commandText = value;
177 ResCategoryAttribute(Res.DataCategory_Data),
178 ResDescriptionAttribute(Res.DbCommand_CommandTimeout),
180 override public int CommandTimeout { // V1.2.3300, XXXCommand V1.0.5000
182 return _commandTimeout;
185 Bid.Trace("<odbc.OdbcCommand.set_CommandTimeout|API> %d#, %d\n", ObjectID, value);
187 throw ADP.InvalidCommandTimeout(value);
189 if (value != _commandTimeout) {
191 _commandTimeout = value;
196 public void ResetCommandTimeout() { // V1.2.3300
197 if (ADP.DefaultCommandTimeout != _commandTimeout) {
199 _commandTimeout = ADP.DefaultCommandTimeout;
203 private bool ShouldSerializeCommandTimeout() { // V1.2.3300
204 return (ADP.DefaultCommandTimeout != _commandTimeout);
208 DefaultValue(System.Data.CommandType.Text),
209 RefreshProperties(RefreshProperties.All),
210 ResCategoryAttribute(Res.DataCategory_Data),
211 ResDescriptionAttribute(Res.DbCommand_CommandType),
213 override public CommandType CommandType {
215 CommandType cmdType = _commandType;
216 return ((0 != cmdType) ? cmdType : CommandType.Text);
219 switch(value) { // @perfnote: Enum.IsDefined
220 case CommandType.Text:
221 case CommandType.StoredProcedure:
223 _commandType = value;
225 case CommandType.TableDirect:
226 throw ODBC.NotSupportedCommandType(value);
228 throw ADP.InvalidCommandType(value);
233 // This will establish a relationship between the command and the connection
236 ResCategoryAttribute(Res.DataCategory_Behavior),
237 ResDescriptionAttribute(Res.DbCommand_Connection),
238 Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
240 new public OdbcConnection Connection {
245 if (value != _connection) {
247 this.DisconnectFromDataReaderAndConnection();
248 Debug.Assert(null == _cmdWrapper, "has CMDWrapper when setting connection");
255 override protected DbConnection DbConnection { // V1.2.3300
260 Connection = (OdbcConnection)value;
264 override protected DbParameterCollection DbParameterCollection { // V1.2.3300
270 override protected DbTransaction DbTransaction { // V1.2.3300
275 Transaction = (OdbcTransaction)value;
279 // @devnote: By default, the cmd object is visible on the design surface (i.e. VS7 Server Tray)
280 // to limit the number of components that clutter the design surface,
281 // when the DataAdapter design wizard generates the insert/update/delete commands it will
282 // set the DesignTimeVisible property to false so that cmds won't appear as individual objects
287 EditorBrowsableAttribute(EditorBrowsableState.Never),
289 public override bool DesignTimeVisible { // V1.2.3300, XXXCommand V1.0.5000
291 return !_designTimeInvisible;
294 _designTimeInvisible = !value;
295 TypeDescriptor.Refresh(this); // VS7 208845
299 internal bool HasParameters {
301 return (null != _parameterCollection);
306 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
307 ResCategoryAttribute(Res.DataCategory_Data),
308 ResDescriptionAttribute(Res.DbCommand_Parameters),
310 new public OdbcParameterCollection Parameters {
312 if (null == _parameterCollection) {
313 _parameterCollection = new OdbcParameterCollection();
315 return _parameterCollection;
321 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
322 ResDescriptionAttribute(Res.DbCommand_Transaction),
324 new public OdbcTransaction Transaction {
326 if ((null != _transaction) && (null == _transaction.Connection)) {
327 _transaction = null; // Dawn of the Dead
332 if (_transaction != value) {
333 PropertyChanging(); // fire event before value is validated
334 _transaction = value;
340 DefaultValue(System.Data.UpdateRowSource.Both),
341 ResCategoryAttribute(Res.DataCategory_Update),
342 ResDescriptionAttribute(Res.DbCommand_UpdatedRowSource),
344 override public UpdateRowSource UpdatedRowSource { // V1.2.3300, XXXCommand V1.0.5000
346 return _updatedRowSource;
349 switch(value) { // @perfnote: Enum.IsDefined
350 case UpdateRowSource.None:
351 case UpdateRowSource.OutputParameters:
352 case UpdateRowSource.FirstReturnedRecord:
353 case UpdateRowSource.Both:
354 _updatedRowSource = value;
357 throw ADP.InvalidUpdateRowSource(value);
362 internal OdbcDescriptorHandle GetDescriptorHandle(ODBC32.SQL_ATTR attribute) {
363 return _cmdWrapper.GetDescriptorHandle(attribute);
367 // GetStatementHandle
368 // ------------------
369 // Try to return a cached statement handle.
371 // Creates a CmdWrapper object if necessary
372 // If no handle is available a handle will be allocated.
373 // Bindings will be unbound if a handle is cached and the bindings are invalid.
375 internal CMDWrapper GetStatementHandle () {
376 // update the command wrapper object, allocate buffer
377 // create reader object
379 if (_cmdWrapper==null) {
380 _cmdWrapper = new CMDWrapper(_connection);
382 Debug.Assert(null != _connection, "GetStatementHandle without connection?");
383 _connection.AddWeakReference(this, OdbcReferenceCollection.CommandTag);
386 if (_cmdWrapper._dataReaderBuf == null) {
387 _cmdWrapper._dataReaderBuf = new CNativeBuffer(4096);
390 // if there is already a statement handle we need to do some cleanup
392 if (null == _cmdWrapper.StatementHandle) {
394 _cmdWrapper.CreateStatementHandle();
396 else if ((null != _parameterCollection) && _parameterCollection.RebindCollection) {
397 _cmdWrapper.FreeStatementHandle(ODBC32.STMT.RESET_PARAMS);
402 // OdbcCommand.Cancel()
404 // In ODBC3.0 ... a call to SQLCancel when no processing is done has no effect at all
405 // (ODBC Programmer's Reference ...)
408 override public void Cancel() {
409 CMDWrapper wrapper = _cmdWrapper;
410 if (null != wrapper) {
411 wrapper.Canceling = true;
412 OdbcStatementHandle stmt = wrapper.StatementHandle;
415 // Cancel the statement
416 ODBC32.RetCode retcode = stmt.Cancel();
418 // copy of StatementErrorHandler, because stmt may become null
420 case ODBC32.RetCode.SUCCESS:
421 case ODBC32.RetCode.SUCCESS_WITH_INFO:
422 // don't fire info message events on cancel
425 throw wrapper.Connection.HandleErrorNoThrow(stmt, retcode);
433 object ICloneable.Clone() {
434 OdbcCommand clone = new OdbcCommand();
435 Bid.Trace("<odbc.OdbcCommand.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
436 clone.CommandText = CommandText;
437 clone.CommandTimeout = this.CommandTimeout;
438 clone.CommandType = CommandType;
439 clone.Connection = this.Connection;
440 clone.Transaction = this.Transaction;
441 clone.UpdatedRowSource = UpdatedRowSource;
443 if ((null != _parameterCollection) && (0 < Parameters.Count)) {
444 OdbcParameterCollection parameters = clone.Parameters;
445 foreach(ICloneable parameter in Parameters) {
446 parameters.Add(parameter.Clone());
452 internal bool RecoverFromConnection() {
453 DisposeDeadDataReader();
454 return (ConnectionState.Closed == cmdState);
457 private void CloseCommandWrapper() {
458 CMDWrapper wrapper = _cmdWrapper;
459 if (null != wrapper) {
463 if (null != _connection) {
464 _connection.RemoveWeakReference(this);
473 internal void CloseFromConnection () {
474 if (null != _parameterCollection) {
475 _parameterCollection.RebindCollection = true;
478 CloseCommandWrapper();
483 internal void CloseFromDataReader() {
484 this.weakDataReaderReference = null;
485 this.cmdState = ConnectionState.Closed;
488 new public OdbcParameter CreateParameter() {
489 return new OdbcParameter();
492 override protected DbParameter CreateDbParameter() {
493 return CreateParameter();
496 override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) {
497 return ExecuteReader(behavior);
500 override public int ExecuteNonQuery() {
501 OdbcConnection.ExecutePermission.Demand();
502 using (OdbcDataReader reader = ExecuteReaderObject(0, ADP.ExecuteNonQuery, false)) {
504 return reader.RecordsAffected;
508 new public OdbcDataReader ExecuteReader() {
509 return ExecuteReader(0/*CommandBehavior*/);
513 new public OdbcDataReader ExecuteReader(CommandBehavior behavior) {
514 OdbcConnection.ExecutePermission.Demand();
515 return ExecuteReaderObject(behavior, ADP.ExecuteReader, true);
518 internal OdbcDataReader ExecuteReaderFromSQLMethod(object[] methodArguments,
519 ODBC32.SQL_API method){
521 return ExecuteReaderObject(CommandBehavior.Default,method.ToString(),true,methodArguments,method);
525 private OdbcDataReader ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader) { // MDAC 68324
527 if ((CommandText == null) || (CommandText.Length == 0)) {
528 throw (ADP.CommandTextRequired(method));
530 // using all functions to tell ExecuteReaderObject that
531 return ExecuteReaderObject(behavior,method,needReader,null,ODBC32.SQL_API.SQLEXECDIRECT);
534 private OdbcDataReader ExecuteReaderObject(CommandBehavior behavior,
537 object[] methodArguments,
538 ODBC32.SQL_API odbcApiMethod) { // MDAC 68324
540 OdbcDataReader localReader = null;
542 DisposeDeadDataReader(); // this is a no-op if cmdState is not Fetching
543 ValidateConnectionAndTransaction(method); // cmdState will change to Executing
545 if(0 != (CommandBehavior.SingleRow & behavior)) {
546 // CommandBehavior.SingleRow implies CommandBehavior.SingleResult
547 behavior |= CommandBehavior.SingleResult;
550 ODBC32.RetCode retcode;
552 OdbcStatementHandle stmt = GetStatementHandle().StatementHandle;
553 _cmdWrapper.Canceling = false;
555 if(null != weakDataReaderReference) {
556 if(weakDataReaderReference.IsAlive) {
557 object target = weakDataReaderReference.Target;
558 if(null != target && weakDataReaderReference.IsAlive) {
559 if(!((OdbcDataReader)target).IsClosed) {
560 throw ADP.OpenReaderExists(); // MDAC 66411
565 localReader = new OdbcDataReader(this, _cmdWrapper, behavior);
567 //Set command properties
568 //Not all drivers support timeout. So fail silently if error
569 if(!Connection.ProviderInfo.NoQueryTimeout) {
570 TrySetStatementAttribute(stmt,
571 ODBC32.SQL_ATTR.QUERY_TIMEOUT,
572 (IntPtr)this.CommandTimeout);
575 // todo: If we remember the state we can omit a lot of SQLSetStmtAttrW calls ...
576 // if we do not create a reader we do not even need to do that
578 if(Connection.IsV3Driver) {
579 if(!Connection.ProviderInfo.NoSqlSoptSSNoBrowseTable && !Connection.ProviderInfo.NoSqlSoptSSHiddenColumns) {
580 // Need to get the metadata information
582 //SQLServer actually requires browse info turned on ahead of time...
583 //Note: We ignore any failures, since this is SQLServer specific
584 //We won't specialcase for SQL Server but at least for non-V3 drivers
585 if(localReader.IsBehavior(CommandBehavior.KeyInfo)) {
586 if(!_cmdWrapper._ssKeyInfoModeOn) {
587 TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE, (IntPtr)ODBC32.SQL_NB.ON);
588 TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS, (IntPtr)ODBC32.SQL_HC.ON);
589 _cmdWrapper._ssKeyInfoModeOff = false;
590 _cmdWrapper._ssKeyInfoModeOn = true;
594 if(!_cmdWrapper._ssKeyInfoModeOff) {
595 TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE, (IntPtr)ODBC32.SQL_NB.OFF);
596 TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS, (IntPtr)ODBC32.SQL_HC.OFF);
597 _cmdWrapper._ssKeyInfoModeOff = true;
598 _cmdWrapper._ssKeyInfoModeOn = false;
605 if(localReader.IsBehavior(CommandBehavior.KeyInfo) ||
606 localReader.IsBehavior(CommandBehavior.SchemaOnly)) {
608 retcode = stmt.Prepare(CommandText);
610 if(ODBC32.RetCode.SUCCESS != retcode) {
611 _connection.HandleError(stmt, retcode);
615 bool mustRelease = false;
616 CNativeBuffer parameterBuffer = _cmdWrapper._nativeParameterBuffer;
618 RuntimeHelpers.PrepareConstrainedRegions();
621 //Note: We use the internal variable as to not instante a new object collection,
622 //for the the common case of using no parameters.
623 if((null != _parameterCollection) && (0 < _parameterCollection.Count)) {
624 int parameterBufferSize = _parameterCollection.CalcParameterBufferSize(this);
626 if(null == parameterBuffer || parameterBuffer.Length < parameterBufferSize) {
627 if (null != parameterBuffer) {
628 parameterBuffer.Dispose();
630 parameterBuffer = new CNativeBuffer(parameterBufferSize);
631 _cmdWrapper._nativeParameterBuffer = parameterBuffer;
634 parameterBuffer.ZeroMemory();
637 parameterBuffer.DangerousAddRef(ref mustRelease);
639 _parameterCollection.Bind(this, _cmdWrapper, parameterBuffer);
642 if(!localReader.IsBehavior(CommandBehavior.SchemaOnly)) {
644 // Can't get the KeyInfo after command execution (SQL Server only since it does not support multiple
645 // results on the same connection). Stored procedures (SP) do not return metadata before actual execution
646 // Need to check the column count since the command type may not be set to SP for a SP.
647 if((localReader.IsBehavior(CommandBehavior.KeyInfo) || localReader.IsBehavior(CommandBehavior.SchemaOnly))
648 && (CommandType != CommandType.StoredProcedure)) {
650 retcode = stmt.NumberOfResultColumns(out cColsAffected);
651 if(retcode == ODBC32.RetCode.SUCCESS || retcode == ODBC32.RetCode.SUCCESS_WITH_INFO) {
652 if(cColsAffected > 0) {
653 localReader.GetSchemaTable();
656 else if(retcode == ODBC32.RetCode.NO_DATA) {
660 // any other returncode indicates an error
661 _connection.HandleError(stmt, retcode);
665 switch(odbcApiMethod) {
666 case ODBC32.SQL_API.SQLEXECDIRECT:
667 if(localReader.IsBehavior(CommandBehavior.KeyInfo) || _isPrepared) {
668 //Already prepared, so use SQLExecute
669 retcode = stmt.Execute();
670 // Build metadata here
671 // localReader.GetSchemaTable();
675 //if (AdapterSwitches.OleDbTrace.TraceInfo) {
676 // ADP.DebugWriteLine("SQLExecDirectW: " + CommandText);
680 retcode = stmt.ExecuteDirect(CommandText);
684 case ODBC32.SQL_API.SQLTABLES:
685 retcode = stmt.Tables((string)methodArguments[0], //TableCatalog
686 (string)methodArguments[1], //TableSchema,
687 (string)methodArguments[2], //TableName
688 (string)methodArguments[3]); //TableType
691 case ODBC32.SQL_API.SQLCOLUMNS:
692 retcode = stmt.Columns((string)methodArguments[0], //TableCatalog
693 (string)methodArguments[1], //TableSchema
694 (string)methodArguments[2], //TableName
695 (string)methodArguments[3]); //ColumnName
698 case ODBC32.SQL_API.SQLPROCEDURES:
699 retcode = stmt.Procedures((string)methodArguments[0], //ProcedureCatalog
700 (string)methodArguments[1], //ProcedureSchema
701 (string)methodArguments[2]); //procedureName
704 case ODBC32.SQL_API.SQLPROCEDURECOLUMNS:
705 retcode = stmt.ProcedureColumns((string)methodArguments[0], //ProcedureCatalog
706 (string)methodArguments[1], //ProcedureSchema
707 (string)methodArguments[2], //procedureName
708 (string)methodArguments[3]); //columnName
711 case ODBC32.SQL_API.SQLSTATISTICS:
712 retcode = stmt.Statistics((string)methodArguments[0], //TableCatalog
713 (string)methodArguments[1], //TableSchema
714 (string)methodArguments[2], //TableName
715 (Int16)methodArguments[3], //IndexTrpe
716 (Int16)methodArguments[4]); //Accuracy
719 case ODBC32.SQL_API.SQLGETTYPEINFO:
720 retcode = stmt.GetTypeInfo((Int16)methodArguments[0]); //SQL Type
724 // this should NEVER happen
725 Debug.Assert(false, "ExecuteReaderObjectcalled with unsupported ODBC API method.");
726 throw ADP.InvalidOperation(method.ToString());
729 //Note: Execute will return NO_DATA for Update/Delete non-row returning queries
730 if((ODBC32.RetCode.SUCCESS != retcode) && (ODBC32.RetCode.NO_DATA != retcode)) {
731 _connection.HandleError(stmt, retcode);
737 parameterBuffer.DangerousRelease();
741 this.weakDataReaderReference = new WeakReference(localReader);
743 // XXXCommand.Execute should position reader on first row returning result
744 // any exceptions in the initial non-row returning results should be thrown
745 // from from ExecuteXXX not the DataReader
746 if(!localReader.IsBehavior(CommandBehavior.SchemaOnly)) {
747 localReader.FirstResult();
749 cmdState = ConnectionState.Fetching;
752 if(ConnectionState.Fetching != cmdState) {
753 if(null != localReader) {
754 // clear bindings so we don't grab output parameters on a failed execute
755 if(null != _parameterCollection) {
756 _parameterCollection.ClearBindings();
758 ((IDisposable)localReader).Dispose();
760 if(ConnectionState.Closed != cmdState) {
761 cmdState = ConnectionState.Closed;
768 override public object ExecuteScalar() {
769 OdbcConnection.ExecutePermission.Demand();
772 using(IDataReader reader = ExecuteReaderObject(0, ADP.ExecuteScalar, false)) {
773 if (reader.Read() && (0 < reader.FieldCount)) {
774 value = reader.GetValue(0);
781 internal string GetDiagSqlState() {
782 return _cmdWrapper.GetDiagSqlState();
785 private void PropertyChanging() {
791 // if the CommandType property is set to TableDirect Prepare does nothing.
792 // if the CommandType property is set to StoredProcedure Prepare should succeed but result
795 // throw InvalidOperationException
796 // if the connection is not set
797 // if the connection is not open
799 override public void Prepare() {
800 OdbcConnection.ExecutePermission.Demand();
801 ODBC32.RetCode retcode;
803 ValidateOpenConnection(ADP.Prepare);
805 if (0 != (ConnectionState.Fetching & _connection.InternalState)) {
806 throw ADP.OpenReaderExists();
809 if (CommandType == CommandType.TableDirect) {
810 return; // do nothing
813 DisposeDeadDataReader();
814 GetStatementHandle();
816 OdbcStatementHandle stmt = _cmdWrapper.StatementHandle;
818 retcode = stmt.Prepare(CommandText);
821 if (ODBC32.RetCode.SUCCESS != retcode) {
822 _connection.HandleError(stmt, retcode);
829 void TrySetStatementAttribute (OdbcStatementHandle stmt, ODBC32.SQL_ATTR stmtAttribute, IntPtr value) {
831 ODBC32.RetCode retcode = stmt.SetStatementAttribute(
834 ODBC32.SQL_IS.UINTEGER);
836 if (retcode == ODBC32.RetCode.ERROR) {
839 stmt.GetDiagnosticField(out sqlState);
841 if ((sqlState == "HYC00") || (sqlState == "HY092")) {
842 Connection.FlagUnsupportedStmtAttr(stmtAttribute);
845 // now what? Should we throw?
850 private void ValidateOpenConnection(string methodName) {
851 // see if we have a connection
852 OdbcConnection connection = Connection;
854 if (null == connection) {
855 throw ADP.ConnectionRequired(methodName);
858 // must have an open and available connection
859 ConnectionState state = connection.State;
861 if (ConnectionState.Open != state) {
862 throw ADP.OpenConnectionRequired(methodName, state);
866 private void ValidateConnectionAndTransaction(string method) {
867 if (null == _connection) {
868 throw ADP.ConnectionRequired(method);
870 _transaction = _connection.SetStateExecuting(method, Transaction);
871 cmdState = ConnectionState.Executing;
875 sealed internal class CMDWrapper {
877 private OdbcStatementHandle _stmt; // hStmt
878 private OdbcStatementHandle _keyinfostmt; // hStmt for keyinfo
880 internal OdbcDescriptorHandle _hdesc; // hDesc
882 internal CNativeBuffer _nativeParameterBuffer; // Native memory for internal memory management
883 // (Performance optimization)
885 internal CNativeBuffer _dataReaderBuf; // Reusable DataReader buffer
887 private readonly OdbcConnection _connection; // Connection
888 private bool _canceling; // true if the command is canceling
889 internal bool _hasBoundColumns;
890 internal bool _ssKeyInfoModeOn; // tells us if the SqlServer specific options are on
891 internal bool _ssKeyInfoModeOff; // a tri-state value would be much better ...
893 internal CMDWrapper (OdbcConnection connection) {
894 _connection = connection;
897 internal bool Canceling {
906 internal OdbcConnection Connection {
912 internal bool HasBoundColumns {
914 // return _hasBoundColumns;
917 _hasBoundColumns = value;
921 internal OdbcStatementHandle StatementHandle {
922 get { return _stmt; }
925 internal OdbcStatementHandle KeyInfoStatement {
931 internal void CreateKeyInfoStatementHandle() {
932 DisposeKeyInfoStatementHandle();
933 _keyinfostmt = _connection.CreateStatementHandle();
936 internal void CreateStatementHandle() {
937 DisposeStatementHandle();
938 _stmt = _connection.CreateStatementHandle();
941 internal void Dispose() {
942 if (null != _dataReaderBuf) {
943 _dataReaderBuf.Dispose();
944 _dataReaderBuf = null;
946 DisposeStatementHandle();
948 CNativeBuffer buffer = _nativeParameterBuffer;
949 _nativeParameterBuffer = null;
950 if (null != buffer) {
953 _ssKeyInfoModeOn = false;
954 _ssKeyInfoModeOff = false;
957 private void DisposeDescriptorHandle() {
958 OdbcDescriptorHandle handle = _hdesc;
959 if (null != handle) {
964 internal void DisposeStatementHandle() {
965 DisposeKeyInfoStatementHandle();
966 DisposeDescriptorHandle();
968 OdbcStatementHandle handle = _stmt;
969 if (null != handle) {
975 internal void DisposeKeyInfoStatementHandle() {
976 OdbcStatementHandle handle = _keyinfostmt;
977 if (null != handle) {
983 internal void FreeStatementHandle(ODBC32.STMT stmt) {
984 DisposeDescriptorHandle();
986 OdbcStatementHandle handle = _stmt;
987 if (null != handle) {
989 ODBC32.RetCode retcode;
990 retcode = handle.FreeStatement(stmt);
991 StatementErrorHandler(retcode);
993 catch (Exception e) {
995 if (ADP.IsCatchableExceptionType(e)) {
1005 internal void FreeKeyInfoStatementHandle(ODBC32.STMT stmt) {
1006 OdbcStatementHandle handle = _keyinfostmt;
1007 if (null != handle) {
1009 handle.FreeStatement(stmt);
1011 catch (Exception e) {
1013 if (ADP.IsCatchableExceptionType(e)) {
1014 _keyinfostmt = null;
1023 // Get the Descriptor Handle for the current statement
1025 internal OdbcDescriptorHandle GetDescriptorHandle(ODBC32.SQL_ATTR attribute) {
1026 OdbcDescriptorHandle hdesc = _hdesc;
1027 if (null == _hdesc) {
1028 _hdesc = hdesc = new OdbcDescriptorHandle(_stmt, attribute);
1033 internal string GetDiagSqlState () {
1035 _stmt.GetDiagnosticField(out sqlstate);
1039 internal void StatementErrorHandler(ODBC32.RetCode retcode) {
1041 case ODBC32.RetCode.SUCCESS:
1042 case ODBC32.RetCode.SUCCESS_WITH_INFO:
1043 _connection.HandleErrorNoThrow(_stmt, retcode);
1046 throw _connection.HandleErrorNoThrow(_stmt, retcode);
1050 internal void UnbindStmtColumns() {
1051 if (_hasBoundColumns) {
1052 FreeStatementHandle(ODBC32.STMT.UNBIND);
1053 _hasBoundColumns = false;