-//
+//
// OracleParameter.cs
//
// Part of the Mono class libraries at
// Assembly: System.Data.OracleClient.dll
// Namespace: System.Data.OracleClient
//
-// Authors:
+// Authors:
// Tim Coleman <tim@timcoleman.com>
-// Daniel Moragn <danielmorgan@verizon.net>
+// Daniel Moragn <monodanmorg@yahoo.com>
// Hubert FONGARNAND <informatique.internet@fiducial.fr>
+// Veerapuram Varadhan <vvaradhan@novell.com>
//
// Copyright (C) Tim Coleman , 2003
-// Copyright (C) Daniel Morgan, 2005
+// Copyright (C) Daniel Morgan, 2005, 2008, 2009
// Copyright (C) Hubert FONGARNAND, 2005
+// Copyright (C) Novell Inc, 2009
//
// Licensed under the MIT/X11 License.
//
using System.Collections;
using System.ComponentModel;
using System.Data;
+#if NET_2_0
+using System.Data.Common;
+#endif
using System.Data.SqlTypes;
using System.Data.OracleClient.Oci;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
-namespace System.Data.OracleClient {
+namespace System.Data.OracleClient
+{
[TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
- public sealed class OracleParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
+ public sealed class OracleParameter :
+#if NET_2_0
+ DbParameter, IDbDataParameter, ICloneable
+#else
+ MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
+#endif
{
#region Fields
byte precision;
byte scale;
string srcColumn;
+#if NET_2_0
+ bool sourceColumnNullMapping;
+#endif
DataRowVersion srcVersion;
DbType dbType = DbType.AnsiString;
- int offset = 0;
- bool sizeSet = false;
+ int offset;
+ bool sizeSet;
+ bool oracleTypeSet;
object value = DBNull.Value;
- OciLobLocator lobLocator = null; // only if Blob or Clob
+ OciLobLocator lobLocator; // only if Blob or Clob
IntPtr bindOutValue = IntPtr.Zero;
- OciDateTimeDescriptor dateTimeDesc = null;
+ OciDateTimeDescriptor dateTimeDesc;
IntPtr cursor = IntPtr.Zero;
- OracleParameterCollection container = null;
+ OracleParameterCollection container;
OciBindHandle bindHandle;
- OciErrorHandle errorHandle;
OracleConnection connection;
- byte[] bytes = null;
+ byte[] bytes;
IntPtr bindValue = IntPtr.Zero;
- bool useRef = false;
+ bool useRef;
OciDataType bindType;
- short indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
- int bindSize = 0;
- uint position = 0;
+ short indicator;
+ int bindSize;
+ bool sizeManuallySet;
#endregion // Fields
#region Constructors
// constructor for cloning the object
- internal OracleParameter (OracleParameter value) {
+ private OracleParameter (OracleParameter value)
+ {
this.name = value.name;
this.oracleType = value.oracleType;
this.ociType = value.ociType;
this.sizeSet = value.sizeSet;
this.value = value.value;
this.lobLocator = value.lobLocator;
+ this.oracleTypeSet = value.oracleTypeSet;
}
public OracleParameter ()
- : this (String.Empty, OracleType.VarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
{
+ this.name = String.Empty;
+ this.oracleType = OracleType.VarChar;
+ this.size = 0;
+ this.direction = ParameterDirection.Input;
+ this.isNullable = false;
+ this.precision = 0;
+ this.scale = 0;
+ this.srcColumn = String.Empty;
+ this.srcVersion = DataRowVersion.Current;
+ this.value = null;
+ this.oracleTypeSet = false;
}
public OracleParameter (string name, object value)
{
this.name = name;
this.value = value;
+
+ srcColumn = string.Empty;
SourceVersion = DataRowVersion.Current;
- InferOracleType (value);
+ InferOracleType (value);
+#if NET_2_0
+ // Find the OciType before inferring for the size
+ if (value != null && value != DBNull.Value) {
+ this.sizeSet = true;
+ this.size = InferSize ();
+ }
+#endif
}
- public OracleParameter (string name, OracleType dataType)
- : this (name, dataType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
+ public OracleParameter (string name, OracleType oracleType)
+ : this (name, oracleType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
{
}
- public OracleParameter (string name, OracleType dataType, int size)
- : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
+ public OracleParameter (string name, OracleType oracleType, int size)
+ : this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
{
}
- public OracleParameter (string name, OracleType dataType, int size, string srcColumn)
- : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
+ public OracleParameter (string name, OracleType oracleType, int size, string srcColumn)
+ : this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
{
}
- public OracleParameter (string name, OracleType dataType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
+#if NET_2_0
+ public OracleParameter (string name, OracleType oracleType, int size, ParameterDirection direction, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value)
{
this.name = name;
+ if (size < 0)
+ throw new ArgumentException("Size must be not be negative.");
+
+ this.value = value;
this.size = size;
+ Direction = direction;
+
+ // set sizeSet to true iff value is not-null or non-zero size value
+ if (((value != null && value != DBNull.Value) || Direction == ParameterDirection.Output) &&
+ size > 0)
+ this.sizeSet = true;
+
+ SourceColumnNullMapping = sourceColumnNullMapping;
+ OracleType = oracleType;
+ SourceColumn = sourceColumn;
+ SourceVersion = sourceVersion;
+ }
+#endif
+
+ public OracleParameter (string name, OracleType oracleType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
+ {
+ this.name = name;
+ if (size < 0)
+ throw new ArgumentException("Size must be not be negative.");
+
this.value = value;
+ this.size = size;
- OracleType = dataType;
Direction = direction;
+
+ // set sizeSet to true iff value is not-null or non-zero size value
+ if (((value != null && value != DBNull.Value) || Direction == ParameterDirection.Output) &&
+ size > 0)
+ this.sizeSet = true;
+
+ this.isNullable = isNullable;
+ this.precision = precision;
+ this.scale = scale;
+
+ OracleType = oracleType;
SourceColumn = srcColumn;
SourceVersion = srcVersion;
}
set { container = value; }
}
+#if !NET_2_0
[Browsable (false)]
[RefreshProperties (RefreshProperties.All)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
- public DbType DbType {
+#endif
+ public
+#if NET_2_0
+ override
+#endif
+ DbType DbType {
get { return dbType; }
set { SetDbType (value); }
}
+#if !NET_2_0
[DefaultValue (ParameterDirection.Input)]
+#endif
[RefreshProperties (RefreshProperties.All)]
- public ParameterDirection Direction {
+ public
+#if NET_2_0
+ override
+#endif
+ ParameterDirection Direction {
get { return direction; }
- set { direction = value; }
+ set {
+ direction = value;
+ if (this.size > 0 && direction == ParameterDirection.Output)
+ this.sizeSet = true;
+ }
}
+#if !NET_2_0
[Browsable (false)]
[DesignOnly (true)]
[DefaultValue (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
- public bool IsNullable {
+#endif
+ public
+#if NET_2_0
+ override
+#endif
+ bool IsNullable {
get { return isNullable; }
set { isNullable = value; }
}
+#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+#else
[DefaultValue (0)]
+#endif
[Browsable (false)]
public int Offset {
get { return offset; }
[DefaultValue (OracleType.VarChar)]
[RefreshProperties (RefreshProperties.All)]
+#if NET_2_0
+ [DbProviderSpecificTypeProperty (true)]
+#endif
public OracleType OracleType {
get { return oracleType; }
- set { SetOracleType (value); }
+ set {
+ oracleTypeSet = true;
+ SetOracleType (value, false);
+ }
}
-
+
+#if !NET_2_0
[DefaultValue ("")]
- public string ParameterName {
- get { return name; }
+#endif
+ public
+#if NET_2_0
+ override
+#endif
+ string ParameterName {
+ get {
+ if (name == null)
+ return string.Empty;
+ return name;
+ }
set { name = value; }
}
+#if NET_2_0
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Obsolete("Set the precision of a decimal use the Math classes.")]
+#else
[DefaultValue (0)]
+#endif
public byte Precision {
get { return precision; }
set { /* NO EFFECT*/ }
}
+#if NET_2_0
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ [Obsolete("Set the precision of a decimal use the Math classes.")]
+#else
[DefaultValue (0)]
+#endif
public byte Scale {
get { return scale; }
set { /* NO EFFECT*/ }
}
+#if !NET_2_0
[DefaultValue (0)]
- public int Size {
+#endif
+ public
+#if NET_2_0
+ override
+#endif
+ int Size {
get { return size; }
- set {
+ set {
sizeSet = true;
- size = value;
+ size = value;
+ sizeManuallySet = true;
}
}
+#if !NET_2_0
[DefaultValue ("")]
- public string SourceColumn {
+#endif
+ public
+#if NET_2_0
+ override
+#endif
+ string SourceColumn {
get { return srcColumn; }
set { srcColumn = value; }
}
+#if NET_2_0
+ [MonoTODO]
+ public override bool SourceColumnNullMapping {
+ get { return sourceColumnNullMapping; }
+ set { sourceColumnNullMapping = value; }
+ }
+#endif
+
+#if !NET_2_0
[DefaultValue ("Current")]
- public DataRowVersion SourceVersion {
+#endif
+ public
+#if NET_2_0
+ override
+#endif
+ DataRowVersion SourceVersion {
get { return srcVersion; }
set { srcVersion = value; }
}
+#if !NET_2_0
[DefaultValue (null)]
+#endif
[RefreshProperties (RefreshProperties.All)]
[TypeConverter (typeof(StringConverter))]
- public object Value {
+ public
+#if NET_2_0
+ override
+#endif
+ object Value {
get { return this.value; }
- set { this.value = value; }
+ set {
+ this.value = value;
+ if (!oracleTypeSet)
+ InferOracleType (value);
+#if NET_2_0
+ if (value != null && value != DBNull.Value) {
+ this.size = InferSize ();
+ this.sizeSet = true;
+ }
+#endif
+ }
}
#endregion // Properties
{
connection = con;
- errorHandle = connection.ErrorHandle;
-
if (bindHandle == null)
bindHandle = new OciBindHandle ((OciHandle) statement);
IntPtr tmpHandle = bindHandle.Handle;
- position = pos;
-
if (Direction != ParameterDirection.Input)
AssertSizeIsSet ();
if (!sizeSet)
bindType = ociType;
int rsize = 0;
+ string svalue;
+ string sDate;
+ DateTime dt;
bool isnull = false;
+ int byteCount;
+ byte[] byteArrayLen;
+
if (direction == ParameterDirection.Input || direction == ParameterDirection.InputOutput) {
- try {
- Type nullable = v.GetType ().GetInterface ("System.Data.SqlTypes.INullable", false);
- if (nullable != null) {
- INullable mynullable = (INullable)v;
+ if (v == null)
+ isnull = true;
+ else if (v is DBNull)
+ isnull = true;
+ else {
+ INullable mynullable = v as INullable;
+ if (mynullable != null)
isnull = mynullable.IsNull;
- }
- }
- catch(Exception e) {
- System.IO.TextWriter.Null.WriteLine(e.Message);
- }
- }
+ }
+ }
- // TODO: handle InputOutput and Return parameters
- if (direction == ParameterDirection.Output) {
- // TODO: need to figure out how OracleParameter
- // which uses OciBindHandle to share code
- // with OciDefineHandle
+ if (isnull == true && direction == ParameterDirection.Input) {
+ indicator = 0;
+ bindType = OciDataType.VarChar2;
+ bindSize = 0;
+ } else {
switch(ociType) {
- case OciDataType.VarChar2:
- case OciDataType.String:
- case OciDataType.VarChar:
- case OciDataType.Char:
- case OciDataType.CharZ:
- case OciDataType.OciString:
- bindType = OciDataType.Char;
- bindSize = size * 2;
- bindOutValue = Marshal.AllocHGlobal (bindSize);
- bindValue = bindOutValue;
- break;
- case OciDataType.RowIdDescriptor:
- size = 10;
- bindType = OciDataType.Char;
- bindSize = size * 2;
- bindOutValue = Marshal.AllocHGlobal (bindSize);
- bindValue = bindOutValue;
- break;
- case OciDataType.Date:
- bindSize = 7;
- bindType = OciDataType.Date;
- bindOutValue = Marshal.AllocHGlobal (bindSize);
- bindValue = bindOutValue;
- break;
- case OciDataType.TimeStamp:
- dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
- if (dateTimeDesc == null) {
- OciErrorInfo info = connection.ErrorHandle.HandleError ();
- throw new OracleException (info.ErrorCode, info.ErrorMessage);
+ case OciDataType.VarChar2:
+ case OciDataType.String:
+ case OciDataType.VarChar:
+ case OciDataType.Char:
+ case OciDataType.CharZ:
+ case OciDataType.OciString:
+ bindType = OciDataType.String;
+ indicator = 0;
+ svalue = "\0";
+ // convert value from managed type to type to marshal
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+
+ svalue = v.ToString ();
+
+ if (direction == ParameterDirection.Input && size > 0 && svalue.Length > size)
+ svalue = svalue.Substring(0, size);
+
+ svalue = svalue.ToString () + '\0';
+
+ // convert managed type to memory allocated earlier
+ // in this case using OCIUnicodeToCharSet
+ rsize = 0;
+ // Get size of buffer
+ status = OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
+
+ if (direction == ParameterDirection.Input)
+ bindSize = rsize;
+ else {
+ // this cannot be rsize because you need room for the output after the execute
+ bindSize = Encoding.UTF8.GetMaxByteCount (Size + 1);
}
- dateTimeDesc.ErrorHandle = connection.ErrorHandle;
- bindSize = 11;
- bindType = OciDataType.TimeStamp;
- bindOutValue = dateTimeDesc.Handle;
- bindValue = dateTimeDesc.Handle;
- useRef = true;
- break;
- case OciDataType.Number:
- bindSize = 22;
- bindType = OciDataType.Char;
- bindOutValue = Marshal.AllocHGlobal (bindSize);
- bindValue = bindOutValue;
- break;
- case OciDataType.Long:
- case OciDataType.LongVarChar:
- // LAMESPEC: you don't know size until you get it;
- // therefore, you must allocate an insane size
- // see OciDefineHandle
- bindSize = OciDefineHandle.LongVarCharMaxValue;
- bindOutValue = Marshal.AllocHGlobal (bindSize);
- bindType = OciDataType.LongVarChar;
- bindValue = bindOutValue;
- break;
- case OciDataType.Blob:
- case OciDataType.Clob:
+
+ // allocate memory based on bind size
+ bytes = new byte [bindSize];
+
+ // Fill buffer
+ status = OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
+ } else {
+ // for Output and ReturnValue parameters, get size in bytes
+ bindSize = Encoding.UTF8.GetMaxByteCount (size + 1);
+ // allocate memory for oracle to place the results for the Return or Output param
+ bytes = new byte [bindSize];
+ }
+ break;
+ case OciDataType.Date:
+ bindType = OciDataType.Date;
+ bindSize = 7;
+ // convert value from managed type to type to marshal
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+
+ if (isnull)
+ bytes = new byte [7];
+ else {
+ sDate = "";
+ dt = DateTime.MinValue;
+ if (v is String) {
+ sDate = (string) v;
+ dt = DateTime.Parse (sDate);
+ }
+ else if (v is DateTime)
+ dt = (DateTime) v;
+ else if (v is OracleString) {
+ sDate = v.ToString ();
+ dt = DateTime.Parse (sDate);
+ }
+ else if (v is OracleDateTime) {
+ OracleDateTime odt = (OracleDateTime) v;
+ dt = (DateTime) odt.Value;
+ }
+ else
+ throw new NotImplementedException ("For OracleType.DateTime, data type not implemented: " + v.GetType().ToString() + ".");
+
+ // for Input and InputOuput, create byte array and pack DateTime into it
+ bytes = PackDate (dt);
+ }
+ } else {
+ // allocate 7-byte array for Output and ReturnValue to put date
+ bytes = new byte [7];
+ }
+ break;
+ case OciDataType.TimeStamp:
+ dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
+ if (dateTimeDesc == null) {
+ OciErrorInfo info = connection.ErrorHandle.HandleError ();
+ throw new OracleException (info.ErrorCode, info.ErrorMessage);
+ }
+ dateTimeDesc.ErrorHandle = connection.ErrorHandle;
+ bindSize = 11;
+ bindType = OciDataType.TimeStamp;
+ bindOutValue = dateTimeDesc.Handle;
+ bindValue = dateTimeDesc.Handle;
+ useRef = true;
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+
+ dt = DateTime.MinValue;
+ sDate = "";
+ if (isnull)
+ indicator = -1;
+ else if (v is String) {
+ sDate = (string) v;
+ dt = DateTime.Parse (sDate);
+ }
+ else if (v is DateTime)
+ dt = (DateTime) v;
+ else if (v is OracleString) {
+ sDate = (string) v;
+ dt = DateTime.Parse (sDate);
+ }
+ else if (v is OracleDateTime) {
+ OracleDateTime odt = (OracleDateTime) v;
+ dt = (DateTime) odt.Value;
+ }
+ else
+ throw new NotImplementedException ("For OracleType.Timestamp, data type not implemented: " + v.GetType().ToString()); // ?
+
+ short year = (short) dt.Year;
+ byte month = (byte) dt.Month;
+ byte day = (byte) dt.Day;
+ byte hour = (byte) dt.Hour;
+ byte min = (byte) dt.Minute;
+ byte sec = (byte) dt.Second;
+ uint fsec = (uint) dt.Millisecond;
+ string timezone = "";
+ dateTimeDesc.SetDateTime (connection.Session,
+ connection.ErrorHandle,
+ year, month, day, hour, min, sec, fsec,
+ timezone);
+ }
+ break;
+ case OciDataType.Integer:
+ case OciDataType.Float:
+ case OciDataType.Number:
+ bindType = OciDataType.String;
+ indicator = 0;
+ svalue = "\0";
+ // convert value from managed type to type to marshal
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+
+ svalue = null;
+ if(v is IFormattable)
+ svalue = ((IFormattable)v).ToString (null, con.SessionFormatProvider);
+ else if (v is OracleNumber)
+ svalue = ((OracleNumber)v).ToString(con.SessionFormatProvider);
+ else
+ svalue = v.ToString();
+
+ svalue = svalue + "\0";
+
+ rsize = 0;
+ // Get size of buffer
+ OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
+
+ // Fill buffer
+
+ if (direction == ParameterDirection.Input)
+ bindSize = rsize;
+ else
+ bindSize = 30; // need room for output possibly being bigger than the input
+
+ bytes = new byte [bindSize];
+ OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
+ } else {
+ // Output and ReturnValue parameters allocate memory
+ bindSize = 30;
+ bytes = new byte [bindSize];
+ }
+ break;
+ case OciDataType.Long:
+ case OciDataType.LongVarChar:
+ bindType = OciDataType.LongVarChar;
+
+ // FIXME: use piecewise fetching for Long, Clob, Blob, and Long Raw
+ // See http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/oci05bnd.htm#sthref724
+
+ bindSize = Size + 5; // 4 bytes prepended for length, bytes, 1 byte NUL character
+
+ indicator = 0;
+ svalue = "\0";
+ // convert value from managed type to type to marshal
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+
+ svalue = v.ToString () + '\0';
+ }
+
+ bytes = new byte [bindSize];
+ // LONG is only ANSI
+ ASCIIEncoding enc = new ASCIIEncoding ();
+
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+ if (svalue.Length > 0) {
+ byteCount = enc.GetBytes (svalue, 4, svalue.Length, bytes, 0);
+ // LONG VARCHAR prepends a 4-byte length
+ if (byteCount > 0) {
+ byteArrayLen = BitConverter.GetBytes ((uint) byteCount);
+ bytes[0] = byteArrayLen[0];
+ bytes[1] = byteArrayLen[1];
+ bytes[2] = byteArrayLen[2];
+ bytes[3] = byteArrayLen[3];
+ }
+ }
+ }
+ break;
+ case OciDataType.Clob:
+ if (direction == ParameterDirection.Input) {
+ svalue = v.ToString();
+ rsize = 0;
+
+ // Get size of buffer
+ OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
+
+ // Fill buffer
+ bytes = new byte[rsize];
+ OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
+
+ bindType = OciDataType.Long;
+ bindSize = bytes.Length;
+ }
+ else if (direction == ParameterDirection.InputOutput) {
+ // not the exact error that .net 2.0 throws, but this is better
+ throw new NotImplementedException ("Parameters of OracleType.Clob with direction of InputOutput are not supported.");
+ }
+ else {
+ // Output and Return parameters
bindSize = -1;
lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
if (lobLocator == null) {
bindValue = lobLocator.Handle;
lobLocator.ErrorHandle = connection.ErrorHandle;
lobLocator.Service = statement.Service;
+ lobLocator.Environment = connection.Environment;
useRef = true;
- break;
- case OciDataType.RSet: // REF CURSOR
- cursor = IntPtr.Zero;
+ }
+ break;
+ case OciDataType.Blob:
+ if (direction == ParameterDirection.Input) {
+ if (v is byte[]) {
+ bytes = (byte[]) v;
+ bindType = OciDataType.LongRaw;
+ bindSize = bytes.Length;
+ }
+ else if (v is OracleLob) {
+ OracleLob lob = (OracleLob) v;
+ if (lob.LobType == OracleType.Blob) {
+ lobLocator = lob.Locator;
+ bindOutValue = lobLocator.Handle;
+ bindValue = lobLocator.Handle;
+ lobLocator.ErrorHandle = connection.ErrorHandle;
+ lobLocator.Service = connection.ServiceContext;
+ useRef = true;
+ }
+ else
+ throw new NotImplementedException("For OracleType.Blob, data type OracleLob of LobType Clob/NClob is not implemented.");
+ }
+ else
+ throw new NotImplementedException ("For OracleType.Blob, data type not implemented: " + v.GetType().ToString()); // ?
+ }
+ else if (direction == ParameterDirection.InputOutput) {
+ // not the exact error that .net 2.0 throws, but this is better
+ throw new NotImplementedException ("Parameters of OracleType.Blob with direction of InputOutput are not supported.");
+ }
+ else {
+ bindSize = -1;
+ if (value != null && value is OracleLob) {
+ OracleLob blob = (OracleLob) value;
+ if (blob.LobType == OracleType.Blob)
+ if (value != OracleLob.Null) {
+ lobLocator = blob.Locator;
+ byte[] bs = (byte[]) blob.Value;
+ bindSize = bs.Length;
+ }
+ }
+ if (lobLocator == null) {
+ lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
+ if (lobLocator == null) {
+ OciErrorInfo info = connection.ErrorHandle.HandleError ();
+ throw new OracleException (info.ErrorCode, info.ErrorMessage);
+ }
+ }
+ bindOutValue = lobLocator.Handle;
+ bindValue = lobLocator.Handle;
+ lobLocator.ErrorHandle = connection.ErrorHandle;
+ lobLocator.Service = connection.ServiceContext;
+ lobLocator.Environment = connection.Environment;
+ useRef = true;
+ }
+ break;
+ case OciDataType.Raw:
+ case OciDataType.VarRaw:
+ bindType = OciDataType.VarRaw;
+ bindSize = Size + 2; // include 2 bytes prepended to hold the length
+ indicator = 0;
+ bytes = new byte [bindSize];
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+ byteCount = 0;
+ byte[] val;
+ if (dbType == DbType.Guid)
+ val = ((Guid)v).ToByteArray();
+ else
+ val = v as byte[];
+ if (val.Length > 0) {
+ byteCount = val.Length;
+ // LONG VARRAW prepends a 4-byte length
+ if (byteCount > 0) {
+ byteArrayLen = BitConverter.GetBytes ((ushort) byteCount);
+ bytes[0] = byteArrayLen[0];
+ bytes[1] = byteArrayLen[1];
+ Array.ConstrainedCopy (val, 0, bytes, 2, byteCount);
+ }
+ }
+ }
+ break;
+ case OciDataType.LongRaw:
+ case OciDataType.LongVarRaw:
+ bindType = OciDataType.LongVarRaw;
+ bindSize = Size + 4; // include 4 bytes prepended to hold the length
+ indicator = 0;
+ bytes = new byte [bindSize];
+ if (direction == ParameterDirection.Input ||
+ direction == ParameterDirection.InputOutput) {
+ byteCount = 0;
+ byte[] val = v as byte[];
+ if (val.Length > 0) {
+ byteCount = val.Length;
+ // LONG VARRAW prepends a 4-byte length
+ if (byteCount > 0) {
+ byteArrayLen = BitConverter.GetBytes ((uint) byteCount);
+ bytes[0] = byteArrayLen[0];
+ bytes[1] = byteArrayLen[1];
+ bytes[2] = byteArrayLen[2];
+ bytes[3] = byteArrayLen[3];
+ Array.ConstrainedCopy (val, 0, bytes, 4, byteCount);
+ }
+ }
+ }
+ break;
+ case OciDataType.RowIdDescriptor:
+ if (direction == ParameterDirection.Output ||
+ direction == ParameterDirection.InputOutput ||
+ direction == ParameterDirection.ReturnValue) {
+
+ size = 10;
+ bindType = OciDataType.Char;
+ bindSize = size * 2;
+ bindOutValue = OciCalls.AllocateClear (bindSize);
+ bindValue = bindOutValue;
+ } else
+ throw new NotImplementedException("data type RowIdDescriptor as Intput parameters");
+ break;
+ case OciDataType.RSet: // REF CURSOR
+ if (direction == ParameterDirection.Output ||
+ direction == ParameterDirection.InputOutput ||
+ direction == ParameterDirection.ReturnValue) {
+ if (cursor != IntPtr.Zero) {
+ OciCalls.OCIHandleFree (cursor,
+ OciHandleType.Statement);
+ cursor = IntPtr.Zero;
+ }
OciCalls.OCIHandleAlloc (connection.Environment,
out cursor,
OciHandleType.Statement,
0,
IntPtr.Zero);
-
- bindSize = 0;
+ bindSize = 0;
bindType = OciDataType.RSet;
- break;
- default:
- // define other types
- throw new NotImplementedException ();
- }
- bindValue = bindOutValue;
+ } else
+ throw new NotImplementedException ("data type Ref Cursor not implemented for Input parameters");
+ break;
+ default:
+ throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
+ }
}
- else if ((v == DBNull.Value || v == null || isnull == true) && direction == ParameterDirection.Input) {
- indicator = 0;
- bindType = OciDataType.VarChar2;
- bindSize = 0;
- }
- else {
- // TODO: do other data types and oracle data types
- // should I be using IConvertible to convert?
- string sDate = "";
- DateTime dt = DateTime.MinValue;
- if (oracleType == OracleType.Timestamp){
- bindType = OciDataType.TimeStamp;
- bindSize = 11;
- dt = DateTime.MinValue;
- sDate = "";
- if (v is String){
- sDate = (string) v;
- dt = DateTime.Parse (sDate);
- }
- else if (v is DateTime)
- dt = (DateTime) v;
- else if (v is OracleString){
- sDate = (string) v;
- dt = DateTime.Parse (sDate);
- }
- else if (v is OracleDateTime) {
- OracleDateTime odt = (OracleDateTime) v;
- dt = (DateTime) odt.Value;
- }
- else
- throw new NotImplementedException (); // ?
-
- short year = (short) dt.Year;
- byte month = (byte) dt.Month;
- byte day = (byte) dt.Day;
- byte hour = (byte) dt.Hour;
- byte min = (byte) dt.Minute;
- byte sec = (byte) dt.Second;
- uint fsec = (uint) dt.Millisecond;
- string timezone = "";
- dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
- if (dateTimeDesc == null) {
- OciErrorInfo info = connection.ErrorHandle.HandleError ();
- throw new OracleException (info.ErrorCode, info.ErrorMessage);
- }
- dateTimeDesc.ErrorHandle = connection.ErrorHandle;
- dateTimeDesc.SetDateTime (connection.Session,
- connection.ErrorHandle,
- year, month, day, hour, min, sec, fsec,
- timezone);
- useRef = true;
- }
- else if (oracleType == OracleType.DateTime) {
- sDate = "";
- dt = DateTime.MinValue;
- if (v is String) {
- sDate = (string) v;
- dt = DateTime.Parse (sDate);
- }
- else if (v is DateTime)
- dt = (DateTime) v;
- else if (v is OracleString) {
- sDate = (string) v;
- dt = DateTime.Parse (sDate);
- }
- else if (v is OracleDateTime) {
- OracleDateTime odt = (OracleDateTime) v;
- dt = (DateTime) odt.Value;
- }
- else
- throw new NotImplementedException (); // ?
-
- bytes = PackDate (dt);
- bindType = OciDataType.Date;
- bindSize = bytes.Length;
- }
- else if (oracleType == OracleType.Blob) {
- bytes = (byte[]) v;
- bindType = OciDataType.LongRaw;
- bindSize = bytes.Length;
- }
- else if (oracleType == OracleType.Clob) {
- string sv = v.ToString();
- rsize = 0;
-
- // Get size of buffer
- OciCalls.OCIUnicodeToCharSet (statement.Parent, null, sv, out rsize);
-
- // Fill buffer
- bytes = new byte[rsize];
- OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, sv, out rsize);
-
- bindType = OciDataType.Long;
- bindSize = bytes.Length;
- }
- else if (oracleType == OracleType.Raw) {
- byte[] val = v as byte[];
- bindValue = Marshal.AllocHGlobal (val.Length);
- Marshal.Copy (val, 0, bindValue, val.Length);
- bindSize = val.Length;
- }
- else if (oracleType == OracleType.Number
- || oracleType == OracleType.Double
- || oracleType == OracleType.Int32
- || oracleType == OracleType.Int16
- || oracleType == OracleType.Byte
- || oracleType == OracleType.Float) {
- string svalue = null;
- if(v is IFormattable)
- svalue = ((IFormattable)v).ToString (null, con.SessionFormatProvider);
- else
- svalue = v.ToString();
- rsize = 0;
- // Get size of buffer
- OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
-
- // Fill buffer
- bytes = new byte[rsize];
- OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
-
- bindType = OciDataType.VarChar2;
- bindSize = svalue.Length;
- }
- else {
- string svalue = v.ToString () + '\0';
- rsize = 0;
-
- // Get size of buffer
- OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
-
- // Fill buffer
- bytes = new byte[rsize];
- OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
-
- bindType = OciDataType.String;
- bindSize = svalue.Length;
- }
- }
-
- // Now, call the appropriate OCI Bind function
+ // Now, call the appropriate OCI Bind function;
if (useRef == true) {
if (bindType == OciDataType.TimeStamp) {
IntPtr.Zero,
0,
IntPtr.Zero,
- 0);
+ 0);
}
else if (bytes != null) {
status = OciCalls.OCIBindByNameBytes (statement,
out tmpHandle,
connection.ErrorHandle,
ParameterName,
- ParameterName.Length,
+ ParameterName.Length, // FIXME: this should be in bytes!
bindValue,
bindSize,
bindType,
IntPtr.Zero,
0);
}
-
- if (status != 0) {
- OciErrorInfo info = connection.ErrorHandle.HandleError ();
- throw new OracleException (info.ErrorCode, info.ErrorMessage);
- }
+ OciErrorHandle.ThrowExceptionIfError (connection.ErrorHandle, status);
bindHandle.SetHandle (tmpHandle);
}
private void InferOracleType (object value)
{
+ // Should we throw an exception here?
+ if (value == null || value == DBNull.Value)
+ return;
+
Type type = value.GetType ();
- string exception = String.Format ("The parameter data type of {0} is invalid.", type.Name);
+ string exception = String.Format ("The parameter data type of {0} is invalid.", type.FullName);
switch (type.FullName) {
case "System.Int64":
- SetOracleType (OracleType.Number);
+ SetOracleType (OracleType.Number, true);
break;
case "System.Boolean":
case "System.Byte":
- SetOracleType (OracleType.Byte);
+ SetOracleType (OracleType.Byte, true);
break;
case "System.String":
- SetOracleType (OracleType.VarChar);
+ case "System.Data.OracleClient.OracleString":
+ SetOracleType (OracleType.VarChar, true);
break;
+ case "System.Data.OracleClient.OracleDateTime":
case "System.DateTime":
- SetOracleType (OracleType.DateTime);
+ SetOracleType (OracleType.DateTime, true);
break;
case "System.Decimal":
- SetOracleType (OracleType.Number);
- //scale = ((decimal) value).Scale;
+ case "System.Data.OracleClient.OracleNumber":
+ SetOracleType (OracleType.Number, true);
break;
case "System.Double":
- SetOracleType (OracleType.Double);
+ SetOracleType (OracleType.Double, true);
break;
case "System.Byte[]":
case "System.Guid":
- SetOracleType (OracleType.Raw);
+ SetOracleType (OracleType.Raw, true);
break;
case "System.Int32":
- SetOracleType (OracleType.Int32);
+ SetOracleType (OracleType.Int32, true);
break;
case "System.Single":
- SetOracleType (OracleType.Float);
+ SetOracleType (OracleType.Float, true);
break;
case "System.Int16":
- SetOracleType (OracleType.Int16);
+ SetOracleType (OracleType.Int16, true);
+ break;
+ case "System.DBNull":
+ break; //unable to guess type
+ case "System.Data.OracleClient.OracleLob":
+ SetOracleType (((OracleLob) value).LobType, true);
break;
default:
throw new ArgumentException (exception);
private int InferSize ()
{
int newSize = 0;
-
+
switch (ociType) {
case OciDataType.VarChar2:
case OciDataType.String:
case OciDataType.OciString:
case OciDataType.Long:
case OciDataType.LongVarChar:
+ if (sizeManuallySet == true)
+ return size;
if (value == null || value == DBNull.Value)
newSize = 0;
else
break;
case OciDataType.TimeStamp:
newSize = 11;
- break;
+ break;
case OciDataType.Blob:
case OciDataType.Clob:
case OciDataType.RSet: // REF CURSOR
newSize = -1;
- break;
+ break;
+ case OciDataType.Raw:
+ if (dbType == DbType.Guid)
+ newSize = ((Guid)value).ToByteArray().Length;
+ else
+ newSize = (value as byte[]).Length;
+ break;
default:
if (value == null || value == DBNull.Value)
newSize = 0;
break;
case DbType.StringFixedLength:
oracleType = OracleType.NChar;
- ociType = OciDataType.Char;
+ ociType = OciDataType.Char;
+
break;
default:
throw new ArgumentException (exception);
}
dbType = type;
-
}
- private void SetOracleType (OracleType type)
+ private void SetOracleType (OracleType type, bool inferring)
{
- FreeHandle ();
+ Type valType;
+ FreeHandle ();
+
+ if (value == null)
+ valType = typeof(System.DBNull);
+ else
+ valType = value.GetType ();
+
string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
switch (type) {
case OracleType.BFile:
break;
case OracleType.LongRaw:
case OracleType.Raw:
- dbType = DbType.Binary;
+ if (valType.FullName == "System.Guid")
+ dbType = DbType.Guid;
+ else
+ dbType = DbType.Binary;
ociType = OciDataType.Raw;
break;
case OracleType.Byte:
throw new ArgumentException (exception);
}
- oracleType = type;
+ if (!oracleTypeSet || !inferring )
+ oracleType = type;
}
+#if NET_2_0
+ public override void ResetDbType ()
+ {
+ ResetOracleType ();
+ }
+
+ public void ResetOracleType ()
+ {
+ oracleTypeSet = false;
+ InferOracleType (value);
+ }
+#endif // NET_2_0
+
public override string ToString ()
{
return ParameterName;
}
- private void GetOutValue (OracleCommand cmd)
+ private void GetOutValue (OracleCommand cmd)
{
// used to update the parameter value
// for Output, the output of InputOutput, and Return parameters
if (indicator == -1)
return;
- byte[] buffer = null;
- object tmp = null;
+ int rsize = 0;
+ IntPtr env = IntPtr.Zero;
+ StringBuilder ret = null;
+
+ // FIXME: redo all types - see how Char, Number, and Date are done
+ // here and in Bind()
switch (ociType) {
case OciDataType.VarChar2:
case OciDataType.CharZ:
case OciDataType.OciString:
case OciDataType.RowIdDescriptor:
- buffer = new byte [Size];
- Marshal.Copy (bindOutValue, buffer, 0, Size);
-
// Get length of returned string
- int rsize = 0;
- IntPtr env = cmd.Connection.Environment;
- OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
-
+ rsize = 0;
+ env = cmd.Connection.Environment;
+ OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
+
// Get string
- StringBuilder ret = new StringBuilder(rsize);
- OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
-
+ ret = new StringBuilder(rsize);
+ OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
+
value = ret.ToString ();
break;
+ case OciDataType.Long:
+ case OciDataType.LongVarChar:
+ int longSize = 0;
+ if (BitConverter.IsLittleEndian)
+ longSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
+ else
+ longSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
+
+ ASCIIEncoding encoding = new ASCIIEncoding ();
+ value = encoding.GetString (bytes, 4, longSize);
+ encoding = null;
+ break;
+ case OciDataType.LongRaw:
+ case OciDataType.LongVarRaw:
+ int longrawSize = 0;
+ if (BitConverter.IsLittleEndian)
+ longrawSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
+ else
+ longrawSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
+
+ byte[] longraw_buffer = new byte [longrawSize];
+ Array.ConstrainedCopy (bytes, 4, longraw_buffer, 0, longrawSize);
+ value = longraw_buffer;
+ break;
+ case OciDataType.Raw:
+ case OciDataType.VarRaw:
+ int rawSize = 0;
+ if (BitConverter.IsLittleEndian)
+ rawSize = (int) BitConverter.ToInt16 (new byte [] {bytes [0], bytes [1]}, 0);
+ else
+ rawSize = (int) BitConverter.ToInt16 (new byte [] {bytes [1], bytes [0]}, 0);
+
+ byte[] raw_buffer = new byte [rawSize];
+ Array.ConstrainedCopy (bytes, 2, raw_buffer, 0, rawSize);
+ value = raw_buffer;
+ break;
case OciDataType.Integer:
case OciDataType.Number:
case OciDataType.Float:
- tmp = Marshal.PtrToStringAnsi (bindOutValue, bindSize);
- if (tmp != null)
- value = Decimal.Parse (String.Copy ((string) tmp), cmd.Connection.SessionFormatProvider);
+ rsize = 0;
+ env = cmd.Connection.Environment;
+ OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
+
+ // Get string
+ ret = new StringBuilder(rsize);
+ OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
+
+ // if not empty, parse string as a decimal using session format
+ if (ret.Length > 0) {
+ switch (dbType) {
+ case DbType.UInt16:
+ value = UInt16.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
+ break;
+ case DbType.UInt32:
+ value = UInt32.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
+ break;
+ case DbType.Int16:
+ value = Int16.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
+ break;
+ case DbType.Int32:
+ value = Int32.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
+ break;
+ default:
+ value = Decimal.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
+ break;
+ }
+ }
break;
case OciDataType.TimeStamp:
value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
break;
case OciDataType.Date:
- value = UnpackDate (bindOutValue);
- break;
+ value = UnpackDate (bytes);
+ break;
case OciDataType.Blob:
case OciDataType.Clob:
- OracleLob lob = new OracleLob (lobLocator, ociType);
- lob.connection = connection;
- value = lob;
+ if (value != null && value is OracleLob && value != OracleLob.Null) {
+ OracleLob lob2 = (OracleLob) value;
+ lob2.connection = connection;
+ }
+ else {
+ OracleLob lob = new OracleLob (lobLocator, ociType);
+ lob.connection = connection;
+ value = lob;
+ }
break;
- case OciDataType.RSet: // REF CURSOR
- OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.Environment, cursor);
- cursorStatement.ErrorHandle = cmd.ErrorHandle;
- cursorStatement.Command = cmd.Connection.CreateCommand ();
- cursorStatement.SetupRefCursorResult ();
+ case OciDataType.RSet: // REF CURSOR
+ OciStatementHandle cursorStatement = GetOutRefCursor (cmd);
value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
- cursor = IntPtr.Zero;
- cursorStatement = null;
break;
default:
- throw new NotImplementedException ();
+ throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
}
- tmp = null;
- buffer = null;
}
- internal void Update (OracleCommand cmd)
+ internal OciStatementHandle GetOutRefCursor (OracleCommand cmd)
+ {
+ OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.ServiceContext, cursor);
+
+ cursorStatement.ErrorHandle = cmd.ErrorHandle;
+ cursorStatement.Command = cmd;
+ cursorStatement.SetupRefCursorResult (cmd.Connection);
+ cursorStatement.Service = cmd.Connection.ServiceContext;
+ cursor = IntPtr.Zero;
+ return cursorStatement;
+ }
+
+ internal void Update (OracleCommand cmd)
{
if (Direction != ParameterDirection.Input)
GetOutValue (cmd);
-
+
FreeHandle ();
}
- internal void FreeHandle ()
+ internal void FreeHandle ()
{
switch (ociType) {
case OciDataType.Clob:
case OciDataType.Blob:
lobLocator = null;
break;
- case OciDataType.Raw:
- Marshal.FreeHGlobal (bindValue);
+ case OciDataType.TimeStamp:
break;
- case OciDataType.TimeStamp:
- break;
- default:
- Marshal.FreeHGlobal (bindOutValue);
- break;
- }
+ default:
+ Marshal.FreeHGlobal (bindOutValue);
+ break;
+ }
bindOutValue = IntPtr.Zero;
bindValue = IntPtr.Zero;
bindHandle = null;
- errorHandle = null;
connection = null;
}
// copied from OciDefineHandle
[MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
- internal DateTime UnpackDate (IntPtr dateValue)
+ private DateTime UnpackDate (byte[] bytes)
{
- byte century = Marshal.ReadByte (dateValue, 0);
- byte year = Marshal.ReadByte (dateValue, 1);
- byte month = Marshal.ReadByte (dateValue, 2);
- byte day = Marshal.ReadByte (dateValue, 3);
- byte hour = Marshal.ReadByte (dateValue, 4);
- byte minute = Marshal.ReadByte (dateValue, 5);
- byte second = Marshal.ReadByte (dateValue, 6);
+ byte century = bytes [0];
+ byte year = bytes [1];
+ byte month = bytes [2];
+ byte day = bytes [3];
+ byte hour = bytes [4];
+ byte minute = bytes [5];
+ byte second = bytes [6];
+
return new DateTime ((century - 100) * 100 + (year - 100),
month,
}
- internal byte[] PackDate (DateTime dateValue)
+ private byte[] PackDate (DateTime dateValue)
{
byte[] buffer = new byte[7];
}
}
}
-