//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // Microsoft // Microsoft //------------------------------------------------------------------------------ namespace System.Data.OleDb { using System; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Threading; using System.Text; [ DefaultEvent("RecordsAffected"), ToolboxItem(true), Designer("Microsoft.VSDesigner.Data.VS.OleDbCommandDesigner, " + AssemblyRef.MicrosoftVSDesigner) ] public sealed class OleDbCommand : DbCommand, ICloneable, IDbCommand { // command data private string _commandText; private CommandType _commandType; private int _commandTimeout = ADP.DefaultCommandTimeout; private UpdateRowSource _updatedRowSource = UpdateRowSource.Both; private bool _designTimeInvisible; private OleDbConnection _connection; private OleDbTransaction _transaction; private static int _objectTypeCount; // Bid counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private OleDbParameterCollection _parameters; // native information private UnsafeNativeMethods.ICommandText _icommandText; // if executing with a different CommandBehavior.KeyInfo behavior // original ICommandText must be released and a new ICommandText generated private CommandBehavior commandBehavior; private Bindings _dbBindings; internal bool canceling; // MDAC 68964 private bool _isPrepared; private bool _executeQuery; private bool _trackingForClose; private bool _hasDataReader; private IntPtr _recordsAffected; private int _changeID; private int _lastChangeID; public OleDbCommand() : base() { GC.SuppressFinalize(this); } public OleDbCommand(string cmdText) : this() { CommandText = cmdText; } public OleDbCommand(string cmdText, OleDbConnection connection) : this() { CommandText = cmdText; Connection = connection; } public OleDbCommand(string cmdText, OleDbConnection connection, OleDbTransaction transaction) : this() { CommandText = cmdText; Connection = connection; Transaction = transaction; } private OleDbCommand(OleDbCommand from) : this() { // Clone CommandText = from.CommandText; CommandTimeout = from.CommandTimeout; CommandType = from.CommandType; Connection = from.Connection; DesignTimeVisible = from.DesignTimeVisible; UpdatedRowSource = from.UpdatedRowSource; Transaction = from.Transaction; OleDbParameterCollection parameters = Parameters; foreach(object parameter in from.Parameters) { parameters.Add((parameter is ICloneable) ? (parameter as ICloneable).Clone() : parameter); } } private Bindings ParameterBindings { get { return _dbBindings; } set { Bindings bindings = _dbBindings; _dbBindings = value; if ((null != bindings) && (value != bindings)) { bindings.Dispose(); } } } [ DefaultValue(""), Editor("Microsoft.VSDesigner.Data.ADO.Design.OleDbCommandTextEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), RefreshProperties(RefreshProperties.All), // MDAC 67707 ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbCommand_CommandText), ] 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: case CommandType.TableDirect: PropertyChanging(); _commandType = value; break; default: throw ADP.InvalidCommandType(value); } } } [ DefaultValue(null), Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbCommand_Connection), ] new public OleDbConnection Connection { get { return _connection; } set { OleDbConnection connection = _connection; if (value != connection) { PropertyChanging(); ResetConnection(); _connection = value; Bid.Trace(" %d#\n", ObjectID); if (null != value) { _transaction = OleDbTransaction.TransactionUpdate(_transaction); // MDAC 63226 } } } } private void ResetConnection() { OleDbConnection connection = _connection; if (null != connection) { PropertyChanging(); CloseInternal(); if (_trackingForClose) { connection.RemoveWeakReference(this); _trackingForClose = false; } } _connection = null; } override protected DbConnection DbConnection { // V1.2.3300 get { return Connection; } set { Connection = (OleDbConnection)value; } } override protected DbParameterCollection DbParameterCollection { // V1.2.3300 get { return Parameters; } } override protected DbTransaction DbTransaction { // V1.2.3300 get { return Transaction; } set { Transaction = (OleDbTransaction)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 } } [ DesignerSerializationVisibility(DesignerSerializationVisibility.Content), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbCommand_Parameters), ] new public OleDbParameterCollection Parameters { get { OleDbParameterCollection value = _parameters; if (null == value) { // delay the creation of the OleDbParameterCollection // until user actually uses the Parameters property value = new OleDbParameterCollection(); _parameters = value; } return value; } } private bool HasParameters() { // MDAC 65548 OleDbParameterCollection value = _parameters; return (null != value) && (0 < value.Count); // VS 300569 } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(Res.DbCommand_Transaction), ] new public OleDbTransaction Transaction { get { // find the last non-zombied local transaction object, but not transactions // that may have been started after the current local transaction OleDbTransaction transaction = _transaction; while ((null != transaction) && (null == transaction.Connection)) { transaction = transaction.Parent; _transaction = transaction; } return transaction; } set { _transaction = value; Bid.Trace(" %d#\n", ObjectID); } } [ 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); } } } // required interface, safe cast private UnsafeNativeMethods.IAccessor IAccessor() { Bid.Trace(" %d#, IAccessor\n", ObjectID); Debug.Assert(null != _icommandText, "IAccessor: null ICommandText"); return (UnsafeNativeMethods.IAccessor) _icommandText; } // required interface, safe cast internal UnsafeNativeMethods.ICommandProperties ICommandProperties() { Bid.Trace(" %d#, ICommandProperties\n", ObjectID); Debug.Assert(null != _icommandText, "ICommandProperties: null ICommandText"); return (UnsafeNativeMethods.ICommandProperties) _icommandText; } // optional interface, unsafe cast private UnsafeNativeMethods.ICommandPrepare ICommandPrepare() { Bid.Trace(" %d#, ICommandPrepare\n", ObjectID); Debug.Assert(null != _icommandText, "ICommandPrepare: null ICommandText"); return (_icommandText as UnsafeNativeMethods.ICommandPrepare); } // optional interface, unsafe cast private UnsafeNativeMethods.ICommandWithParameters ICommandWithParameters() { Bid.Trace(" %d#, ICommandWithParameters\n", ObjectID); Debug.Assert(null != _icommandText, "ICommandWithParameters: null ICommandText"); UnsafeNativeMethods.ICommandWithParameters value = (_icommandText as UnsafeNativeMethods.ICommandWithParameters); if (null == value) { throw ODB.NoProviderSupportForParameters(_connection.Provider, (Exception)null); } return value; } private void CreateAccessor() { Debug.Assert(System.Data.CommandType.Text == CommandType || System.Data.CommandType.StoredProcedure == CommandType, "CreateAccessor: incorrect CommandType"); Debug.Assert(null == _dbBindings, "CreateAccessor: already has dbBindings"); Debug.Assert(HasParameters(), "CreateAccessor: unexpected, no parameter collection"); // do this first in-case the command doesn't support parameters UnsafeNativeMethods.ICommandWithParameters commandWithParameters = ICommandWithParameters(); OleDbParameterCollection collection = _parameters; OleDbParameter[] parameters = new OleDbParameter[collection.Count]; collection.CopyTo(parameters, 0); // _dbBindings is used as a switch during ExecuteCommand, so don't set it until everything okay Bindings bindings = new Bindings(parameters, collection.ChangeID); for (int i = 0; i < parameters.Length; ++i) { bindings.ForceRebind |= parameters[i].BindParameter(i, bindings); } bindings.AllocateForAccessor(null, 0, 0); ApplyParameterBindings(commandWithParameters, bindings.BindInfo); UnsafeNativeMethods.IAccessor iaccessor = IAccessor(); OleDbHResult hr = bindings.CreateAccessor(iaccessor, ODB.DBACCESSOR_PARAMETERDATA); if (hr < 0) { ProcessResults(hr); } _dbBindings = bindings; } private void ApplyParameterBindings(UnsafeNativeMethods.ICommandWithParameters commandWithParameters, tagDBPARAMBINDINFO[] bindInfo) { IntPtr[] ordinals = new IntPtr[bindInfo.Length]; for (int i = 0; i < ordinals.Length; ++i) { ordinals[i] = (IntPtr)(i+1); } Bid.Trace(" %d#\n", ObjectID); OleDbHResult hr = commandWithParameters.SetParameterInfo((IntPtr)bindInfo.Length, ordinals, bindInfo); Bid.Trace(" %08X{HRESULT}\n", hr); if (hr < 0) { ProcessResults(hr); } } override public void Cancel() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#\n", ObjectID); try { unchecked { _changeID++; } UnsafeNativeMethods.ICommandText icmdtxt = _icommandText; if (null != icmdtxt) { OleDbHResult hr = OleDbHResult.S_OK; lock(icmdtxt) { // lock the object to avoid race conditions between using the object and releasing the object // after we acquire the lock, if the class has moved on don't actually call Cancel if (icmdtxt == _icommandText) { Bid.Trace(" %d#\n", ObjectID); hr = icmdtxt.Cancel(); Bid.Trace(" %08X{HRESULT}\n", hr); } } if (OleDbHResult.DB_E_CANTCANCEL != hr) { // if the provider can't cancel the command - don't cancel the DataReader this.canceling = true; } // since cancel is allowed to occur at anytime we can't check the connection status // since if it returns as closed then the connection will close causing the reader to close // and that would introduce the possilbility of one thread reading and one thread closing at the same time ProcessResultsNoReset(hr); // MDAC 72667 } else { this.canceling = true; } } finally { Bid.ScopeLeave(ref hscp); } } public OleDbCommand Clone() { OleDbCommand clone = new OleDbCommand(this); Bid.Trace(" %d#, clone=%d#\n", ObjectID, clone.ObjectID); return clone; } object ICloneable.Clone() { return Clone(); } // Connection.Close & Connection.Dispose(true) notification internal void CloseCommandFromConnection(bool canceling) { this.canceling = canceling; // MDAC 71435 CloseInternal(); _trackingForClose = false; _transaction = null; //GC.SuppressFinalize(this); } internal void CloseInternal() { Debug.Assert(null != _connection, "no connection, CloseInternal"); CloseInternalParameters(); CloseInternalCommand(); } // may be called from either // OleDbDataReader.Close/Dispose // via OleDbCommand.Dispose or OleDbConnection.Close internal void CloseFromDataReader(Bindings bindings) { if (null != bindings) { if (canceling) { bindings.Dispose(); Debug.Assert(_dbBindings == bindings, "bindings with two owners"); } else { bindings.ApplyOutputParameters(); ParameterBindings = bindings; } } _hasDataReader = false; } private void CloseInternalCommand() { unchecked { _changeID++; } this.commandBehavior = CommandBehavior.Default; _isPrepared = false; UnsafeNativeMethods.ICommandText ict = Interlocked.Exchange(ref _icommandText, null); if (null != ict) { lock(ict) { // lock the object to avoid race conditions between using the object and releasing the object Marshal.ReleaseComObject(ict); } } } private void CloseInternalParameters() { Debug.Assert(null != _connection, "no connection, CloseInternalParameters"); Bindings bindings = _dbBindings; _dbBindings = null; if (null != bindings) { bindings.Dispose(); } } new public OleDbParameter CreateParameter() { return new OleDbParameter(); } override protected DbParameter CreateDbParameter() { return CreateParameter(); } override protected void Dispose(bool disposing) { // MDAC 65459 if (disposing) { // release mananged objects // the DataReader takes ownership of the parameter Bindings // this way they don't get destroyed when user calls OleDbCommand.Dispose // when there is an open DataReader unchecked { _changeID++; } // in V1.0, V1.1 the Connection,Parameters,CommandText,Transaction where reset ResetConnection(); _transaction = null; _parameters = null; CommandText = null; } // release unmanaged objects base.Dispose(disposing); // notify base classes } new public OleDbDataReader ExecuteReader() { return ExecuteReader(CommandBehavior.Default); } IDataReader IDbCommand.ExecuteReader() { return ExecuteReader(CommandBehavior.Default); } new public OleDbDataReader ExecuteReader(CommandBehavior behavior) { OleDbConnection.ExecutePermission.Demand(); IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, behavior=%d{ds.CommandBehavior}\n", ObjectID, (int)behavior); try { _executeQuery = true; return ExecuteReaderInternal(behavior, ADP.ExecuteReader); } finally { Bid.ScopeLeave(ref hscp); } } IDataReader IDbCommand.ExecuteReader(CommandBehavior behavior) { return ExecuteReader(behavior); } override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { return ExecuteReader(behavior); } private OleDbDataReader ExecuteReaderInternal(CommandBehavior behavior, string method) { OleDbDataReader dataReader = null; OleDbException nextResultsFailure = null; int state = ODB.InternalStateClosed; try { ValidateConnectionAndTransaction(method); if (0 != (CommandBehavior.SingleRow & behavior)) { // CommandBehavior.SingleRow implies CommandBehavior.SingleResult behavior |= CommandBehavior.SingleResult; } object executeResult; int resultType; switch(CommandType) { case 0: // uninitialized CommandType.Text case CommandType.Text: case CommandType.StoredProcedure: resultType = ExecuteCommand(behavior, out executeResult); break; case CommandType.TableDirect: resultType = ExecuteTableDirect(behavior, out executeResult); break; default: throw ADP.InvalidCommandType(CommandType); } if (_executeQuery) { try { dataReader = new OleDbDataReader(_connection, this, 0, this.commandBehavior); switch(resultType) { case ODB.ExecutedIMultipleResults: dataReader.InitializeIMultipleResults(executeResult); dataReader.NextResult(); break; case ODB.ExecutedIRowset: dataReader.InitializeIRowset(executeResult, ChapterHandle.DB_NULL_HCHAPTER, _recordsAffected); dataReader.BuildMetaInfo(); dataReader.HasRowsRead(); break; case ODB.ExecutedIRow: dataReader.InitializeIRow(executeResult, _recordsAffected); dataReader.BuildMetaInfo(); break; case ODB.PrepareICommandText: if (!_isPrepared) { PrepareCommandText(2); } OleDbDataReader.GenerateSchemaTable(dataReader, _icommandText, behavior); break; default: Debug.Assert(false, "ExecuteReaderInternal: unknown result type"); break; } executeResult = null; _hasDataReader = true; _connection.AddWeakReference(dataReader, OleDbReferenceCollection.DataReaderTag); // command stays in the executing state until the connection // has a datareader to track for it being closed state = ODB.InternalStateOpen; // MDAC 72655 } finally { if (ODB.InternalStateOpen != state) { this.canceling = true; if (null != dataReader) { ((IDisposable) dataReader).Dispose(); dataReader = null; } } } Debug.Assert(null != dataReader, "ExecuteReader should never return a null DataReader"); } else { // optimized code path for ExecuteNonQuery to not create a OleDbDataReader object try { if (ODB.ExecutedIMultipleResults == resultType) { UnsafeNativeMethods.IMultipleResults multipleResults = (UnsafeNativeMethods.IMultipleResults) executeResult; // may cause a Connection.ResetState which closes connection nextResultsFailure = OleDbDataReader.NextResults(multipleResults, _connection, this, out _recordsAffected); } } finally { try { if (null != executeResult) { Marshal.ReleaseComObject(executeResult); executeResult = null; } CloseFromDataReader(ParameterBindings); } catch(Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } if (null != nextResultsFailure) { nextResultsFailure = new OleDbException(nextResultsFailure, e); } else { throw; } } } } } finally { // finally clear executing state try { if ((null == dataReader) && (ODB.InternalStateOpen != state)) { // MDAC 67218 ParameterCleanup(); } } catch(Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } if (null != nextResultsFailure) { nextResultsFailure = new OleDbException(nextResultsFailure, e); } else { throw; } } if (null != nextResultsFailure) { throw nextResultsFailure; } } return dataReader; } private int ExecuteCommand(CommandBehavior behavior, out object executeResult) { if (InitializeCommand(behavior, false)) { if (0 != (CommandBehavior.SchemaOnly & this.commandBehavior)) { executeResult = null; return ODB.PrepareICommandText; } return ExecuteCommandText(out executeResult); } return ExecuteTableDirect(behavior, out executeResult); // MDAC 57856 } // dbindings handle can't be freed until the output parameters // have been filled in which occurs after the last rowset is released // dbbindings.FreeDataHandle occurs in Cloe private int ExecuteCommandText(out object executeResult) { int retcode; tagDBPARAMS dbParams = null; RowBinding rowbinding = null; Bindings bindings = ParameterBindings; bool mustRelease = false; RuntimeHelpers.PrepareConstrainedRegions(); try { if (null != bindings) { // parameters may be suppressed rowbinding = bindings.RowBinding(); rowbinding.DangerousAddRef(ref mustRelease); // bindings can't be released until after last rowset is released // that is when output parameters are populated // initialize the input parameters to the input databuffer bindings.ApplyInputParameters(); dbParams = new tagDBPARAMS(); dbParams.pData = rowbinding.DangerousGetDataPtr(); dbParams.cParamSets = 1; dbParams.hAccessor = rowbinding.DangerousGetAccessorHandle(); } if ((0 == (CommandBehavior.SingleResult & this.commandBehavior)) && _connection.SupportMultipleResults()) { retcode = ExecuteCommandTextForMultpleResults(dbParams, out executeResult); } else if (0 == (CommandBehavior.SingleRow & this.commandBehavior) || !_executeQuery) { retcode = ExecuteCommandTextForSingleResult(dbParams, out executeResult); } else { retcode = ExecuteCommandTextForSingleRow(dbParams, out executeResult); } } finally { if (mustRelease) { rowbinding.DangerousRelease(); } } return retcode; } private int ExecuteCommandTextForMultpleResults(tagDBPARAMS dbParams, out object executeResult) { Debug.Assert(0 == (CommandBehavior.SingleRow & this.commandBehavior), "SingleRow implies SingleResult"); OleDbHResult hr; Bid.Trace(" %d#, IID_IMultipleResults\n", ObjectID); hr = _icommandText.Execute(ADP.PtrZero, ref ODB.IID_IMultipleResults, dbParams, out _recordsAffected, out executeResult); Bid.Trace(" %08X{HRESULT}, RecordsAffected=%Id\n", hr, _recordsAffected); if (OleDbHResult.E_NOINTERFACE != hr) { ExecuteCommandTextErrorHandling(hr); return ODB.ExecutedIMultipleResults; } SafeNativeMethods.Wrapper.ClearErrorInfo(); return ExecuteCommandTextForSingleResult(dbParams, out executeResult); } private int ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, out object executeResult) { OleDbHResult hr; // MDAC 64465 (Microsoft.Jet.OLEDB.4.0 returns 0 for recordsAffected instead of -1) if (_executeQuery) { Bid.Trace(" %d#, IID_IRowset\n", ObjectID); hr = _icommandText.Execute(ADP.PtrZero, ref ODB.IID_IRowset, dbParams, out _recordsAffected, out executeResult); Bid.Trace(" %08X{HRESULT}, RecordsAffected=%Id\n", hr, _recordsAffected); } else { Bid.Trace(" %d#, IID_NULL\n", ObjectID); hr = _icommandText.Execute(ADP.PtrZero, ref ODB.IID_NULL, dbParams, out _recordsAffected, out executeResult); Bid.Trace(" %08X{HRESULT}, RecordsAffected=%Id\n", hr, _recordsAffected); } ExecuteCommandTextErrorHandling(hr); return ODB.ExecutedIRowset; } private int ExecuteCommandTextForSingleRow(tagDBPARAMS dbParams, out object executeResult) { Debug.Assert(_executeQuery, "ExecuteNonQuery should always use ExecuteCommandTextForSingleResult"); if (_connection.SupportIRow(this)) { OleDbHResult hr; Bid.Trace(" %d#, IID_IRow\n", ObjectID); hr = _icommandText.Execute(ADP.PtrZero, ref ODB.IID_IRow, dbParams, out _recordsAffected, out executeResult); Bid.Trace(" %08X{HRESULT}, RecordsAffected=%Id\n", hr, _recordsAffected); if (OleDbHResult.DB_E_NOTFOUND == hr) { // MDAC 76110 SafeNativeMethods.Wrapper.ClearErrorInfo(); return ODB.ExecutedIRow; } else if (OleDbHResult.E_NOINTERFACE != hr) { ExecuteCommandTextErrorHandling(hr); return ODB.ExecutedIRow; } } SafeNativeMethods.Wrapper.ClearErrorInfo(); return ExecuteCommandTextForSingleResult(dbParams, out executeResult); } private void ExecuteCommandTextErrorHandling(OleDbHResult hr) { Exception e = OleDbConnection.ProcessResults(hr, _connection, this); if (null != e) { e = ExecuteCommandTextSpecialErrorHandling(hr, e); throw e; } } private Exception ExecuteCommandTextSpecialErrorHandling(OleDbHResult hr, Exception e) { if (((OleDbHResult.DB_E_ERRORSOCCURRED == hr) || (OleDbHResult.DB_E_BADBINDINFO == hr)) && (null != _dbBindings)) { // MDAC 66026, 67039 // // this code exist to try for a better user error message by post-morten detection // of invalid parameter types being passed to a provider that doesn't understand // the user specified parameter OleDbType Debug.Assert(null != e, "missing inner exception"); StringBuilder builder = new StringBuilder(); ParameterBindings.ParameterStatus(builder); e = ODB.CommandParameterStatus(builder.ToString(), e); } return e; } override public int ExecuteNonQuery() { OleDbConnection.ExecutePermission.Demand(); IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#\n", ObjectID); try { _executeQuery = false; ExecuteReaderInternal(CommandBehavior.Default, ADP.ExecuteNonQuery); return ADP.IntPtrToInt32(_recordsAffected); } finally { Bid.ScopeLeave(ref hscp); } } override public object ExecuteScalar() { OleDbConnection.ExecutePermission.Demand(); IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#\n", ObjectID); try { object value = null; _executeQuery = true; using(OleDbDataReader reader = ExecuteReaderInternal(CommandBehavior.Default, ADP.ExecuteScalar)) { if (reader.Read() && (0 < reader.FieldCount)) { value = reader.GetValue(0); } } return value; } finally { Bid.ScopeLeave(ref hscp); } } private int ExecuteTableDirect(CommandBehavior behavior, out object executeResult) { this.commandBehavior = behavior; executeResult = null; OleDbHResult hr = OleDbHResult.S_OK; StringMemHandle sptr = null; bool mustReleaseStringHandle = false; RuntimeHelpers.PrepareConstrainedRegions(); try { sptr = new StringMemHandle(ExpandCommandText()); sptr.DangerousAddRef(ref mustReleaseStringHandle); if (mustReleaseStringHandle) { tagDBID tableID = new tagDBID(); tableID.uGuid = Guid.Empty; tableID.eKind = ODB.DBKIND_NAME; tableID.ulPropid = sptr.DangerousGetHandle(); using(IOpenRowsetWrapper iopenRowset = _connection.IOpenRowset()) { using(DBPropSet propSet = CommandPropertySets()) { if (null != propSet) { // MDAC 65279 Bid.Trace(" %d#, IID_IRowset\n", ObjectID); bool mustRelease = false; RuntimeHelpers.PrepareConstrainedRegions(); try { propSet.DangerousAddRef(ref mustRelease); hr = iopenRowset.Value.OpenRowset(ADP.PtrZero, tableID, ADP.PtrZero, ref ODB.IID_IRowset, propSet.PropertySetCount, propSet.DangerousGetHandle(), out executeResult); } finally { if (mustRelease) { propSet.DangerousRelease(); } } Bid.Trace(" %08X{HRESULT}", hr); if (OleDbHResult.DB_E_ERRORSOCCURRED == hr) { Bid.Trace(" %d#, IID_IRowset\n", ObjectID); hr = iopenRowset.Value.OpenRowset(ADP.PtrZero, tableID, ADP.PtrZero, ref ODB.IID_IRowset, 0, IntPtr.Zero, out executeResult); Bid.Trace(" %08X{HRESULT}", hr); } } else { Bid.Trace(" %d#, IID_IRowset\n", ObjectID); hr = iopenRowset.Value.OpenRowset(ADP.PtrZero, tableID, ADP.PtrZero, ref ODB.IID_IRowset, 0, IntPtr.Zero, out executeResult); Bid.Trace(" %08X{HRESULT}", hr); } } } } } finally { if (mustReleaseStringHandle) { sptr.DangerousRelease(); } } ProcessResults(hr); _recordsAffected = ADP.RecordsUnaffected; return ODB.ExecutedIRowset; } private string ExpandCommandText() { string cmdtxt = CommandText; if (ADP.IsEmpty(cmdtxt)) { return ADP.StrEmpty; } CommandType cmdtype = CommandType; switch(cmdtype) { case System.Data.CommandType.Text: // do nothing, already expanded by user return cmdtxt; case System.Data.CommandType.StoredProcedure: // { ? = CALL SPROC (? ?) }, { ? = CALL SPROC }, { CALL SPRC (? ?) }, { CALL SPROC } return ExpandStoredProcedureToText(cmdtxt); case System.Data.CommandType.TableDirect: // @devnote: Provider=Jolt4.0 doesn't like quoted table names, SQOLEDB requires them // Providers should not require table names to be quoted and should guarantee that // unquoted table names correctly open the specified table, even if the table name // contains special characters, as long as the table can be unambiguously identified // without quoting. return cmdtxt; default: throw ADP.InvalidCommandType(cmdtype); } } private string ExpandOdbcMaximumToText(string sproctext, int parameterCount) { StringBuilder builder = new StringBuilder(); if ((0 < parameterCount) && (ParameterDirection.ReturnValue == Parameters[0].Direction)) { parameterCount--; builder.Append("{ ? = CALL "); } else { builder.Append("{ CALL "); } builder.Append(sproctext); // WebData 95634 switch(parameterCount) { case 0: builder.Append(" }"); break; case 1: builder.Append("( ? ) }"); break; default: builder.Append("( ?, ?"); for (int i = 2; i < parameterCount; ++i) { builder.Append(", ?"); } builder.Append(" ) }"); break; } return builder.ToString(); } private string ExpandOdbcMinimumToText(string sproctext, int parameterCount) { //if ((0 < parameterCount) && (ParameterDirection.ReturnValue == Parameters[0].Direction)) { // Debug.Assert("doesn't support ReturnValue parameters"); //} StringBuilder builder = new StringBuilder(); builder.Append("exec "); builder.Append(sproctext); if (0 < parameterCount) { builder.Append(" ?"); for(int i = 1; i < parameterCount; ++i) { builder.Append(", ?"); } } return builder.ToString(); } private string ExpandStoredProcedureToText(string sproctext) { Debug.Assert(null != _connection, "ExpandStoredProcedureToText: null Connection"); int parameterCount = (null != _parameters) ? _parameters.Count : 0; if (0 == (ODB.DBPROPVAL_SQL_ODBC_MINIMUM & _connection.SqlSupport())) { return ExpandOdbcMinimumToText(sproctext, parameterCount); } return ExpandOdbcMaximumToText(sproctext, parameterCount); } private void ParameterCleanup() { Bindings bindings = ParameterBindings; if (null != bindings) { bindings.CleanupBindings(); } } private bool InitializeCommand(CommandBehavior behavior, bool throwifnotsupported) { Debug.Assert(null != _connection, "InitializeCommand: null OleDbConnection"); int changeid = _changeID; if ((0 != (CommandBehavior.KeyInfo & (this.commandBehavior ^ behavior))) || (_lastChangeID != changeid)) { CloseInternalParameters(); // could optimize out CloseInternalCommand(); } this.commandBehavior = behavior; changeid = _changeID; if (!PropertiesOnCommand(false)) { return false; // MDAC 57856 } if ((null != _dbBindings) && _dbBindings.AreParameterBindingsInvalid(_parameters)) { CloseInternalParameters(); } // if we already having bindings - don't create the accessor // if _parameters is null - no parameters exist - don't create the collection // do we actually have parameters since the collection exists if ((null == _dbBindings) && HasParameters()) { // if we setup the parameters before setting cmdtxt then named parameters can happen CreateAccessor(); } if (_lastChangeID != changeid) { OleDbHResult hr; String commandText = ExpandCommandText(); if (Bid.TraceOn) { Bid.Trace(" %d#, DBGUID_DEFAULT, CommandText='", ObjectID); Bid.PutStr(commandText); // Use PutStr to write out entire string Bid.Trace("'\n"); } hr = _icommandText.SetCommandText(ref ODB.DBGUID_DEFAULT, commandText); Bid.Trace(" %08X{HRESULT}\n", hr); if (hr < 0) { ProcessResults(hr); } } _lastChangeID = changeid; return true; } private void PropertyChanging() { unchecked { _changeID++; } } override public void Prepare() { OleDbConnection.ExecutePermission.Demand(); IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#\n", ObjectID); try { if (CommandType.TableDirect != CommandType) { // MDAC 70946, 71194 ValidateConnectionAndTransaction(ADP.Prepare); _isPrepared = false; if (CommandType.TableDirect != CommandType) { InitializeCommand(0, true); PrepareCommandText(1); } } } finally { Bid.ScopeLeave(ref hscp); } } private void PrepareCommandText(int expectedExecutionCount) { OleDbParameterCollection parameters = _parameters; if (null != parameters) { foreach(OleDbParameter parameter in parameters) { if (parameter.IsParameterComputed()) { // @devnote: use IsParameterComputed which is called in the normal case // only to call Prepare to throw the specialized error message // reducing the overall number of methods to actually jit parameter.Prepare(this); // MDAC 70232 } } } UnsafeNativeMethods.ICommandPrepare icommandPrepare = ICommandPrepare(); if (null != icommandPrepare) { OleDbHResult hr; Bid.Trace(" %d#, expectedExecutionCount=%d\n", ObjectID, expectedExecutionCount); hr = icommandPrepare.Prepare(expectedExecutionCount); Bid.Trace(" %08X{HRESULT}\n", hr); ProcessResults(hr); } // don't recompute bindings on prepared statements _isPrepared = true; } private void ProcessResults(OleDbHResult hr) { Exception e = OleDbConnection.ProcessResults(hr, _connection, this); if (null != e) { throw e; } } private void ProcessResultsNoReset(OleDbHResult hr) { Exception e = OleDbConnection.ProcessResults(hr, null, this); if (null != e) { throw e; } } internal object GetPropertyValue(Guid propertySet, int propertyID) { if (null != _icommandText) { OleDbHResult hr; tagDBPROP[] dbprops; UnsafeNativeMethods.ICommandProperties icommandProperties = ICommandProperties(); using(PropertyIDSet propidset = new PropertyIDSet(propertySet, propertyID)) { using(DBPropSet propset = new DBPropSet(icommandProperties, propidset, out hr)) { if (hr < 0) { // VSDD 621427: OLEDB Data Reader masks provider specific errors by raising "Internal .Net Framework Data Provider error 30." // DBPropSet c-tor will register the exception and it will be raised at GetPropertySet call in case of failure SafeNativeMethods.Wrapper.ClearErrorInfo(); } dbprops = propset.GetPropertySet(0, out propertySet); } } if (OleDbPropertyStatus.Ok == dbprops[0].dwStatus) { return dbprops[0].vValue; } return dbprops[0].dwStatus; } return OleDbPropertyStatus.NotSupported; } private bool PropertiesOnCommand(bool throwNotSupported) { if (null != _icommandText) { return true; } Debug.Assert(!_isPrepared, "null command isPrepared"); OleDbConnection connection = _connection; if (null == connection) { connection.CheckStateOpen(ODB.Properties); } if (!_trackingForClose) { _trackingForClose = true; connection.AddWeakReference(this, OleDbReferenceCollection.CommandTag); } _icommandText = connection.ICommandText(); if (null == _icommandText) { if (throwNotSupported || HasParameters()) { throw ODB.CommandTextNotSupported(connection.Provider, null); } return false; // MDAC 57856 } using(DBPropSet propSet = CommandPropertySets()) { if (null != propSet) { UnsafeNativeMethods.ICommandProperties icommandProperties = ICommandProperties(); Bid.Trace(" %d#\n", ObjectID); OleDbHResult hr = icommandProperties.SetProperties(propSet.PropertySetCount, propSet); Bid.Trace(" %08X{HRESULT}\n", hr); if (hr < 0) { SafeNativeMethods.Wrapper.ClearErrorInfo(); } } } return true; } private DBPropSet CommandPropertySets() { DBPropSet propSet = null; bool keyInfo = (0 != (CommandBehavior.KeyInfo & this.commandBehavior)); // always set the CommandTimeout value? int count = (_executeQuery ? (keyInfo ? 4 : 2) : 1); if (0 < count) { propSet = new DBPropSet(1); tagDBPROP[] dbprops = new tagDBPROP[count]; dbprops[0] = new tagDBPROP(ODB.DBPROP_COMMANDTIMEOUT, false, CommandTimeout); if (_executeQuery) { // 'Microsoft.Jet.OLEDB.4.0' default is DBPROPVAL_AO_SEQUENTIAL dbprops[1] = new tagDBPROP(ODB.DBPROP_ACCESSORDER, false, ODB.DBPROPVAL_AO_RANDOM); // MDAC 73030 if (keyInfo) { // 'Unique Rows' property required for SQLOLEDB to retrieve things like 'BaseTableName' dbprops[2] = new tagDBPROP(ODB.DBPROP_UNIQUEROWS, false, keyInfo); // otherwise 'Microsoft.Jet.OLEDB.4.0' doesn't support IColumnsRowset dbprops[3] = new tagDBPROP(ODB.DBPROP_IColumnsRowset, false, true); } } propSet.SetPropertySet(0, OleDbPropertySetGuid.Rowset, dbprops); } return propSet; } internal Bindings TakeBindingOwnerShip() { Bindings bindings = _dbBindings; _dbBindings = null; return bindings; } private void ValidateConnection(string method) { if (null == _connection) { throw ADP.ConnectionRequired(method); } _connection.CheckStateOpen(method); // user attempting to execute the command while the first dataReader hasn't returned // use the connection reference collection to see if the dataReader referencing this // command has been garbage collected or not. if (_hasDataReader) { if (_connection.HasLiveReader(this)) { throw ADP.OpenReaderExists(); } _hasDataReader = false; } } private void ValidateConnectionAndTransaction(string method) { ValidateConnection(method); _transaction = _connection.ValidateTransaction(Transaction, method); this.canceling = false; } } }