688 lines
17 KiB
C#
688 lines
17 KiB
C#
//
|
|
// 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 <danielmorgan@verizon.net>
|
|
// Tim Coleman <tim@timcoleman.com>
|
|
// Marek Safar <marek.safar@gmail.com>
|
|
//
|
|
// 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;
|
|
using System.Data.Common;
|
|
using System.Data.OracleClient.Oci;
|
|
using System.Drawing.Design;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace System.Data.OracleClient
|
|
{
|
|
[DefaultEvent ("RecordsAffected")]
|
|
[Designer ("Microsoft.VSDesigner.Data.VS.OracleCommandDesigner, " + Consts.AssemblyMicrosoft_VSDesigner)]
|
|
[ToolboxItem (true)]
|
|
public sealed class OracleCommand :
|
|
DbCommand, ICloneable
|
|
{
|
|
#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
|
|
override
|
|
string CommandText {
|
|
get {
|
|
if (commandText == null)
|
|
return string.Empty;
|
|
|
|
return commandText;
|
|
}
|
|
set { commandText = value; }
|
|
}
|
|
|
|
[RefreshProperties (RefreshProperties.All)]
|
|
[DefaultValue (CommandType.Text)]
|
|
public
|
|
override
|
|
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
|
|
new
|
|
OracleConnection Connection {
|
|
get { return connection; }
|
|
set { connection = value; }
|
|
}
|
|
|
|
[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; }
|
|
}
|
|
|
|
[DefaultValue (true)]
|
|
[Browsable (false)]
|
|
[DesignOnly (true)]
|
|
[EditorBrowsable (EditorBrowsableState.Never)]
|
|
public
|
|
override
|
|
bool DesignTimeVisible {
|
|
get { return designTimeVisible; }
|
|
set { designTimeVisible = value; }
|
|
}
|
|
|
|
internal OciEnvironmentHandle Environment {
|
|
get { return Connection.Environment; }
|
|
}
|
|
|
|
internal OciErrorHandle ErrorHandle {
|
|
get { return Connection.ErrorHandle; }
|
|
}
|
|
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public
|
|
new
|
|
OracleParameterCollection Parameters {
|
|
get { return parameters; }
|
|
}
|
|
|
|
[Browsable (false)]
|
|
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
|
|
public
|
|
new
|
|
OracleTransaction Transaction {
|
|
get { return transaction; }
|
|
set { transaction = value; }
|
|
}
|
|
|
|
[DefaultValue (UpdateRowSource.Both)]
|
|
public
|
|
override
|
|
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
|
|
override
|
|
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;
|
|
}
|
|
|
|
protected override DbParameter CreateDbParameter ()
|
|
{
|
|
return CreateParameter ();
|
|
}
|
|
|
|
protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
|
|
{
|
|
return ExecuteReader (behavior);
|
|
}
|
|
|
|
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
|
|
new
|
|
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
|
|
override
|
|
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
|
|
new
|
|
OracleDataReader ExecuteReader ()
|
|
{
|
|
return ExecuteReader (CommandBehavior.Default);
|
|
}
|
|
|
|
public
|
|
new
|
|
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
|
|
override
|
|
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();
|
|
}
|
|
|
|
|
|
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
|
|
override
|
|
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
|
|
}
|
|
}
|