4 // Part of the Mono class libraries at
5 // mcs/class/System.Data.OracleClient/System.Data.OracleClient
7 // Assembly: System.Data.OracleClient.dll
8 // Namespace: System.Data.OracleClient
11 // Daniel Morgan <danielmorgan@verizon.net>
12 // Tim Coleman <tim@timcoleman.com>
13 // Marek Safar <marek.safar@gmail.com>
15 // Copyright (C) Daniel Morgan, 2002, 2004-2005
16 // Copyright (C) Tim Coleman , 2003
18 // Licensed under the MIT/X11 License.
22 using System.ComponentModel;
25 using System.Data.Common;
27 using System.Data.OracleClient.Oci;
28 using System.Drawing.Design;
30 using System.Threading;
32 namespace System.Data.OracleClient
35 [DefaultEvent ("RecordsAffected")]
37 [Designer ("Microsoft.VSDesigner.Data.VS.OracleCommandDesigner, " + Consts.AssemblyMicrosoft_VSDesigner)]
39 public sealed class OracleCommand :
43 Component, ICloneable, IDbCommand
48 CommandBehavior behavior;
50 CommandType commandType;
51 OracleConnection connection;
52 bool designTimeVisible;
53 OracleParameterCollection parameters;
54 OracleTransaction transaction;
55 UpdateRowSource updatedRowSource;
56 OciStatementHandle preparedStatement;
64 public OracleCommand ()
65 : this (String.Empty, null, null)
69 public OracleCommand (string commandText)
70 : this (commandText, null, null)
74 public OracleCommand (string commandText, OracleConnection connection)
75 : this (commandText, connection, null)
79 public OracleCommand (string commandText, OracleConnection connection, OracleTransaction tx)
82 preparedStatement = null;
83 CommandText = commandText;
84 Connection = connection;
86 CommandType = CommandType.Text;
87 UpdatedRowSource = UpdateRowSource.Both;
88 DesignTimeVisible = true;
89 parameters = new OracleParameterCollection ();
92 #endregion // Constructors
97 [RefreshProperties (RefreshProperties.All)]
98 [Editor ("Microsoft.VSDesigner.Data.Oracle.Design.OracleCommandTextEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))]
105 if (commandText == null)
110 set { commandText = value; }
113 [RefreshProperties (RefreshProperties.All)]
114 [DefaultValue (CommandType.Text)]
119 CommandType CommandType {
120 get { return commandType; }
122 if (value == CommandType.TableDirect)
123 throw new ArgumentException ("OracleClient provider does not support TableDirect CommandType.");
128 [DefaultValue (null)]
129 [Editor ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))]
134 OracleConnection Connection {
135 get { return connection; }
136 set { connection = value; }
141 [EditorBrowsable (EditorBrowsableState.Never)]
142 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
143 public override int CommandTimeout {
149 protected override DbConnection DbConnection {
150 get { return Connection; }
151 set { Connection = (OracleConnection) value; }
155 protected override DbParameterCollection DbParameterCollection {
156 get { return Parameters; }
160 protected override DbTransaction DbTransaction {
161 get { return Transaction; }
162 set { Transaction = (OracleTransaction) value; }
166 [DefaultValue (true)]
170 [EditorBrowsable (EditorBrowsableState.Never)]
176 bool DesignTimeVisible {
177 get { return designTimeVisible; }
178 set { designTimeVisible = value; }
181 internal OciEnvironmentHandle Environment {
182 get { return Connection.Environment; }
185 internal OciErrorHandle ErrorHandle {
186 get { return Connection.ErrorHandle; }
190 int IDbCommand.CommandTimeout {
195 [Editor ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))]
196 [DefaultValue (null)]
197 IDbConnection IDbCommand.Connection {
198 get { return Connection; }
200 // InvalidCastException is expected when types do not match
201 Connection = (OracleConnection) value;
205 IDataParameterCollection IDbCommand.Parameters {
206 get { return Parameters; }
209 IDbTransaction IDbCommand.Transaction {
210 get { return Transaction; }
212 // InvalidCastException is expected when types do not match
213 Transaction = (OracleTransaction) value;
218 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
223 OracleParameterCollection Parameters {
224 get { return parameters; }
228 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
233 OracleTransaction Transaction {
234 get { return transaction; }
235 set { transaction = value; }
238 [DefaultValue (UpdateRowSource.Both)]
243 UpdateRowSource UpdatedRowSource {
244 get { return updatedRowSource; }
245 set { updatedRowSource = value; }
252 private void AssertCommandTextIsSet ()
254 if (CommandText.Length == 0)
255 throw new InvalidOperationException ("The command text for this Command has not been set.");
258 private void AssertConnectionIsOpen ()
260 if (Connection == null || Connection.State == ConnectionState.Closed)
261 throw new InvalidOperationException ("An open Connection object is required to continue.");
264 private void AssertTransactionMatch ()
266 if (Connection.Transaction != null && Transaction != Connection.Transaction)
267 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.");
270 private void BindParameters (OciStatementHandle statement)
272 Console.Error.WriteLine("{0} - BindParameter",Thread.CurrentThread.ManagedThreadId);
273 for (int p = 0; p < Parameters.Count; p++)
274 Parameters[p].Bind (statement, Connection, (uint) p);
284 throw new NotImplementedException ();
288 public object Clone ()
290 // create a new OracleCommand object with the same properties
292 OracleCommand cmd = new OracleCommand ();
294 cmd.CommandText = this.CommandText;
295 cmd.CommandType = this.CommandType;
297 // FIXME: not sure if I should set the same object here
298 // or get a clone of these too
299 cmd.Connection = this.Connection;
300 cmd.Transaction = this.Transaction;
302 foreach (OracleParameter parm in this.Parameters) {
304 OracleParameter newParm = cmd.CreateParameter ();
306 newParm.DbType = parm.DbType;
307 newParm.Direction = parm.Direction;
308 newParm.IsNullable = parm.IsNullable;
309 newParm.Offset = parm.Offset;
310 newParm.OracleType = parm.OracleType;
311 newParm.ParameterName = parm.ParameterName;
312 //newParm.Precision = parm.Precision;
313 //newParm.Scale = parm.Scale;
314 newParm.SourceColumn = parm.SourceColumn;
315 newParm.SourceVersion = parm.SourceVersion;
316 newParm.Value = parm.Value;
318 cmd.Parameters.Add (newParm);
321 //cmd.Container = this.Container;
322 cmd.DesignTimeVisible = this.DesignTimeVisible;
323 //cmd.DesignMode = this.DesignMode;
324 cmd.Site = this.Site;
325 //cmd.UpdateRowSource = this.UpdateRowSource;
331 protected override DbParameter CreateDbParameter ()
333 return CreateParameter ();
336 protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
338 return ExecuteReader (behavior);
342 internal void UpdateParameterValues ()
345 if (Parameters.Count > 0) {
346 bool foundCursor = false;
347 for (int p = 0; p < Parameters.Count; p++) {
348 OracleParameter parm = Parameters [p];
349 if (parm.OracleType.Equals (OracleType.Cursor)) {
350 if (!foundCursor && parm.Direction != ParameterDirection.Input) {
351 // if there are multiple REF CURSORs,
352 // you only can get the first cursor for now
353 // because user of OracleDataReader
354 // will do a NextResult to get the next
355 // REF CURSOR (if it exists)
358 if (p + 1 == Parameters.Count)
369 internal void CloseDataReader ()
371 Connection.DataReader = null;
372 if ((behavior & CommandBehavior.CloseConnection) != 0)
380 OracleParameter CreateParameter ()
382 return new OracleParameter ();
385 internal void DeriveParameters ()
387 if (commandType != CommandType.StoredProcedure)
388 throw new InvalidOperationException (String.Format ("OracleCommandBuilder DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
390 //OracleParameterCollection localParameters = new OracleParameterCollection (this);
392 throw new NotImplementedException ();
395 private int ExecuteNonQueryInternal (OciStatementHandle statement, bool useAutoCommit)
399 if (preparedStatement == null)
400 PrepareStatement (statement);
402 bool isNonQuery = IsNonQuery (statement);
404 BindParameters (statement);
405 if (isNonQuery == true)
406 statement.ExecuteNonQuery (useAutoCommit);
408 statement.ExecuteQuery (false);
410 UpdateParameterValues ();
412 int rowsAffected = statement.GetAttributeInt32 (OciAttributeType.RowCount, ErrorHandle);
421 int ExecuteNonQuery ()
425 AssertConnectionIsOpen ();
426 AssertTransactionMatch ();
427 AssertCommandTextIsSet ();
428 bool useAutoCommit = false;
430 if (Transaction != null)
431 Transaction.AttachToServiceContext ();
433 useAutoCommit = true;
435 OciStatementHandle statement = GetStatementHandle ();
437 return ExecuteNonQueryInternal (statement, useAutoCommit);
439 SafeDisposeHandle (statement);
443 public int ExecuteOracleNonQuery (out OracleString rowid)
447 AssertConnectionIsOpen ();
448 AssertTransactionMatch ();
449 AssertCommandTextIsSet ();
450 bool useAutoCommit = false;
452 if (Transaction != null)
453 Transaction.AttachToServiceContext ();
455 useAutoCommit = true;
457 OciStatementHandle statement = GetStatementHandle ();
460 int retval = ExecuteNonQueryInternal (statement, useAutoCommit);
461 OciRowIdDescriptor rowIdDescriptor = statement.GetAttributeRowIdDescriptor (ErrorHandle, Environment);
462 string srowid = rowIdDescriptor.GetRowIdToString (ErrorHandle);
463 rowid = new OracleString (srowid);
464 rowIdDescriptor = null;
467 SafeDisposeHandle (statement);
471 public object ExecuteOracleScalar ()
475 object output = DBNull.Value;
477 AssertConnectionIsOpen ();
478 AssertTransactionMatch ();
479 AssertCommandTextIsSet ();
481 if (Transaction != null)
482 Transaction.AttachToServiceContext ();
484 OciStatementHandle statement = GetStatementHandle ();
486 if (preparedStatement == null)
487 PrepareStatement (statement);
489 bool isNonQuery = IsNonQuery (statement);
491 BindParameters (statement);
493 if (isNonQuery == true)
494 ExecuteNonQueryInternal (statement, false);
496 statement.ExecuteQuery (false);
498 if (statement.Fetch ()) {
499 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [0];
500 if (!defineHandle.IsNull)
501 output = defineHandle.GetOracleValue (Connection.SessionFormatProvider, Connection);
502 switch (defineHandle.DataType) {
503 case OciDataType.Blob:
504 case OciDataType.Clob:
505 ((OracleLob) output).connection = Connection;
509 UpdateParameterValues ();
514 SafeDisposeHandle (statement);
518 private bool IsNonQuery (OciStatementHandle statementHandle)
520 // assumes Prepare() has been called prior to calling this function
522 OciStatementType statementType = statementHandle.GetStatementType ();
523 if (statementType.Equals (OciStatementType.Select))
533 OracleDataReader ExecuteReader ()
535 return ExecuteReader (CommandBehavior.Default);
542 OracleDataReader ExecuteReader (CommandBehavior behavior)
544 AssertConnectionIsOpen ();
545 AssertTransactionMatch ();
546 AssertCommandTextIsSet ();
550 bool hasRows = false;
552 this.behavior = behavior;
554 if (Transaction != null)
555 Transaction.AttachToServiceContext ();
557 OciStatementHandle statement = GetStatementHandle ();
558 OracleDataReader rd = null;
561 if (preparedStatement == null)
562 PrepareStatement (statement);
564 preparedStatement = null; // OracleDataReader releases the statement handle
566 bool isNonQuery = IsNonQuery (statement);
568 BindParameters (statement);
571 ExecuteNonQueryInternal (statement, false);
573 if ((behavior & CommandBehavior.SchemaOnly) != 0)
574 statement.ExecuteQuery (true);
576 hasRows = statement.ExecuteQuery (false);
578 UpdateParameterValues ();
581 if (Parameters.Count > 0) {
582 for (int p = 0; p < Parameters.Count; p++) {
583 OracleParameter parm = Parameters [p];
584 if (parm.OracleType.Equals (OracleType.Cursor)) {
585 if (parm.Direction != ParameterDirection.Input) {
586 rd = (OracleDataReader) parm.Value;
594 rd = new OracleDataReader (this, statement, hasRows, behavior);
597 if (statement != null && rd == null)
608 object ExecuteScalar ()
611 object output = null;//if we find nothing we return this
613 AssertConnectionIsOpen ();
614 AssertTransactionMatch ();
615 AssertCommandTextIsSet ();
617 if (Transaction != null)
618 Transaction.AttachToServiceContext ();
620 OciStatementHandle statement = GetStatementHandle ();
622 if (preparedStatement == null)
623 PrepareStatement (statement);
625 bool isNonQuery = IsNonQuery (statement);
627 BindParameters (statement);
629 if (isNonQuery == true)
630 ExecuteNonQueryInternal (statement, false);
632 statement.ExecuteQuery (false);
634 if (statement.Fetch ()) {
635 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [0];
636 if (!defineHandle.IsNull)
638 switch (defineHandle.DataType) {
639 case OciDataType.Blob:
640 case OciDataType.Clob:
641 OracleLob lob = (OracleLob) defineHandle.GetValue (
642 Connection.SessionFormatProvider, Connection);
643 lob.connection = Connection;
648 output = defineHandle.GetValue (
649 Connection.SessionFormatProvider, Connection);
654 UpdateParameterValues ();
657 SafeDisposeHandle (statement);
663 internal OciStatementHandle GetNextResult ()
665 if (moreResults == -1)
668 if (Parameters.Count > 0) {
669 int p = moreResults + 1;
671 if (p >= Parameters.Count) {
676 for (; p < Parameters.Count; p++) {
677 OracleParameter parm = Parameters [p];
678 if (parm.OracleType.Equals (OracleType.Cursor)) {
679 if (parm.Direction != ParameterDirection.Input) {
680 if (p + 1 == Parameters.Count)
684 return parm.GetOutRefCursor (this);
695 private OciStatementHandle GetStatementHandle ()
697 AssertConnectionIsOpen ();
698 if (preparedStatement != null)
699 return preparedStatement;
701 OciStatementHandle h = (OciStatementHandle) Connection.Environment.Allocate (OciHandleType.Statement);
702 h.ErrorHandle = Connection.ErrorHandle;
703 h.Service = Connection.ServiceContext;
708 private void SafeDisposeHandle (OciStatementHandle h)
710 if (h != null && h != preparedStatement)
715 IDbDataParameter IDbCommand.CreateParameter ()
717 return CreateParameter ();
720 IDataReader IDbCommand.ExecuteReader ()
722 return ExecuteReader ();
725 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
727 return ExecuteReader (behavior);
731 void PrepareStatement (OciStatementHandle statement)
733 if (commandType == CommandType.StoredProcedure) {
734 StringBuilder sb = new StringBuilder ();
735 if (Parameters.Count > 0)
736 foreach (OracleParameter parm in Parameters) {
739 sb.Append (parm.ParameterName + "=>:" + parm.ParameterName);
742 string sql = "begin " + commandText + "(" + sb.ToString() + "); end;";
743 statement.Prepare (sql);
745 statement.Prepare (commandText);
754 AssertConnectionIsOpen ();
755 OciStatementHandle statement = GetStatementHandle ();
756 PrepareStatement (statement);
757 preparedStatement = statement;
760 protected override void Dispose (bool disposing)
762 if (preparedStatement != null)
763 OciCalls.OCIHandleFree(preparedStatement,
764 OciHandleType.Statement);
766 if (Parameters.Count > 0)
767 foreach (OracleParameter parm in Parameters)
769 base.Dispose (disposing);
772 #endregion // Methods