OdbcCommand.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Data / System / Data / Odbc / OdbcCommand.cs / 1305376 / 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)
    ] 
    public 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