// // OracleCommand.cs // // Part of the Mono class libraries at // mcs/class/System.Data.OracleClient/System.Data.OracleClient // // Assembly: System.Data.OracleClient.dll // Namespace: System.Data.OracleClient // // Authors: // Daniel Morgan // Tim Coleman // Marek Safar // // Copyright (C) Daniel Morgan, 2002, 2004-2005 // Copyright (C) Tim Coleman , 2003 // // Licensed under the MIT/X11 License. // using System; using System.ComponentModel; using System.Data; #if NET_2_0 using System.Data.Common; #endif using System.Data.OracleClient.Oci; using System.Drawing.Design; using System.Text; using System.Threading; namespace System.Data.OracleClient { #if NET_2_0 [DefaultEvent ("RecordsAffected")] #endif [Designer ("Microsoft.VSDesigner.Data.VS.OracleCommandDesigner, " + Consts.AssemblyMicrosoft_VSDesigner)] [ToolboxItem (true)] public sealed class OracleCommand : #if NET_2_0 DbCommand, ICloneable #else Component, ICloneable, IDbCommand #endif { #region Fields CommandBehavior behavior; string commandText; CommandType commandType; OracleConnection connection; bool designTimeVisible; OracleParameterCollection parameters; OracleTransaction transaction; UpdateRowSource updatedRowSource; OciStatementHandle preparedStatement; int moreResults; #endregion // Fields #region Constructors public OracleCommand () : this (String.Empty, null, null) { } public OracleCommand (string commandText) : this (commandText, null, null) { } public OracleCommand (string commandText, OracleConnection connection) : this (commandText, connection, null) { } public OracleCommand (string commandText, OracleConnection connection, OracleTransaction tx) { moreResults = -1; preparedStatement = null; CommandText = commandText; Connection = connection; Transaction = tx; CommandType = CommandType.Text; UpdatedRowSource = UpdateRowSource.Both; DesignTimeVisible = true; parameters = new OracleParameterCollection (); } #endregion // Constructors #region Properties [DefaultValue ("")] [RefreshProperties (RefreshProperties.All)] [Editor ("Microsoft.VSDesigner.Data.Oracle.Design.OracleCommandTextEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))] public #if NET_2_0 override #endif string CommandText { get { if (commandText == null) return string.Empty; return commandText; } set { commandText = value; } } [RefreshProperties (RefreshProperties.All)] [DefaultValue (CommandType.Text)] public #if NET_2_0 override #endif CommandType CommandType { get { return commandType; } set { if (value == CommandType.TableDirect) throw new ArgumentException ("OracleClient provider does not support TableDirect CommandType."); commandType = value; } } [DefaultValue (null)] [Editor ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))] public #if NET_2_0 new #endif OracleConnection Connection { get { return connection; } set { connection = value; } } #if NET_2_0 [Browsable (false)] [EditorBrowsable (EditorBrowsableState.Never)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public override int CommandTimeout { get { return 0; } set { } } [MonoTODO] protected override DbConnection DbConnection { get { return Connection; } set { Connection = (OracleConnection) value; } } [MonoTODO] protected override DbParameterCollection DbParameterCollection { get { return Parameters; } } [MonoTODO] protected override DbTransaction DbTransaction { get { return Transaction; } set { Transaction = (OracleTransaction) value; } } #endif [DefaultValue (true)] [Browsable (false)] [DesignOnly (true)] #if NET_2_0 [EditorBrowsable (EditorBrowsableState.Never)] #endif public #if NET_2_0 override #endif bool DesignTimeVisible { get { return designTimeVisible; } set { designTimeVisible = value; } } internal OciEnvironmentHandle Environment { get { return Connection.Environment; } } internal OciErrorHandle ErrorHandle { get { return Connection.ErrorHandle; } } #if !NET_2_0 int IDbCommand.CommandTimeout { get { return 0; } set { } } [Editor ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))] [DefaultValue (null)] IDbConnection IDbCommand.Connection { get { return Connection; } set { // InvalidCastException is expected when types do not match Connection = (OracleConnection) value; } } IDataParameterCollection IDbCommand.Parameters { get { return Parameters; } } IDbTransaction IDbCommand.Transaction { get { return Transaction; } set { // InvalidCastException is expected when types do not match Transaction = (OracleTransaction) value; } } #endif [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public #if NET_2_0 new #endif OracleParameterCollection Parameters { get { return parameters; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public #if NET_2_0 new #endif OracleTransaction Transaction { get { return transaction; } set { transaction = value; } } [DefaultValue (UpdateRowSource.Both)] public #if NET_2_0 override #endif UpdateRowSource UpdatedRowSource { get { return updatedRowSource; } set { updatedRowSource = value; } } #endregion #region Methods private void AssertCommandTextIsSet () { if (CommandText.Length == 0) throw new InvalidOperationException ("The command text for this Command has not been set."); } private void AssertConnectionIsOpen () { if (Connection == null || Connection.State == ConnectionState.Closed) throw new InvalidOperationException ("An open Connection object is required to continue."); } private void AssertTransactionMatch () { if (Connection.Transaction != null && Transaction != Connection.Transaction) throw new InvalidOperationException ("Execute requires the Command object to have a Transaction object when the Connection object assigned to the command is in a pending local transaction. The Transaction property of the Command has not been initialized."); } private void BindParameters (OciStatementHandle statement) { for (int p = 0; p < Parameters.Count; p++) Parameters[p].Bind (statement, Connection, (uint) p); } [MonoTODO] public #if NET_2_0 override #endif void Cancel () { throw new NotImplementedException (); } [MonoTODO] public object Clone () { // create a new OracleCommand object with the same properties OracleCommand cmd = new OracleCommand (); cmd.CommandText = this.CommandText; cmd.CommandType = this.CommandType; // FIXME: not sure if I should set the same object here // or get a clone of these too cmd.Connection = this.Connection; cmd.Transaction = this.Transaction; foreach (OracleParameter parm in this.Parameters) { OracleParameter newParm = cmd.CreateParameter (); newParm.DbType = parm.DbType; newParm.Direction = parm.Direction; newParm.IsNullable = parm.IsNullable; newParm.Offset = parm.Offset; newParm.OracleType = parm.OracleType; newParm.ParameterName = parm.ParameterName; //newParm.Precision = parm.Precision; //newParm.Scale = parm.Scale; newParm.SourceColumn = parm.SourceColumn; newParm.SourceVersion = parm.SourceVersion; newParm.Value = parm.Value; cmd.Parameters.Add (newParm); } //cmd.Container = this.Container; cmd.DesignTimeVisible = this.DesignTimeVisible; //cmd.DesignMode = this.DesignMode; cmd.Site = this.Site; //cmd.UpdateRowSource = this.UpdateRowSource; return cmd; } #if NET_2_0 protected override DbParameter CreateDbParameter () { return CreateParameter (); } protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior) { return ExecuteReader (behavior); } #endif internal void UpdateParameterValues () { moreResults = -1; if (Parameters.Count > 0) { bool foundCursor = false; for (int p = 0; p < Parameters.Count; p++) { OracleParameter parm = Parameters [p]; if (parm.OracleType.Equals (OracleType.Cursor)) { if (!foundCursor && parm.Direction != ParameterDirection.Input) { // if there are multiple REF CURSORs, // you only can get the first cursor for now // because user of OracleDataReader // will do a NextResult to get the next // REF CURSOR (if it exists) foundCursor = true; parm.Update (this); if (p + 1 == Parameters.Count) moreResults = -1; else moreResults = p; } } else parm.Update (this); } } } internal void CloseDataReader () { Connection.DataReader = null; if ((behavior & CommandBehavior.CloseConnection) != 0) Connection.Close (); } public #if NET_2_0 new #endif OracleParameter CreateParameter () { return new OracleParameter (); } internal void DeriveParameters () { if (commandType != CommandType.StoredProcedure) throw new InvalidOperationException (String.Format ("OracleCommandBuilder DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType)); //OracleParameterCollection localParameters = new OracleParameterCollection (this); throw new NotImplementedException (); } private int ExecuteNonQueryInternal (OciStatementHandle statement, bool useAutoCommit) { moreResults = -1; if (preparedStatement == null) PrepareStatement (statement); bool isNonQuery = IsNonQuery (statement); BindParameters (statement); if (isNonQuery == true) statement.ExecuteNonQuery (useAutoCommit); else statement.ExecuteQuery (false); UpdateParameterValues (); int rowsAffected = statement.GetAttributeInt32 (OciAttributeType.RowCount, ErrorHandle); return rowsAffected; } public #if NET_2_0 override #endif int ExecuteNonQuery () { moreResults = -1; AssertConnectionIsOpen (); AssertTransactionMatch (); AssertCommandTextIsSet (); bool useAutoCommit = false; if (Transaction != null) Transaction.AttachToServiceContext (); else useAutoCommit = true; OciStatementHandle statement = GetStatementHandle (); try { return ExecuteNonQueryInternal (statement, useAutoCommit); } finally { SafeDisposeHandle (statement); } } public int ExecuteOracleNonQuery (out OracleString rowid) { moreResults = -1; AssertConnectionIsOpen (); AssertTransactionMatch (); AssertCommandTextIsSet (); bool useAutoCommit = false; if (Transaction != null) Transaction.AttachToServiceContext (); else useAutoCommit = true; OciStatementHandle statement = GetStatementHandle (); try { int retval = ExecuteNonQueryInternal (statement, useAutoCommit); OciRowIdDescriptor rowIdDescriptor = statement.GetAttributeRowIdDescriptor (ErrorHandle, Environment); string srowid = rowIdDescriptor.GetRowIdToString (ErrorHandle); rowid = new OracleString (srowid); rowIdDescriptor = null; return retval; } finally { SafeDisposeHandle (statement); } } public object ExecuteOracleScalar () { moreResults = -1; object output = DBNull.Value; AssertConnectionIsOpen (); AssertTransactionMatch (); AssertCommandTextIsSet (); if (Transaction != null) Transaction.AttachToServiceContext (); OciStatementHandle statement = GetStatementHandle (); try { if (preparedStatement == null) PrepareStatement (statement); bool isNonQuery = IsNonQuery (statement); BindParameters (statement); if (isNonQuery == true) ExecuteNonQueryInternal (statement, false); else { statement.ExecuteQuery (false); if (statement.Fetch ()) { OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [0]; if (!defineHandle.IsNull) output = defineHandle.GetOracleValue (Connection.SessionFormatProvider, Connection); switch (defineHandle.DataType) { case OciDataType.Blob: case OciDataType.Clob: ((OracleLob) output).connection = Connection; break; } } UpdateParameterValues (); } return output; } finally { SafeDisposeHandle (statement); } } private bool IsNonQuery (OciStatementHandle statementHandle) { // assumes Prepare() has been called prior to calling this function OciStatementType statementType = statementHandle.GetStatementType (); if (statementType.Equals (OciStatementType.Select)) return false; return true; } public #if NET_2_0 new #endif OracleDataReader ExecuteReader () { return ExecuteReader (CommandBehavior.Default); } public #if NET_2_0 new #endif OracleDataReader ExecuteReader (CommandBehavior behavior) { AssertConnectionIsOpen (); AssertTransactionMatch (); AssertCommandTextIsSet (); moreResults = -1; bool hasRows = false; this.behavior = behavior; if (Transaction != null) Transaction.AttachToServiceContext (); OciStatementHandle statement = GetStatementHandle (); OracleDataReader rd = null; try { if (preparedStatement == null) PrepareStatement (statement); else preparedStatement = null; // OracleDataReader releases the statement handle bool isNonQuery = IsNonQuery (statement); BindParameters (statement); if (isNonQuery) ExecuteNonQueryInternal (statement, false); else { if ((behavior & CommandBehavior.SchemaOnly) != 0) statement.ExecuteQuery (true); else hasRows = statement.ExecuteQuery (false); UpdateParameterValues (); } if (Parameters.Count > 0) { for (int p = 0; p < Parameters.Count; p++) { OracleParameter parm = Parameters [p]; if (parm.OracleType.Equals (OracleType.Cursor)) { if (parm.Direction != ParameterDirection.Input) { rd = (OracleDataReader) parm.Value; break; } } } } if (rd == null) rd = new OracleDataReader (this, statement, hasRows, behavior); } finally { if (statement != null && rd == null) statement.Dispose(); } return rd; } public #if NET_2_0 override #endif object ExecuteScalar () { moreResults = -1; object output = null;//if we find nothing we return this AssertConnectionIsOpen (); AssertTransactionMatch (); AssertCommandTextIsSet (); if (Transaction != null) Transaction.AttachToServiceContext (); OciStatementHandle statement = GetStatementHandle (); try { if (preparedStatement == null) PrepareStatement (statement); bool isNonQuery = IsNonQuery (statement); BindParameters (statement); if (isNonQuery == true) ExecuteNonQueryInternal (statement, false); else { statement.ExecuteQuery (false); if (statement.Fetch ()) { OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [0]; if (!defineHandle.IsNull) { switch (defineHandle.DataType) { case OciDataType.Blob: case OciDataType.Clob: OracleLob lob = (OracleLob) defineHandle.GetValue ( Connection.SessionFormatProvider, Connection); lob.connection = Connection; output = lob.Value; lob.Close (); break; default: output = defineHandle.GetValue ( Connection.SessionFormatProvider, Connection); break; } } } UpdateParameterValues (); } } finally { SafeDisposeHandle (statement); } return output; } internal OciStatementHandle GetNextResult () { if (moreResults == -1) return null; if (Parameters.Count > 0) { int p = moreResults + 1; if (p >= Parameters.Count) { moreResults = -1; return null; } for (; p < Parameters.Count; p++) { OracleParameter parm = Parameters [p]; if (parm.OracleType.Equals (OracleType.Cursor)) { if (parm.Direction != ParameterDirection.Input) { if (p + 1 == Parameters.Count) moreResults = -1; else moreResults = p; return parm.GetOutRefCursor (this); } } } } moreResults = -1; return null; } private OciStatementHandle GetStatementHandle () { AssertConnectionIsOpen (); if (preparedStatement != null) return preparedStatement; OciStatementHandle h = (OciStatementHandle) Connection.Environment.Allocate (OciHandleType.Statement); h.ErrorHandle = Connection.ErrorHandle; h.Service = Connection.ServiceContext; h.Command = this; return h; } private void SafeDisposeHandle (OciStatementHandle h) { if (h != null && h != preparedStatement) h.Dispose(); } #if !NET_2_0 IDbDataParameter IDbCommand.CreateParameter () { return CreateParameter (); } IDataReader IDbCommand.ExecuteReader () { return ExecuteReader (); } IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior) { return ExecuteReader (behavior); } #endif void PrepareStatement (OciStatementHandle statement) { if (commandType == CommandType.StoredProcedure) { StringBuilder sb = new StringBuilder (); if (Parameters.Count > 0) foreach (OracleParameter parm in Parameters) { if (sb.Length > 0) sb.Append (","); sb.Append (parm.ParameterName + "=>:" + parm.ParameterName); } string sql = "begin " + commandText + "(" + sb.ToString() + "); end;"; statement.Prepare (sql); } else // Text statement.Prepare (commandText); } public #if NET_2_0 override #endif void Prepare () { AssertConnectionIsOpen (); OciStatementHandle statement = GetStatementHandle (); PrepareStatement (statement); preparedStatement = statement; } protected override void Dispose (bool disposing) { if (preparedStatement != null) OciCalls.OCIHandleFree(preparedStatement, OciHandleType.Statement); if (disposing) if (Parameters.Count > 0) foreach (OracleParameter parm in Parameters) parm.FreeHandle (); base.Dispose (disposing); } #endregion // Methods } }