OdbcCommand.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Data / System / Data / Odbc / OdbcCommand.cs / 1 / OdbcCommand.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
using System;
using System.ComponentModel;            //Component 
using System.Data;
using System.Data.Common;
using System.Data.ProviderBase;
using System.Diagnostics; 
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; 
using System.Threading; 

// todo: 
// There may be two ways to improve performance:
// 1. pool statements on the connection object
// 2. Do not create a datareader object for non-datareader returning command execution.
// 
// We do not want to do the effort unless we have to squeze performance.
 
 

namespace System.Data.Odbc { 

    [
    DefaultEvent("RecordsAffected"),
    ToolboxItem(true), 
    Designer("Microsoft.VSDesigner.Data.VS.OdbcCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner)
    ] 
#if WINFSInternalOnly 
    internal
#else 
    public
#endif
    sealed class OdbcCommand : DbCommand, ICloneable {
        private static int          _objectTypeCount; // Bid counter 
        internal readonly int       ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 
        private string              _commandText; 
        private CommandType         _commandType;
        private int                 _commandTimeout = ADP.DefaultCommandTimeout; 
        private UpdateRowSource     _updatedRowSource = UpdateRowSource.Both;
        private bool                _designTimeInvisible;
        private bool                _isPrepared;                        // true if the command is prepared
 
        private OdbcConnection      _connection;
        private OdbcTransaction     _transaction; 
 
        private WeakReference       weakDataReaderReference;
 
        private CMDWrapper          _cmdWrapper;

        private OdbcParameterCollection    _parameterCollection;   // Parameter collection
 
        private ConnectionState     cmdState;
 
        public OdbcCommand() : base() { 
            GC.SuppressFinalize(this);
        } 

        public OdbcCommand(string cmdText) : this() {
            // note: arguments are assigned to properties so we do not have to trace them.
            // We still need to include them into the argument list of the definition! 
            CommandText = cmdText;
        } 
 
        public OdbcCommand(string cmdText, OdbcConnection connection) : this() {
            CommandText = cmdText; 
            Connection  = connection;
        }

        public OdbcCommand(string cmdText, OdbcConnection connection, OdbcTransaction transaction) : this() { 
            CommandText = cmdText;
            Connection = connection; 
            Transaction = transaction; 
        }
 
        private void DisposeDeadDataReader() {
            if (ConnectionState.Fetching == cmdState) {
                if (null != this.weakDataReaderReference && !this.weakDataReaderReference.IsAlive) {
                    if (_cmdWrapper != null) { 
                        _cmdWrapper.FreeKeyInfoStatementHandle(ODBC32.STMT.CLOSE);
                        _cmdWrapper.FreeStatementHandle(ODBC32.STMT.CLOSE); 
                    } 
                    CloseFromDataReader();
                } 
            }
        }

        private void DisposeDataReader() { 
            if (null != this.weakDataReaderReference) {
                IDisposable reader = (IDisposable) this.weakDataReaderReference.Target; 
                if ((null != reader) && this.weakDataReaderReference.IsAlive) { 
                    ((IDisposable) reader).Dispose();
                } 
                CloseFromDataReader();
            }
        }
 
        internal void DisconnectFromDataReaderAndConnection () {
            // get a reference to the datareader if it is alive 
            OdbcDataReader liveReader = null; 
            if (this.weakDataReaderReference != null){
                OdbcDataReader reader; 
                reader = (OdbcDataReader)this.weakDataReaderReference.Target;
                if (this.weakDataReaderReference.IsAlive) {
                    liveReader = reader;
                } 
            }
 
            // remove reference to this from the live datareader 
            if (liveReader != null) {
                liveReader.Command = null; 
            }

            _transaction = null;
 
            if (null != _connection) {
                _connection.RemoveWeakReference(this); 
                _connection = null; 
            }
 
            // if the reader is dead we have to dismiss the statement
            if (liveReader == null){
                CloseCommandWrapper();
            } 
            // else DataReader now has exclusive ownership
            _cmdWrapper = null; 
        } 

        override protected void Dispose(bool disposing) { // MDAC 65459 
            if (disposing) {
                // release mananged objects
                // in V1.0, V1.1 the Connection,Parameters,CommandText,Transaction where reset
                this.DisconnectFromDataReaderAndConnection (); 
                _parameterCollection = null;
                CommandText = null; 
            } 
            _cmdWrapper = null;                         // let go of the CommandWrapper
            _isPrepared = false; 

            base.Dispose(disposing);    // notify base classes
        }
 
        internal bool Canceling {
            get { 
                return _cmdWrapper.Canceling; 
            }
        } 

        [
        ResCategoryAttribute(Res.DataCategory_Data),
        DefaultValue(""), 
        RefreshProperties(RefreshProperties.All), // MDAC 67707
        ResDescriptionAttribute(Res.DbCommand_CommandText), 
        Editor("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing) 
        ]
        override public string CommandText { 
            get {
                string value = _commandText;
                return ((null != value) ? value : ADP.StrEmpty);
            } 
            set {
                if (Bid.TraceOn) { 
                    Bid.Trace(" %d#, '", ObjectID); 
                    Bid.PutStr(value); // Use PutStr to write out entire string
                    Bid.Trace("'\n"); 
                }
                if (0 != ADP.SrcCompare(_commandText, value)) {
                    PropertyChanging();
                    _commandText = value; 
                }
            } 
        } 

        [ 
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DbCommand_CommandTimeout),
        ]
        override public int CommandTimeout { // V1.2.3300, XXXCommand V1.0.5000 
            get {
                return _commandTimeout; 
            } 
            set {
                Bid.Trace(" %d#, %d\n", ObjectID, value); 
                if (value < 0) {
                    throw ADP.InvalidCommandTimeout(value);
                }
                if (value != _commandTimeout) { 
                    PropertyChanging();
                    _commandTimeout = value; 
                } 
            }
        } 

        public void ResetCommandTimeout() { // V1.2.3300
            if (ADP.DefaultCommandTimeout != _commandTimeout) {
                PropertyChanging(); 
                _commandTimeout = ADP.DefaultCommandTimeout;
            } 
        } 

        private bool ShouldSerializeCommandTimeout() { // V1.2.3300 
            return (ADP.DefaultCommandTimeout != _commandTimeout);
        }

        [ 
        DefaultValue(System.Data.CommandType.Text),
        RefreshProperties(RefreshProperties.All), 
        ResCategoryAttribute(Res.DataCategory_Data), 
        ResDescriptionAttribute(Res.DbCommand_CommandType),
        ] 
        override public CommandType CommandType {
            get {
                CommandType cmdType = _commandType;
                return ((0 != cmdType) ? cmdType : CommandType.Text); 
            }
            set  { 
                switch(value) { // @perfnote: Enum.IsDefined 
                case CommandType.Text:
                case CommandType.StoredProcedure: 
                    PropertyChanging();
                    _commandType = value;
                    break;
                case CommandType.TableDirect: 
                    throw ODBC.NotSupportedCommandType(value);
                default: 
                    throw ADP.InvalidCommandType(value); 
                }
            } 
        }

        // This will establish a relationship between the command and the connection
        [ 
        DefaultValue(null),
        ResCategoryAttribute(Res.DataCategory_Behavior), 
        ResDescriptionAttribute(Res.DbCommand_Connection), 
        Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
        ] 
        new public OdbcConnection Connection {
            get {
                return _connection;
            } 
            set {
                if (value != _connection) { 
                    PropertyChanging(); 
                    this.DisconnectFromDataReaderAndConnection();
                    Debug.Assert(null == _cmdWrapper, "has CMDWrapper when setting connection"); 
                    _connection = value;
                    //OnSchemaChanged();
                }
            } 
        }
 
        override protected DbConnection DbConnection { // V1.2.3300 
            get {
                return Connection; 
            }
            set {
                Connection = (OdbcConnection)value;
            } 
        }
 
        override protected DbParameterCollection DbParameterCollection { // V1.2.3300 
            get {
                return Parameters; 
            }
        }

        override protected DbTransaction DbTransaction { // V1.2.3300 
            get {
                return Transaction; 
            } 
            set {
                Transaction = (OdbcTransaction)value; 
            }
        }

        // @devnote: By default, the cmd object is visible on the design surface (i.e. VS7 Server Tray) 
        // to limit the number of components that clutter the design surface,
        // when the DataAdapter design wizard generates the insert/update/delete commands it will 
        // set the DesignTimeVisible property to false so that cmds won't appear as individual objects 
        [
        DefaultValue(true), 
        DesignOnly(true),
        Browsable(false),
        EditorBrowsableAttribute(EditorBrowsableState.Never),
        ] 
        public override bool DesignTimeVisible { // V1.2.3300, XXXCommand V1.0.5000
            get { 
                return !_designTimeInvisible; 
            }
            set { 
                _designTimeInvisible = !value;
                TypeDescriptor.Refresh(this); // VS7 208845
            }
        } 

        internal bool HasParameters { 
            get { 
                return (null != _parameterCollection);
            } 
        }

        [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DbCommand_Parameters), 
        ] 
        new public OdbcParameterCollection Parameters {
            get { 
                if (null == _parameterCollection) {
                    _parameterCollection = new OdbcParameterCollection();
                }
                return _parameterCollection; 
            }
        } 
 
        [
        Browsable(false), 
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.DbCommand_Transaction),
        ]
        new public OdbcTransaction Transaction { 
            get {
                if ((null != _transaction) && (null == _transaction.Connection)) { 
                    _transaction = null;       // Dawn of the Dead 
                }
                return _transaction; 
            }
            set {
                if (_transaction != value) {
                    PropertyChanging(); // fire event before value is validated 
                    _transaction = value;
                } 
            } 
        }
 
        [
        DefaultValue(System.Data.UpdateRowSource.Both),
        ResCategoryAttribute(Res.DataCategory_Update),
        ResDescriptionAttribute(Res.DbCommand_UpdatedRowSource), 
        ]
        override public UpdateRowSource UpdatedRowSource { // V1.2.3300, XXXCommand V1.0.5000 
            get { 
                return _updatedRowSource;
            } 
            set {
                switch(value) { // @perfnote: Enum.IsDefined
                case UpdateRowSource.None:
                case UpdateRowSource.OutputParameters: 
                case UpdateRowSource.FirstReturnedRecord:
                case UpdateRowSource.Both: 
                    _updatedRowSource = value; 
                    break;
                default: 
                    throw ADP.InvalidUpdateRowSource(value);
                }
            }
        } 

        internal OdbcDescriptorHandle GetDescriptorHandle(ODBC32.SQL_ATTR attribute) { 
            return _cmdWrapper.GetDescriptorHandle(attribute); 
        }
 

        // GetStatementHandle
        // ------------------
        // Try to return a cached statement handle. 
        //
        // Creates a CmdWrapper object if necessary 
        // If no handle is available a handle will be allocated. 
        // Bindings will be unbound if a handle is cached and the bindings are invalid.
        // 
        internal CMDWrapper GetStatementHandle () {
            // update the command wrapper object, allocate buffer
            // create reader object
            // 
            if (_cmdWrapper==null) {
                _cmdWrapper = new CMDWrapper(_connection); 
 
                Debug.Assert(null != _connection, "GetStatementHandle without connection?");
                _connection.AddWeakReference(this, OdbcReferenceCollection.CommandTag); 
            }

            if (_cmdWrapper._dataReaderBuf == null) {
                _cmdWrapper._dataReaderBuf = new CNativeBuffer(4096); 
            }
 
            // if there is already a statement handle we need to do some cleanup 
            //
            if (null == _cmdWrapper.StatementHandle) { 
                _isPrepared = false;
                _cmdWrapper.CreateStatementHandle();
            }
            else if ((null != _parameterCollection) && _parameterCollection.RebindCollection) { 
                _cmdWrapper.FreeStatementHandle(ODBC32.STMT.RESET_PARAMS);
            } 
            return _cmdWrapper; 
        }
 
        // OdbcCommand.Cancel()
        //
        // In ODBC3.0 ... a call to SQLCancel when no processing is done has no effect at all
        // (ODBC Programmer's Reference ...) 
        //
 
        override public void Cancel() { 
            CMDWrapper wrapper = _cmdWrapper;
            if (null != wrapper) { 
                wrapper.Canceling = true;
                OdbcStatementHandle stmt = wrapper.StatementHandle;
                if (null != stmt) {
                    lock (stmt) { 
                        // Cancel the statement
                        ODBC32.RetCode retcode = stmt.Cancel(); 
 
                        // copy of StatementErrorHandler, because stmt may become null
                        switch(retcode) { 
                        case ODBC32.RetCode.SUCCESS:
                        case ODBC32.RetCode.SUCCESS_WITH_INFO:
                            // don't fire info message events on cancel
                            break; 
                        default:
                            throw wrapper.Connection.HandleErrorNoThrow(stmt, retcode); 
                        } 
                    }
                } 
            }
        }

 
        object ICloneable.Clone() {
            OdbcCommand clone = new OdbcCommand(); 
            Bid.Trace(" %d#, clone=%d#\n", ObjectID, clone.ObjectID); 
            clone.CommandText = CommandText;
            clone.CommandTimeout = this.CommandTimeout; 
            clone.CommandType = CommandType;
            clone.Connection = this.Connection;
            clone.Transaction = this.Transaction;
            clone.UpdatedRowSource = UpdatedRowSource; 

            if ((null != _parameterCollection) && (0 < Parameters.Count)) { 
                OdbcParameterCollection parameters = clone.Parameters; 
                foreach(ICloneable parameter in Parameters) {
                    parameters.Add(parameter.Clone()); 
                }
            }
            return clone;
        } 

        internal bool RecoverFromConnection() { 
            DisposeDeadDataReader(); 
            return (ConnectionState.Closed == cmdState);
        } 

        private void CloseCommandWrapper() {
            CMDWrapper wrapper = _cmdWrapper;
            if (null != wrapper) { 
                try {
                    wrapper.Dispose(); 
 
                    if (null != _connection) {
                        _connection.RemoveWeakReference(this); 
                    }
                }
                finally {
                    _cmdWrapper = null; 
                }
            } 
        } 

        internal void CloseFromConnection () { 
            if (null != _parameterCollection) {
                _parameterCollection.RebindCollection = true;
            }
            DisposeDataReader(); 
            CloseCommandWrapper();
            _isPrepared = false; 
            _transaction = null; 
        }
 
        internal void CloseFromDataReader() {
            this.weakDataReaderReference = null;
            this.cmdState = ConnectionState.Closed;
        } 

        new public OdbcParameter CreateParameter() { 
            return new OdbcParameter(); 
            }
 
        override protected DbParameter CreateDbParameter() {
            return CreateParameter();
        }
 
        override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) {
            return ExecuteReader(behavior); 
        } 

        override public int ExecuteNonQuery() { 
            OdbcConnection.ExecutePermission.Demand();
            using (OdbcDataReader reader = ExecuteReaderObject(0, ADP.ExecuteNonQuery, false)) {
                reader.Close();
                return reader.RecordsAffected; 
            }
        } 
 
        new public OdbcDataReader ExecuteReader() {
            return ExecuteReader(0/*CommandBehavior*/); 
        }


        new public OdbcDataReader ExecuteReader(CommandBehavior behavior) { 
            OdbcConnection.ExecutePermission.Demand();
            return ExecuteReaderObject(behavior, ADP.ExecuteReader, true); 
        } 

        internal  OdbcDataReader ExecuteReaderFromSQLMethod(object[] methodArguments, 
                                                            ODBC32.SQL_API method){

            return ExecuteReaderObject(CommandBehavior.Default,method.ToString(),true,methodArguments,method);
 
        }
 
        private OdbcDataReader ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader) { // MDAC 68324 

            if ((CommandText == null) || (CommandText.Length == 0)) { 
                throw (ADP.CommandTextRequired(method));
            }
            // using all functions to tell ExecuteReaderObject that
            return ExecuteReaderObject(behavior,method,needReader,null,ODBC32.SQL_API.SQLEXECDIRECT); 
        }
 
        private OdbcDataReader ExecuteReaderObject(CommandBehavior behavior, 
                                                   string method,
                                                   bool needReader, 
                                                   object[] methodArguments,
                                                   ODBC32.SQL_API odbcApiMethod) { // MDAC 68324

            OdbcDataReader localReader = null; 
            try {
                DisposeDeadDataReader();    // this is a no-op if cmdState is not Fetching 
                ValidateConnectionAndTransaction(method);  // cmdState will change to Executing 

                if(0 != (CommandBehavior.SingleRow & behavior)) { 
                    // CommandBehavior.SingleRow implies CommandBehavior.SingleResult
                    behavior |= CommandBehavior.SingleResult;
                }
 
                ODBC32.RetCode retcode;
 
                OdbcStatementHandle stmt = GetStatementHandle().StatementHandle; 
                _cmdWrapper.Canceling = false;
 
                if(null != weakDataReaderReference) {
                    if(weakDataReaderReference.IsAlive) {
                        object target = weakDataReaderReference.Target;
                        if(null != target && weakDataReaderReference.IsAlive) { 
                            if(!((OdbcDataReader)target).IsClosed) {
                                throw ADP.OpenReaderExists(); // MDAC 66411 
                            } 
                        }
                    } 
                }
                localReader = new OdbcDataReader(this, _cmdWrapper, behavior);

                //Set command properties 
                //Not all drivers support timeout. So fail silently if error
                if(!Connection.ProviderInfo.NoQueryTimeout) { 
                    TrySetStatementAttribute(stmt, 
                        ODBC32.SQL_ATTR.QUERY_TIMEOUT,
                        (IntPtr)this.CommandTimeout); 
                }

                // todo: If we remember the state we can omit a lot of SQLSetStmtAttrW calls ...
                // if we do not create a reader we do not even need to do that 
                if(needReader) {
                    if(Connection.IsV3Driver) { 
                        if(!Connection.ProviderInfo.NoSqlSoptSSNoBrowseTable && !Connection.ProviderInfo.NoSqlSoptSSHiddenColumns) { 
                            // Need to get the metadata information
 
                            //SQLServer actually requires browse info turned on ahead of time...
                            //Note: We ignore any failures, since this is SQLServer specific
                            //We won't specialcase for SQL Server but at least for non-V3 drivers
                            if(localReader.IsBehavior(CommandBehavior.KeyInfo)) { 
                                if(!_cmdWrapper._ssKeyInfoModeOn) {
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE, (IntPtr)ODBC32.SQL_NB.ON); 
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS, (IntPtr)ODBC32.SQL_HC.ON); 
                                    _cmdWrapper._ssKeyInfoModeOff = false;
                                    _cmdWrapper._ssKeyInfoModeOn = true; 
                                }
                            }
                            else {
                                if(!_cmdWrapper._ssKeyInfoModeOff) { 
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE, (IntPtr)ODBC32.SQL_NB.OFF);
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS, (IntPtr)ODBC32.SQL_HC.OFF); 
                                    _cmdWrapper._ssKeyInfoModeOff = true; 
                                    _cmdWrapper._ssKeyInfoModeOn = false;
                                } 
                            }
                        }
                    }
                } 

                if(localReader.IsBehavior(CommandBehavior.KeyInfo) || 
                    localReader.IsBehavior(CommandBehavior.SchemaOnly)) { 

                    retcode = stmt.Prepare(CommandText); 

                    if(ODBC32.RetCode.SUCCESS != retcode) {
                        _connection.HandleError(stmt, retcode);
                    } 
                }
 
                bool mustRelease = false; 
                CNativeBuffer parameterBuffer = _cmdWrapper._nativeParameterBuffer;
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try {
                    //Handle Parameters
                    //Note: We use the internal variable as to not instante a new object collection, 
                    //for the the common case of using no parameters.
                    if((null != _parameterCollection) && (0 < _parameterCollection.Count)) { 
                        int parameterBufferSize = _parameterCollection.CalcParameterBufferSize(this); 

                        if(null == parameterBuffer || parameterBuffer.Length < parameterBufferSize) { 
                            if (null != parameterBuffer) {
                                parameterBuffer.Dispose();
                            }
                            parameterBuffer = new CNativeBuffer(parameterBufferSize); 
                            _cmdWrapper._nativeParameterBuffer = parameterBuffer;
                        } 
                        else { 
                            parameterBuffer.ZeroMemory();
                        } 

                        parameterBuffer.DangerousAddRef(ref mustRelease);

                        _parameterCollection.Bind(this, _cmdWrapper, parameterBuffer); 
                    }
 
                    if(!localReader.IsBehavior(CommandBehavior.SchemaOnly)) { 

                        // Can't get the KeyInfo after command execution (SQL Server only since it does not support multiple 
                        // results on the same connection). Stored procedures (SP) do not return metadata before actual execution
                        // Need to check the column count since the command type may not be set to SP for a SP.
                        if((localReader.IsBehavior(CommandBehavior.KeyInfo) || localReader.IsBehavior(CommandBehavior.SchemaOnly))
                            && (CommandType != CommandType.StoredProcedure)) { 
                            Int16 cColsAffected;
                            retcode = stmt.NumberOfResultColumns(out cColsAffected); 
                            if(retcode == ODBC32.RetCode.SUCCESS || retcode == ODBC32.RetCode.SUCCESS_WITH_INFO) { 
                                if(cColsAffected > 0) {
                                    localReader.GetSchemaTable(); 
                                }
                            }
                            else if(retcode == ODBC32.RetCode.NO_DATA) {
                                // do nothing 
                            }
                            else { 
                                // any other returncode indicates an error 
                                _connection.HandleError(stmt, retcode);
                            } 
                        }

                        switch(odbcApiMethod) {
                            case ODBC32.SQL_API.SQLEXECDIRECT: 
                                if(localReader.IsBehavior(CommandBehavior.KeyInfo) || _isPrepared) {
                                    //Already prepared, so use SQLExecute 
                                    retcode = stmt.Execute(); 
                                    // Build metadata here
                                    // localReader.GetSchemaTable(); 
                                }
                                else {
#if DEBUG
                                    //if (AdapterSwitches.OleDbTrace.TraceInfo) { 
                                    //    ADP.DebugWriteLine("SQLExecDirectW: " + CommandText);
                                    //} 
#endif 
                                    //SQLExecDirect
                                    retcode = stmt.ExecuteDirect(CommandText); 
                                }
                                break;

                            case ODBC32.SQL_API.SQLTABLES: 
                                retcode = stmt.Tables((string)methodArguments[0],  //TableCatalog
                                    (string)methodArguments[1],  //TableSchema, 
                                    (string)methodArguments[2],  //TableName 
                                    (string)methodArguments[3]); //TableType
                                break; 

                            case ODBC32.SQL_API.SQLCOLUMNS:
                                retcode = stmt.Columns((string)methodArguments[0],  //TableCatalog
                                    (string)methodArguments[1],  //TableSchema 
                                    (string)methodArguments[2],  //TableName
                                    (string)methodArguments[3]); //ColumnName 
                                break; 

                            case ODBC32.SQL_API.SQLPROCEDURES: 
                                retcode = stmt.Procedures((string)methodArguments[0],  //ProcedureCatalog
                                    (string)methodArguments[1],  //ProcedureSchema
                                    (string)methodArguments[2]); //procedureName
                                break; 

                            case ODBC32.SQL_API.SQLPROCEDURECOLUMNS: 
                                retcode = stmt.ProcedureColumns((string)methodArguments[0],  //ProcedureCatalog 
                                    (string)methodArguments[1],  //ProcedureSchema
                                    (string)methodArguments[2],  //procedureName 
                                    (string)methodArguments[3]); //columnName
                                break;

                            case ODBC32.SQL_API.SQLSTATISTICS: 
                                retcode = stmt.Statistics((string)methodArguments[0],  //TableCatalog
                                    (string)methodArguments[1],  //TableSchema 
                                    (string)methodArguments[2],  //TableName 
                                    (Int16)methodArguments[3],   //IndexTrpe
                                    (Int16)methodArguments[4]);  //Accuracy 
                                break;

                            case ODBC32.SQL_API.SQLGETTYPEINFO:
                                retcode = stmt.GetTypeInfo((Int16)methodArguments[0]);  //SQL Type 
                                break;
 
                            default: 
                                // this should NEVER happen
                                Debug.Assert(false, "ExecuteReaderObjectcalled with unsupported ODBC API method."); 
                                throw ADP.InvalidOperation(method.ToString());
                        }

                        //Note: Execute will return NO_DATA for Update/Delete non-row returning queries 
                        if((ODBC32.RetCode.SUCCESS != retcode) && (ODBC32.RetCode.NO_DATA != retcode)) {
                            _connection.HandleError(stmt, retcode); 
                        } 
                    } // end SchemaOnly
                } 
                finally {
                    if(mustRelease) {
                        parameterBuffer.DangerousRelease();
                    } 
                }
 
                this.weakDataReaderReference = new WeakReference(localReader); 

                // XXXCommand.Execute should position reader on first row returning result 
                // any exceptions in the initial non-row returning results should be thrown
                // from from ExecuteXXX not the DataReader
                if(!localReader.IsBehavior(CommandBehavior.SchemaOnly)) {
                    localReader.FirstResult(); 
                }
                cmdState = ConnectionState.Fetching; 
            } 
            finally {
                if(ConnectionState.Fetching != cmdState) { 
                    if(null != localReader) {
                        // clear bindings so we don't grab output parameters on a failed execute
                        if(null != _parameterCollection) {
                            _parameterCollection.ClearBindings(); 
                        }
                        ((IDisposable)localReader).Dispose(); 
                    } 
                    if(ConnectionState.Closed != cmdState) {
                        cmdState = ConnectionState.Closed; 
                    }
                }
            }
            return localReader; 
        }
 
        override public object ExecuteScalar() { 
            OdbcConnection.ExecutePermission.Demand();
 
            object value = null;
            using(IDataReader reader = ExecuteReaderObject(0, ADP.ExecuteScalar, false)) {
                if (reader.Read() && (0 < reader.FieldCount)) {
                    value = reader.GetValue(0); 
                }
                reader.Close(); 
            } 
            return value;
        } 

        internal string GetDiagSqlState() {
            return _cmdWrapper.GetDiagSqlState();
        } 

        private void PropertyChanging() { 
            _isPrepared = false; 
        }
 
        // Prepare
        //
        // if the CommandType property is set to TableDirect Prepare does nothing.
        // if the CommandType property is set to StoredProcedure Prepare should succeed but result 
        // in a no-op
        // 
        // throw InvalidOperationException 
        // if the connection is not set
        // if the connection is not open 
        //
        override public void Prepare() {
            OdbcConnection.ExecutePermission.Demand();
            ODBC32.RetCode retcode; 

            ValidateOpenConnection(ADP.Prepare); 
 
            if (0 != (ConnectionState.Fetching & _connection.InternalState)) {
                throw ADP.OpenReaderExists(); 
            }

            if (CommandType == CommandType.TableDirect) {
                return; // do nothing 
            }
 
            DisposeDeadDataReader(); 
            GetStatementHandle();
 
            OdbcStatementHandle stmt = _cmdWrapper.StatementHandle;

            retcode = stmt.Prepare(CommandText);
 

            if (ODBC32.RetCode.SUCCESS != retcode) { 
                _connection.HandleError(stmt, retcode); 
            }
            _isPrepared = true; 
        }


 
        void TrySetStatementAttribute (OdbcStatementHandle stmt, ODBC32.SQL_ATTR stmtAttribute, IntPtr value) {
 
            ODBC32.RetCode retcode = stmt.SetStatementAttribute( 
                stmtAttribute,
                value, 
                ODBC32.SQL_IS.UINTEGER);

            if (retcode == ODBC32.RetCode.ERROR) {
 
                string sqlState;
                stmt.GetDiagnosticField(out sqlState); 
 
                if ((sqlState == "HYC00") || (sqlState == "HY092")) {
                    Connection.FlagUnsupportedStmtAttr(stmtAttribute); 
                }
                else {
                    // now what? Should we throw?
                } 
            }
        } 
 
        private void ValidateOpenConnection(string methodName) {
            // see if we have a connection 
            OdbcConnection connection = Connection;

            if (null == connection) {
                throw ADP.ConnectionRequired(methodName); 
            }
 
            // must have an open and available connection 
            ConnectionState state = connection.State;
 
            if (ConnectionState.Open != state) {
                throw ADP.OpenConnectionRequired(methodName, state);
            }
        } 

        private void ValidateConnectionAndTransaction(string method) { 
            if (null == _connection) { 
                throw ADP.ConnectionRequired(method);
            } 
            _transaction = _connection.SetStateExecuting(method, Transaction);
            cmdState = ConnectionState.Executing;
        }
 
    }
    sealed internal class CMDWrapper { 
 
        private OdbcStatementHandle _stmt;                  // hStmt
        private OdbcStatementHandle _keyinfostmt;           // hStmt for keyinfo 

        internal OdbcDescriptorHandle  _hdesc;              // hDesc

        internal CNativeBuffer _nativeParameterBuffer;      // Native memory for internal memory management 
        // (Performance optimization)
 
        internal CNativeBuffer      _dataReaderBuf;         // Reusable DataReader buffer 

        private readonly OdbcConnection _connection;        // Connection 
        private bool                _canceling;             // true if the command is canceling
        internal bool               _hasBoundColumns;
        internal bool               _ssKeyInfoModeOn;       // tells us if the SqlServer specific options are on
        internal bool               _ssKeyInfoModeOff;      // a tri-state value would be much better ... 

        internal CMDWrapper (OdbcConnection connection) { 
            _connection = connection; 
        }
 
        internal bool Canceling {
            get {
                return _canceling;
            } 
            set {
                _canceling = value; 
            } 
        }
 
        internal OdbcConnection Connection {
            get {
                return _connection;
            } 
        }
 
        internal bool HasBoundColumns { 
//            get {
//                return _hasBoundColumns; 
//            }
            set {
                _hasBoundColumns = value;
            } 
        }
 
        internal OdbcStatementHandle StatementHandle { 
            get { return _stmt; }
        } 

        internal OdbcStatementHandle KeyInfoStatement {
            get {
                return _keyinfostmt; 
            }
        } 
 
        internal void CreateKeyInfoStatementHandle() {
            DisposeKeyInfoStatementHandle(); 
            _keyinfostmt =  _connection.CreateStatementHandle();
        }

        internal void CreateStatementHandle() { 
            DisposeStatementHandle();
            _stmt =  _connection.CreateStatementHandle(); 
        } 

        internal void Dispose() { 
            if (null != _dataReaderBuf) {
                _dataReaderBuf.Dispose();
                _dataReaderBuf = null;
            } 
            DisposeStatementHandle();
 
            CNativeBuffer buffer = _nativeParameterBuffer; 
            _nativeParameterBuffer = null;
            if (null != buffer) { 
                buffer.Dispose();
            }
            _ssKeyInfoModeOn = false;
            _ssKeyInfoModeOff = false; 
        }
 
        private void DisposeDescriptorHandle() { 
            OdbcDescriptorHandle handle = _hdesc;
            if (null != handle) { 
                _hdesc = null;
                handle.Dispose();
            }
        } 
        internal void DisposeStatementHandle() {
            DisposeKeyInfoStatementHandle(); 
            DisposeDescriptorHandle(); 

            OdbcStatementHandle handle = _stmt; 
            if (null != handle) {
                _stmt = null;
                handle.Dispose();
            } 
        }
 
        internal void DisposeKeyInfoStatementHandle() { 
            OdbcStatementHandle handle = _keyinfostmt;
            if (null != handle) { 
                _keyinfostmt = null;
                handle.Dispose();
            }
        } 

        internal void FreeStatementHandle(ODBC32.STMT stmt) { 
            DisposeDescriptorHandle(); 

            OdbcStatementHandle handle = _stmt; 
            if (null != handle) {
                try {
                    ODBC32.RetCode retcode;
                    retcode = handle.FreeStatement(stmt); 
                    StatementErrorHandler(retcode);
                } 
                catch (Exception e) { 
                    //
                    if (ADP.IsCatchableExceptionType(e)) { 
                        _stmt = null;
                        handle.Dispose();
                    }
 
                    throw;
                } 
            } 
        }
 
        internal void FreeKeyInfoStatementHandle(ODBC32.STMT stmt) {
            OdbcStatementHandle handle = _keyinfostmt;
            if (null != handle) {
                try { 
                    handle.FreeStatement(stmt);
                } 
                catch (Exception e) { 
                    //
                    if (ADP.IsCatchableExceptionType(e)) { 
                        _keyinfostmt = null;
                        handle.Dispose();
                    }
 
                    throw;
                } 
            } 
        }
 
        // Get the Descriptor Handle for the current statement
        //
        internal OdbcDescriptorHandle GetDescriptorHandle(ODBC32.SQL_ATTR attribute) {
            OdbcDescriptorHandle hdesc = _hdesc; 
            if (null == _hdesc) {
                _hdesc = hdesc = new OdbcDescriptorHandle(_stmt, attribute); 
            } 
            return hdesc;
        } 

        internal string GetDiagSqlState () {
            string sqlstate;
            _stmt.GetDiagnosticField(out sqlstate); 
            return sqlstate;
        } 
 
        internal void StatementErrorHandler(ODBC32.RetCode retcode) {
            switch(retcode) { 
            case ODBC32.RetCode.SUCCESS:
            case ODBC32.RetCode.SUCCESS_WITH_INFO:
                _connection.HandleErrorNoThrow(_stmt, retcode);
                break; 
            default:
                throw _connection.HandleErrorNoThrow(_stmt, retcode); 
            } 
        }
 
        internal void UnbindStmtColumns() {
            if (_hasBoundColumns) {
                FreeStatementHandle(ODBC32.STMT.UNBIND);
                _hasBoundColumns = false; 
            }
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
using System;
using System.ComponentModel;            //Component 
using System.Data;
using System.Data.Common;
using System.Data.ProviderBase;
using System.Diagnostics; 
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; 
using System.Threading; 

// todo: 
// There may be two ways to improve performance:
// 1. pool statements on the connection object
// 2. Do not create a datareader object for non-datareader returning command execution.
// 
// We do not want to do the effort unless we have to squeze performance.
 
 

namespace System.Data.Odbc { 

    [
    DefaultEvent("RecordsAffected"),
    ToolboxItem(true), 
    Designer("Microsoft.VSDesigner.Data.VS.OdbcCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner)
    ] 
#if WINFSInternalOnly 
    internal
#else 
    public
#endif
    sealed class OdbcCommand : DbCommand, ICloneable {
        private static int          _objectTypeCount; // Bid counter 
        internal readonly int       ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 
        private string              _commandText; 
        private CommandType         _commandType;
        private int                 _commandTimeout = ADP.DefaultCommandTimeout; 
        private UpdateRowSource     _updatedRowSource = UpdateRowSource.Both;
        private bool                _designTimeInvisible;
        private bool                _isPrepared;                        // true if the command is prepared
 
        private OdbcConnection      _connection;
        private OdbcTransaction     _transaction; 
 
        private WeakReference       weakDataReaderReference;
 
        private CMDWrapper          _cmdWrapper;

        private OdbcParameterCollection    _parameterCollection;   // Parameter collection
 
        private ConnectionState     cmdState;
 
        public OdbcCommand() : base() { 
            GC.SuppressFinalize(this);
        } 

        public OdbcCommand(string cmdText) : this() {
            // note: arguments are assigned to properties so we do not have to trace them.
            // We still need to include them into the argument list of the definition! 
            CommandText = cmdText;
        } 
 
        public OdbcCommand(string cmdText, OdbcConnection connection) : this() {
            CommandText = cmdText; 
            Connection  = connection;
        }

        public OdbcCommand(string cmdText, OdbcConnection connection, OdbcTransaction transaction) : this() { 
            CommandText = cmdText;
            Connection = connection; 
            Transaction = transaction; 
        }
 
        private void DisposeDeadDataReader() {
            if (ConnectionState.Fetching == cmdState) {
                if (null != this.weakDataReaderReference && !this.weakDataReaderReference.IsAlive) {
                    if (_cmdWrapper != null) { 
                        _cmdWrapper.FreeKeyInfoStatementHandle(ODBC32.STMT.CLOSE);
                        _cmdWrapper.FreeStatementHandle(ODBC32.STMT.CLOSE); 
                    } 
                    CloseFromDataReader();
                } 
            }
        }

        private void DisposeDataReader() { 
            if (null != this.weakDataReaderReference) {
                IDisposable reader = (IDisposable) this.weakDataReaderReference.Target; 
                if ((null != reader) && this.weakDataReaderReference.IsAlive) { 
                    ((IDisposable) reader).Dispose();
                } 
                CloseFromDataReader();
            }
        }
 
        internal void DisconnectFromDataReaderAndConnection () {
            // get a reference to the datareader if it is alive 
            OdbcDataReader liveReader = null; 
            if (this.weakDataReaderReference != null){
                OdbcDataReader reader; 
                reader = (OdbcDataReader)this.weakDataReaderReference.Target;
                if (this.weakDataReaderReference.IsAlive) {
                    liveReader = reader;
                } 
            }
 
            // remove reference to this from the live datareader 
            if (liveReader != null) {
                liveReader.Command = null; 
            }

            _transaction = null;
 
            if (null != _connection) {
                _connection.RemoveWeakReference(this); 
                _connection = null; 
            }
 
            // if the reader is dead we have to dismiss the statement
            if (liveReader == null){
                CloseCommandWrapper();
            } 
            // else DataReader now has exclusive ownership
            _cmdWrapper = null; 
        } 

        override protected void Dispose(bool disposing) { // MDAC 65459 
            if (disposing) {
                // release mananged objects
                // in V1.0, V1.1 the Connection,Parameters,CommandText,Transaction where reset
                this.DisconnectFromDataReaderAndConnection (); 
                _parameterCollection = null;
                CommandText = null; 
            } 
            _cmdWrapper = null;                         // let go of the CommandWrapper
            _isPrepared = false; 

            base.Dispose(disposing);    // notify base classes
        }
 
        internal bool Canceling {
            get { 
                return _cmdWrapper.Canceling; 
            }
        } 

        [
        ResCategoryAttribute(Res.DataCategory_Data),
        DefaultValue(""), 
        RefreshProperties(RefreshProperties.All), // MDAC 67707
        ResDescriptionAttribute(Res.DbCommand_CommandText), 
        Editor("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing) 
        ]
        override public string CommandText { 
            get {
                string value = _commandText;
                return ((null != value) ? value : ADP.StrEmpty);
            } 
            set {
                if (Bid.TraceOn) { 
                    Bid.Trace(" %d#, '", ObjectID); 
                    Bid.PutStr(value); // Use PutStr to write out entire string
                    Bid.Trace("'\n"); 
                }
                if (0 != ADP.SrcCompare(_commandText, value)) {
                    PropertyChanging();
                    _commandText = value; 
                }
            } 
        } 

        [ 
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DbCommand_CommandTimeout),
        ]
        override public int CommandTimeout { // V1.2.3300, XXXCommand V1.0.5000 
            get {
                return _commandTimeout; 
            } 
            set {
                Bid.Trace(" %d#, %d\n", ObjectID, value); 
                if (value < 0) {
                    throw ADP.InvalidCommandTimeout(value);
                }
                if (value != _commandTimeout) { 
                    PropertyChanging();
                    _commandTimeout = value; 
                } 
            }
        } 

        public void ResetCommandTimeout() { // V1.2.3300
            if (ADP.DefaultCommandTimeout != _commandTimeout) {
                PropertyChanging(); 
                _commandTimeout = ADP.DefaultCommandTimeout;
            } 
        } 

        private bool ShouldSerializeCommandTimeout() { // V1.2.3300 
            return (ADP.DefaultCommandTimeout != _commandTimeout);
        }

        [ 
        DefaultValue(System.Data.CommandType.Text),
        RefreshProperties(RefreshProperties.All), 
        ResCategoryAttribute(Res.DataCategory_Data), 
        ResDescriptionAttribute(Res.DbCommand_CommandType),
        ] 
        override public CommandType CommandType {
            get {
                CommandType cmdType = _commandType;
                return ((0 != cmdType) ? cmdType : CommandType.Text); 
            }
            set  { 
                switch(value) { // @perfnote: Enum.IsDefined 
                case CommandType.Text:
                case CommandType.StoredProcedure: 
                    PropertyChanging();
                    _commandType = value;
                    break;
                case CommandType.TableDirect: 
                    throw ODBC.NotSupportedCommandType(value);
                default: 
                    throw ADP.InvalidCommandType(value); 
                }
            } 
        }

        // This will establish a relationship between the command and the connection
        [ 
        DefaultValue(null),
        ResCategoryAttribute(Res.DataCategory_Behavior), 
        ResDescriptionAttribute(Res.DbCommand_Connection), 
        Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
        ] 
        new public OdbcConnection Connection {
            get {
                return _connection;
            } 
            set {
                if (value != _connection) { 
                    PropertyChanging(); 
                    this.DisconnectFromDataReaderAndConnection();
                    Debug.Assert(null == _cmdWrapper, "has CMDWrapper when setting connection"); 
                    _connection = value;
                    //OnSchemaChanged();
                }
            } 
        }
 
        override protected DbConnection DbConnection { // V1.2.3300 
            get {
                return Connection; 
            }
            set {
                Connection = (OdbcConnection)value;
            } 
        }
 
        override protected DbParameterCollection DbParameterCollection { // V1.2.3300 
            get {
                return Parameters; 
            }
        }

        override protected DbTransaction DbTransaction { // V1.2.3300 
            get {
                return Transaction; 
            } 
            set {
                Transaction = (OdbcTransaction)value; 
            }
        }

        // @devnote: By default, the cmd object is visible on the design surface (i.e. VS7 Server Tray) 
        // to limit the number of components that clutter the design surface,
        // when the DataAdapter design wizard generates the insert/update/delete commands it will 
        // set the DesignTimeVisible property to false so that cmds won't appear as individual objects 
        [
        DefaultValue(true), 
        DesignOnly(true),
        Browsable(false),
        EditorBrowsableAttribute(EditorBrowsableState.Never),
        ] 
        public override bool DesignTimeVisible { // V1.2.3300, XXXCommand V1.0.5000
            get { 
                return !_designTimeInvisible; 
            }
            set { 
                _designTimeInvisible = !value;
                TypeDescriptor.Refresh(this); // VS7 208845
            }
        } 

        internal bool HasParameters { 
            get { 
                return (null != _parameterCollection);
            } 
        }

        [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DbCommand_Parameters), 
        ] 
        new public OdbcParameterCollection Parameters {
            get { 
                if (null == _parameterCollection) {
                    _parameterCollection = new OdbcParameterCollection();
                }
                return _parameterCollection; 
            }
        } 
 
        [
        Browsable(false), 
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ResDescriptionAttribute(Res.DbCommand_Transaction),
        ]
        new public OdbcTransaction Transaction { 
            get {
                if ((null != _transaction) && (null == _transaction.Connection)) { 
                    _transaction = null;       // Dawn of the Dead 
                }
                return _transaction; 
            }
            set {
                if (_transaction != value) {
                    PropertyChanging(); // fire event before value is validated 
                    _transaction = value;
                } 
            } 
        }
 
        [
        DefaultValue(System.Data.UpdateRowSource.Both),
        ResCategoryAttribute(Res.DataCategory_Update),
        ResDescriptionAttribute(Res.DbCommand_UpdatedRowSource), 
        ]
        override public UpdateRowSource UpdatedRowSource { // V1.2.3300, XXXCommand V1.0.5000 
            get { 
                return _updatedRowSource;
            } 
            set {
                switch(value) { // @perfnote: Enum.IsDefined
                case UpdateRowSource.None:
                case UpdateRowSource.OutputParameters: 
                case UpdateRowSource.FirstReturnedRecord:
                case UpdateRowSource.Both: 
                    _updatedRowSource = value; 
                    break;
                default: 
                    throw ADP.InvalidUpdateRowSource(value);
                }
            }
        } 

        internal OdbcDescriptorHandle GetDescriptorHandle(ODBC32.SQL_ATTR attribute) { 
            return _cmdWrapper.GetDescriptorHandle(attribute); 
        }
 

        // GetStatementHandle
        // ------------------
        // Try to return a cached statement handle. 
        //
        // Creates a CmdWrapper object if necessary 
        // If no handle is available a handle will be allocated. 
        // Bindings will be unbound if a handle is cached and the bindings are invalid.
        // 
        internal CMDWrapper GetStatementHandle () {
            // update the command wrapper object, allocate buffer
            // create reader object
            // 
            if (_cmdWrapper==null) {
                _cmdWrapper = new CMDWrapper(_connection); 
 
                Debug.Assert(null != _connection, "GetStatementHandle without connection?");
                _connection.AddWeakReference(this, OdbcReferenceCollection.CommandTag); 
            }

            if (_cmdWrapper._dataReaderBuf == null) {
                _cmdWrapper._dataReaderBuf = new CNativeBuffer(4096); 
            }
 
            // if there is already a statement handle we need to do some cleanup 
            //
            if (null == _cmdWrapper.StatementHandle) { 
                _isPrepared = false;
                _cmdWrapper.CreateStatementHandle();
            }
            else if ((null != _parameterCollection) && _parameterCollection.RebindCollection) { 
                _cmdWrapper.FreeStatementHandle(ODBC32.STMT.RESET_PARAMS);
            } 
            return _cmdWrapper; 
        }
 
        // OdbcCommand.Cancel()
        //
        // In ODBC3.0 ... a call to SQLCancel when no processing is done has no effect at all
        // (ODBC Programmer's Reference ...) 
        //
 
        override public void Cancel() { 
            CMDWrapper wrapper = _cmdWrapper;
            if (null != wrapper) { 
                wrapper.Canceling = true;
                OdbcStatementHandle stmt = wrapper.StatementHandle;
                if (null != stmt) {
                    lock (stmt) { 
                        // Cancel the statement
                        ODBC32.RetCode retcode = stmt.Cancel(); 
 
                        // copy of StatementErrorHandler, because stmt may become null
                        switch(retcode) { 
                        case ODBC32.RetCode.SUCCESS:
                        case ODBC32.RetCode.SUCCESS_WITH_INFO:
                            // don't fire info message events on cancel
                            break; 
                        default:
                            throw wrapper.Connection.HandleErrorNoThrow(stmt, retcode); 
                        } 
                    }
                } 
            }
        }

 
        object ICloneable.Clone() {
            OdbcCommand clone = new OdbcCommand(); 
            Bid.Trace(" %d#, clone=%d#\n", ObjectID, clone.ObjectID); 
            clone.CommandText = CommandText;
            clone.CommandTimeout = this.CommandTimeout; 
            clone.CommandType = CommandType;
            clone.Connection = this.Connection;
            clone.Transaction = this.Transaction;
            clone.UpdatedRowSource = UpdatedRowSource; 

            if ((null != _parameterCollection) && (0 < Parameters.Count)) { 
                OdbcParameterCollection parameters = clone.Parameters; 
                foreach(ICloneable parameter in Parameters) {
                    parameters.Add(parameter.Clone()); 
                }
            }
            return clone;
        } 

        internal bool RecoverFromConnection() { 
            DisposeDeadDataReader(); 
            return (ConnectionState.Closed == cmdState);
        } 

        private void CloseCommandWrapper() {
            CMDWrapper wrapper = _cmdWrapper;
            if (null != wrapper) { 
                try {
                    wrapper.Dispose(); 
 
                    if (null != _connection) {
                        _connection.RemoveWeakReference(this); 
                    }
                }
                finally {
                    _cmdWrapper = null; 
                }
            } 
        } 

        internal void CloseFromConnection () { 
            if (null != _parameterCollection) {
                _parameterCollection.RebindCollection = true;
            }
            DisposeDataReader(); 
            CloseCommandWrapper();
            _isPrepared = false; 
            _transaction = null; 
        }
 
        internal void CloseFromDataReader() {
            this.weakDataReaderReference = null;
            this.cmdState = ConnectionState.Closed;
        } 

        new public OdbcParameter CreateParameter() { 
            return new OdbcParameter(); 
            }
 
        override protected DbParameter CreateDbParameter() {
            return CreateParameter();
        }
 
        override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) {
            return ExecuteReader(behavior); 
        } 

        override public int ExecuteNonQuery() { 
            OdbcConnection.ExecutePermission.Demand();
            using (OdbcDataReader reader = ExecuteReaderObject(0, ADP.ExecuteNonQuery, false)) {
                reader.Close();
                return reader.RecordsAffected; 
            }
        } 
 
        new public OdbcDataReader ExecuteReader() {
            return ExecuteReader(0/*CommandBehavior*/); 
        }


        new public OdbcDataReader ExecuteReader(CommandBehavior behavior) { 
            OdbcConnection.ExecutePermission.Demand();
            return ExecuteReaderObject(behavior, ADP.ExecuteReader, true); 
        } 

        internal  OdbcDataReader ExecuteReaderFromSQLMethod(object[] methodArguments, 
                                                            ODBC32.SQL_API method){

            return ExecuteReaderObject(CommandBehavior.Default,method.ToString(),true,methodArguments,method);
 
        }
 
        private OdbcDataReader ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader) { // MDAC 68324 

            if ((CommandText == null) || (CommandText.Length == 0)) { 
                throw (ADP.CommandTextRequired(method));
            }
            // using all functions to tell ExecuteReaderObject that
            return ExecuteReaderObject(behavior,method,needReader,null,ODBC32.SQL_API.SQLEXECDIRECT); 
        }
 
        private OdbcDataReader ExecuteReaderObject(CommandBehavior behavior, 
                                                   string method,
                                                   bool needReader, 
                                                   object[] methodArguments,
                                                   ODBC32.SQL_API odbcApiMethod) { // MDAC 68324

            OdbcDataReader localReader = null; 
            try {
                DisposeDeadDataReader();    // this is a no-op if cmdState is not Fetching 
                ValidateConnectionAndTransaction(method);  // cmdState will change to Executing 

                if(0 != (CommandBehavior.SingleRow & behavior)) { 
                    // CommandBehavior.SingleRow implies CommandBehavior.SingleResult
                    behavior |= CommandBehavior.SingleResult;
                }
 
                ODBC32.RetCode retcode;
 
                OdbcStatementHandle stmt = GetStatementHandle().StatementHandle; 
                _cmdWrapper.Canceling = false;
 
                if(null != weakDataReaderReference) {
                    if(weakDataReaderReference.IsAlive) {
                        object target = weakDataReaderReference.Target;
                        if(null != target && weakDataReaderReference.IsAlive) { 
                            if(!((OdbcDataReader)target).IsClosed) {
                                throw ADP.OpenReaderExists(); // MDAC 66411 
                            } 
                        }
                    } 
                }
                localReader = new OdbcDataReader(this, _cmdWrapper, behavior);

                //Set command properties 
                //Not all drivers support timeout. So fail silently if error
                if(!Connection.ProviderInfo.NoQueryTimeout) { 
                    TrySetStatementAttribute(stmt, 
                        ODBC32.SQL_ATTR.QUERY_TIMEOUT,
                        (IntPtr)this.CommandTimeout); 
                }

                // todo: If we remember the state we can omit a lot of SQLSetStmtAttrW calls ...
                // if we do not create a reader we do not even need to do that 
                if(needReader) {
                    if(Connection.IsV3Driver) { 
                        if(!Connection.ProviderInfo.NoSqlSoptSSNoBrowseTable && !Connection.ProviderInfo.NoSqlSoptSSHiddenColumns) { 
                            // Need to get the metadata information
 
                            //SQLServer actually requires browse info turned on ahead of time...
                            //Note: We ignore any failures, since this is SQLServer specific
                            //We won't specialcase for SQL Server but at least for non-V3 drivers
                            if(localReader.IsBehavior(CommandBehavior.KeyInfo)) { 
                                if(!_cmdWrapper._ssKeyInfoModeOn) {
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE, (IntPtr)ODBC32.SQL_NB.ON); 
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS, (IntPtr)ODBC32.SQL_HC.ON); 
                                    _cmdWrapper._ssKeyInfoModeOff = false;
                                    _cmdWrapper._ssKeyInfoModeOn = true; 
                                }
                            }
                            else {
                                if(!_cmdWrapper._ssKeyInfoModeOff) { 
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.NOBROWSETABLE, (IntPtr)ODBC32.SQL_NB.OFF);
                                    TrySetStatementAttribute(stmt, (ODBC32.SQL_ATTR)ODBC32.SQL_SOPT_SS.HIDDEN_COLUMNS, (IntPtr)ODBC32.SQL_HC.OFF); 
                                    _cmdWrapper._ssKeyInfoModeOff = true; 
                                    _cmdWrapper._ssKeyInfoModeOn = false;
                                } 
                            }
                        }
                    }
                } 

                if(localReader.IsBehavior(CommandBehavior.KeyInfo) || 
                    localReader.IsBehavior(CommandBehavior.SchemaOnly)) { 

                    retcode = stmt.Prepare(CommandText); 

                    if(ODBC32.RetCode.SUCCESS != retcode) {
                        _connection.HandleError(stmt, retcode);
                    } 
                }
 
                bool mustRelease = false; 
                CNativeBuffer parameterBuffer = _cmdWrapper._nativeParameterBuffer;
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try {
                    //Handle Parameters
                    //Note: We use the internal variable as to not instante a new object collection, 
                    //for the the common case of using no parameters.
                    if((null != _parameterCollection) && (0 < _parameterCollection.Count)) { 
                        int parameterBufferSize = _parameterCollection.CalcParameterBufferSize(this); 

                        if(null == parameterBuffer || parameterBuffer.Length < parameterBufferSize) { 
                            if (null != parameterBuffer) {
                                parameterBuffer.Dispose();
                            }
                            parameterBuffer = new CNativeBuffer(parameterBufferSize); 
                            _cmdWrapper._nativeParameterBuffer = parameterBuffer;
                        } 
                        else { 
                            parameterBuffer.ZeroMemory();
                        } 

                        parameterBuffer.DangerousAddRef(ref mustRelease);

                        _parameterCollection.Bind(this, _cmdWrapper, parameterBuffer); 
                    }
 
                    if(!localReader.IsBehavior(CommandBehavior.SchemaOnly)) { 

                        // Can't get the KeyInfo after command execution (SQL Server only since it does not support multiple 
                        // results on the same connection). Stored procedures (SP) do not return metadata before actual execution
                        // Need to check the column count since the command type may not be set to SP for a SP.
                        if((localReader.IsBehavior(CommandBehavior.KeyInfo) || localReader.IsBehavior(CommandBehavior.SchemaOnly))
                            && (CommandType != CommandType.StoredProcedure)) { 
                            Int16 cColsAffected;
                            retcode = stmt.NumberOfResultColumns(out cColsAffected); 
                            if(retcode == ODBC32.RetCode.SUCCESS || retcode == ODBC32.RetCode.SUCCESS_WITH_INFO) { 
                                if(cColsAffected > 0) {
                                    localReader.GetSchemaTable(); 
                                }
                            }
                            else if(retcode == ODBC32.RetCode.NO_DATA) {
                                // do nothing 
                            }
                            else { 
                                // any other returncode indicates an error 
                                _connection.HandleError(stmt, retcode);
                            } 
                        }

                        switch(odbcApiMethod) {
                            case ODBC32.SQL_API.SQLEXECDIRECT: 
                                if(localReader.IsBehavior(CommandBehavior.KeyInfo) || _isPrepared) {
                                    //Already prepared, so use SQLExecute 
                                    retcode = stmt.Execute(); 
                                    // Build metadata here
                                    // localReader.GetSchemaTable(); 
                                }
                                else {
#if DEBUG
                                    //if (AdapterSwitches.OleDbTrace.TraceInfo) { 
                                    //    ADP.DebugWriteLine("SQLExecDirectW: " + CommandText);
                                    //} 
#endif 
                                    //SQLExecDirect
                                    retcode = stmt.ExecuteDirect(CommandText); 
                                }
                                break;

                            case ODBC32.SQL_API.SQLTABLES: 
                                retcode = stmt.Tables((string)methodArguments[0],  //TableCatalog
                                    (string)methodArguments[1],  //TableSchema, 
                                    (string)methodArguments[2],  //TableName 
                                    (string)methodArguments[3]); //TableType
                                break; 

                            case ODBC32.SQL_API.SQLCOLUMNS:
                                retcode = stmt.Columns((string)methodArguments[0],  //TableCatalog
                                    (string)methodArguments[1],  //TableSchema 
                                    (string)methodArguments[2],  //TableName
                                    (string)methodArguments[3]); //ColumnName 
                                break; 

                            case ODBC32.SQL_API.SQLPROCEDURES: 
                                retcode = stmt.Procedures((string)methodArguments[0],  //ProcedureCatalog
                                    (string)methodArguments[1],  //ProcedureSchema
                                    (string)methodArguments[2]); //procedureName
                                break; 

                            case ODBC32.SQL_API.SQLPROCEDURECOLUMNS: 
                                retcode = stmt.ProcedureColumns((string)methodArguments[0],  //ProcedureCatalog 
                                    (string)methodArguments[1],  //ProcedureSchema
                                    (string)methodArguments[2],  //procedureName 
                                    (string)methodArguments[3]); //columnName
                                break;

                            case ODBC32.SQL_API.SQLSTATISTICS: 
                                retcode = stmt.Statistics((string)methodArguments[0],  //TableCatalog
                                    (string)methodArguments[1],  //TableSchema 
                                    (string)methodArguments[2],  //TableName 
                                    (Int16)methodArguments[3],   //IndexTrpe
                                    (Int16)methodArguments[4]);  //Accuracy 
                                break;

                            case ODBC32.SQL_API.SQLGETTYPEINFO:
                                retcode = stmt.GetTypeInfo((Int16)methodArguments[0]);  //SQL Type 
                                break;
 
                            default: 
                                // this should NEVER happen
                                Debug.Assert(false, "ExecuteReaderObjectcalled with unsupported ODBC API method."); 
                                throw ADP.InvalidOperation(method.ToString());
                        }

                        //Note: Execute will return NO_DATA for Update/Delete non-row returning queries 
                        if((ODBC32.RetCode.SUCCESS != retcode) && (ODBC32.RetCode.NO_DATA != retcode)) {
                            _connection.HandleError(stmt, retcode); 
                        } 
                    } // end SchemaOnly
                } 
                finally {
                    if(mustRelease) {
                        parameterBuffer.DangerousRelease();
                    } 
                }
 
                this.weakDataReaderReference = new WeakReference(localReader); 

                // XXXCommand.Execute should position reader on first row returning result 
                // any exceptions in the initial non-row returning results should be thrown
                // from from ExecuteXXX not the DataReader
                if(!localReader.IsBehavior(CommandBehavior.SchemaOnly)) {
                    localReader.FirstResult(); 
                }
                cmdState = ConnectionState.Fetching; 
            } 
            finally {
                if(ConnectionState.Fetching != cmdState) { 
                    if(null != localReader) {
                        // clear bindings so we don't grab output parameters on a failed execute
                        if(null != _parameterCollection) {
                            _parameterCollection.ClearBindings(); 
                        }
                        ((IDisposable)localReader).Dispose(); 
                    } 
                    if(ConnectionState.Closed != cmdState) {
                        cmdState = ConnectionState.Closed; 
                    }
                }
            }
            return localReader; 
        }
 
        override public object ExecuteScalar() { 
            OdbcConnection.ExecutePermission.Demand();
 
            object value = null;
            using(IDataReader reader = ExecuteReaderObject(0, ADP.ExecuteScalar, false)) {
                if (reader.Read() && (0 < reader.FieldCount)) {
                    value = reader.GetValue(0); 
                }
                reader.Close(); 
            } 
            return value;
        } 

        internal string GetDiagSqlState() {
            return _cmdWrapper.GetDiagSqlState();
        } 

        private void PropertyChanging() { 
            _isPrepared = false; 
        }
 
        // Prepare
        //
        // if the CommandType property is set to TableDirect Prepare does nothing.
        // if the CommandType property is set to StoredProcedure Prepare should succeed but result 
        // in a no-op
        // 
        // throw InvalidOperationException 
        // if the connection is not set
        // if the connection is not open 
        //
        override public void Prepare() {
            OdbcConnection.ExecutePermission.Demand();
            ODBC32.RetCode retcode; 

            ValidateOpenConnection(ADP.Prepare); 
 
            if (0 != (ConnectionState.Fetching & _connection.InternalState)) {
                throw ADP.OpenReaderExists(); 
            }

            if (CommandType == CommandType.TableDirect) {
                return; // do nothing 
            }
 
            DisposeDeadDataReader(); 
            GetStatementHandle();
 
            OdbcStatementHandle stmt = _cmdWrapper.StatementHandle;

            retcode = stmt.Prepare(CommandText);
 

            if (ODBC32.RetCode.SUCCESS != retcode) { 
                _connection.HandleError(stmt, retcode); 
            }
            _isPrepared = true; 
        }


 
        void TrySetStatementAttribute (OdbcStatementHandle stmt, ODBC32.SQL_ATTR stmtAttribute, IntPtr value) {
 
            ODBC32.RetCode retcode = stmt.SetStatementAttribute( 
                stmtAttribute,
                value, 
                ODBC32.SQL_IS.UINTEGER);

            if (retcode == ODBC32.RetCode.ERROR) {
 
                string sqlState;
                stmt.GetDiagnosticField(out sqlState); 
 
                if ((sqlState == "HYC00") || (sqlState == "HY092")) {
                    Connection.FlagUnsupportedStmtAttr(stmtAttribute); 
                }
                else {
                    // now what? Should we throw?
                } 
            }
        } 
 
        private void ValidateOpenConnection(string methodName) {
            // see if we have a connection 
            OdbcConnection connection = Connection;

            if (null == connection) {
                throw ADP.ConnectionRequired(methodName); 
            }
 
            // must have an open and available connection 
            ConnectionState state = connection.State;
 
            if (ConnectionState.Open != state) {
                throw ADP.OpenConnectionRequired(methodName, state);
            }
        } 

        private void ValidateConnectionAndTransaction(string method) { 
            if (null == _connection) { 
                throw ADP.ConnectionRequired(method);
            } 
            _transaction = _connection.SetStateExecuting(method, Transaction);
            cmdState = ConnectionState.Executing;
        }
 
    }
    sealed internal class CMDWrapper { 
 
        private OdbcStatementHandle _stmt;                  // hStmt
        private OdbcStatementHandle _keyinfostmt;           // hStmt for keyinfo 

        internal OdbcDescriptorHandle  _hdesc;              // hDesc

        internal CNativeBuffer _nativeParameterBuffer;      // Native memory for internal memory management 
        // (Performance optimization)
 
        internal CNativeBuffer      _dataReaderBuf;         // Reusable DataReader buffer 

        private readonly OdbcConnection _connection;        // Connection 
        private bool                _canceling;             // true if the command is canceling
        internal bool               _hasBoundColumns;
        internal bool               _ssKeyInfoModeOn;       // tells us if the SqlServer specific options are on
        internal bool               _ssKeyInfoModeOff;      // a tri-state value would be much better ... 

        internal CMDWrapper (OdbcConnection connection) { 
            _connection = connection; 
        }
 
        internal bool Canceling {
            get {
                return _canceling;
            } 
            set {
                _canceling = value; 
            } 
        }
 
        internal OdbcConnection Connection {
            get {
                return _connection;
            } 
        }
 
        internal bool HasBoundColumns { 
//            get {
//                return _hasBoundColumns; 
//            }
            set {
                _hasBoundColumns = value;
            } 
        }
 
        internal OdbcStatementHandle StatementHandle { 
            get { return _stmt; }
        } 

        internal OdbcStatementHandle KeyInfoStatement {
            get {
                return _keyinfostmt; 
            }
        } 
 
        internal void CreateKeyInfoStatementHandle() {
            DisposeKeyInfoStatementHandle(); 
            _keyinfostmt =  _connection.CreateStatementHandle();
        }

        internal void CreateStatementHandle() { 
            DisposeStatementHandle();
            _stmt =  _connection.CreateStatementHandle(); 
        } 

        internal void Dispose() { 
            if (null != _dataReaderBuf) {
                _dataReaderBuf.Dispose();
                _dataReaderBuf = null;
            } 
            DisposeStatementHandle();
 
            CNativeBuffer buffer = _nativeParameterBuffer; 
            _nativeParameterBuffer = null;
            if (null != buffer) { 
                buffer.Dispose();
            }
            _ssKeyInfoModeOn = false;
            _ssKeyInfoModeOff = false; 
        }
 
        private void DisposeDescriptorHandle() { 
            OdbcDescriptorHandle handle = _hdesc;
            if (null != handle) { 
                _hdesc = null;
                handle.Dispose();
            }
        } 
        internal void DisposeStatementHandle() {
            DisposeKeyInfoStatementHandle(); 
            DisposeDescriptorHandle(); 

            OdbcStatementHandle handle = _stmt; 
            if (null != handle) {
                _stmt = null;
                handle.Dispose();
            } 
        }
 
        internal void DisposeKeyInfoStatementHandle() { 
            OdbcStatementHandle handle = _keyinfostmt;
            if (null != handle) { 
                _keyinfostmt = null;
                handle.Dispose();
            }
        } 

        internal void FreeStatementHandle(ODBC32.STMT stmt) { 
            DisposeDescriptorHandle(); 

            OdbcStatementHandle handle = _stmt; 
            if (null != handle) {
                try {
                    ODBC32.RetCode retcode;
                    retcode = handle.FreeStatement(stmt); 
                    StatementErrorHandler(retcode);
                } 
                catch (Exception e) { 
                    //
                    if (ADP.IsCatchableExceptionType(e)) { 
                        _stmt = null;
                        handle.Dispose();
                    }
 
                    throw;
                } 
            } 
        }
 
        internal void FreeKeyInfoStatementHandle(ODBC32.STMT stmt) {
            OdbcStatementHandle handle = _keyinfostmt;
            if (null != handle) {
                try { 
                    handle.FreeStatement(stmt);
                } 
                catch (Exception e) { 
                    //
                    if (ADP.IsCatchableExceptionType(e)) { 
                        _keyinfostmt = null;
                        handle.Dispose();
                    }
 
                    throw;
                } 
            } 
        }
 
        // Get the Descriptor Handle for the current statement
        //
        internal OdbcDescriptorHandle GetDescriptorHandle(ODBC32.SQL_ATTR attribute) {
            OdbcDescriptorHandle hdesc = _hdesc; 
            if (null == _hdesc) {
                _hdesc = hdesc = new OdbcDescriptorHandle(_stmt, attribute); 
            } 
            return hdesc;
        } 

        internal string GetDiagSqlState () {
            string sqlstate;
            _stmt.GetDiagnosticField(out sqlstate); 
            return sqlstate;
        } 
 
        internal void StatementErrorHandler(ODBC32.RetCode retcode) {
            switch(retcode) { 
            case ODBC32.RetCode.SUCCESS:
            case ODBC32.RetCode.SUCCESS_WITH_INFO:
                _connection.HandleErrorNoThrow(_stmt, retcode);
                break; 
            default:
                throw _connection.HandleErrorNoThrow(_stmt, retcode); 
            } 
        }
 
        internal void UnbindStmtColumns() {
            if (_hasBoundColumns) {
                FreeStatementHandle(ODBC32.STMT.UNBIND);
                _hasBoundColumns = false; 
            }
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK