//
// Authors:
// Tim Coleman <tim@timcoleman.com>
+// Daniel Moragn <danielmorgan@verizon.net>
+// Hubert FONGARNAND <informatique.internet@fiducial.fr>
//
// Copyright (C) Tim Coleman , 2003
+// Copyright (C) Daniel Morgan, 2005
+// Copyright (C) Hubert FONGARNAND, 2005
//
// Licensed under the MIT/X11 License.
//
using System.Data.OracleClient.Oci;
using System.Globalization;
using System.Runtime.InteropServices;
+using System.Text;
namespace System.Data.OracleClient {
[TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
DbType dbType = DbType.AnsiString;
int offset = 0;
bool sizeSet = false;
- object value = null;
+ object value = DBNull.Value;
+ OciLobLocator lobLocator = null; // only if Blob or Clob
+ IntPtr bindOutValue = IntPtr.Zero;
+ OciDateTimeDescriptor dateTimeDesc = null;
OracleParameterCollection container = null;
OciBindHandle bindHandle;
+ OciErrorHandle errorHandle;
+ OracleConnection connection;
+ byte[] bytes = null;
+ IntPtr bindValue = IntPtr.Zero;
+
+ short indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
+ int bindSize = 0;
#endregion // Fields
#region Constructors
+ // constructor for cloning the object
+ internal OracleParameter (OracleParameter value) {
+ this.name = value.name;
+ this.oracleType = value.oracleType;
+ this.ociType = value.ociType;
+ this.size = value.size;
+ this.direction = value.direction;
+ this.isNullable = value.isNullable;
+ this.precision = value.precision;
+ this.scale = value.scale;
+ this.srcColumn = value.srcColumn;
+ this.srcVersion = value.srcVersion;
+ this.dbType = value.dbType;
+ this.offset = value.offset;
+ this.sizeSet = value.sizeSet;
+ this.value = value.value;
+ this.lobLocator = value.lobLocator;
+ }
+
public OracleParameter ()
: this (String.Empty, OracleType.VarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
{
set { srcColumn = value; }
}
- [DefaultValue (DataRowVersion.Current)]
+ [DefaultValue ("Current")]
public DataRowVersion SourceVersion {
get { return srcVersion; }
set { srcVersion = value; }
private void AssertSizeIsSet ()
{
- if (!sizeSet)
- throw new Exception ("Size must be set.");
+ switch (ociType) {
+ case OciDataType.VarChar2:
+ case OciDataType.String:
+ case OciDataType.VarChar:
+ case OciDataType.Char:
+ case OciDataType.CharZ:
+ case OciDataType.OciString:
+ if (!sizeSet)
+ throw new Exception ("Size must be set.");
+ break;
+ default:
+ break;
+ }
}
- internal void Bind (OciStatementHandle statement, OracleConnection connection)
+ internal void Bind (OciStatementHandle statement, OracleConnection connection)
{
+ errorHandle = connection.ErrorHandle;
+
if (bindHandle == null)
bindHandle = new OciBindHandle ((OciHandle) statement);
if (!sizeSet)
size = InferSize ();
+ bindSize = size;
+ object v = value;
int status = 0;
- int indicator = 0;
OciDataType bindType = ociType;
- IntPtr bindValue = IntPtr.Zero;
- int bindSize = size;
-
- if (value == DBNull.Value)
- indicator = -1;
+ int rsize = 0;
+
+ // 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
+ 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);
+ break;
+ case OciDataType.RowIdDescriptor:
+ size = 10;
+ bindType = OciDataType.Char;
+ bindSize = size * 2;
+ bindOutValue = Marshal.AllocHGlobal (bindSize);
+ break;
+ case OciDataType.Date:
+ bindSize = 7;
+ bindType = OciDataType.Date;
+ bindOutValue = Marshal.AllocHGlobal (bindSize);
+ 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;
+ break;
+ case OciDataType.Number:
+ bindSize = 22;
+ bindType = OciDataType.Char;
+ bindOutValue = Marshal.AllocHGlobal (bindSize);
+ 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;
+ break;
+ case OciDataType.Blob:
+ case OciDataType.Clob:
+ bindSize = -1;
+ 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;
+ lobLocator.ErrorHandle = connection.ErrorHandle;
+ lobLocator.Service = statement.Service;
+ break;
+ default:
+ // define other types
+ throw new NotImplementedException ();
+ }
+ bindValue = bindOutValue;
+ }
+ else if (v == DBNull.Value || v == null) {
+ indicator = 0;
+ bindType = OciDataType.VarChar2;
+ bindSize = 0;
+ }
else {
- bindType = OciDataType.VarChar2; // FIXME
- bindValue = Marshal.StringToHGlobalAnsi (value.ToString ());
- bindSize = value.ToString ().Length;
+ // 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
+ 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);
+ }
+ 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 = (string) v;
+ 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 {
+ string 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 = v.ToString ().Length;
+ }
}
- status = OciCalls.OCIBindByName (statement,
- out tmpHandle,
- connection.ErrorHandle,
- ParameterName,
- ParameterName.Length,
- bindValue,
- bindSize,
- bindType,
- indicator,
- IntPtr.Zero,
- IntPtr.Zero,
- 0,
- IntPtr.Zero,
- 0);
+ if (dateTimeDesc != null) {
+ bindValue = dateTimeDesc.Handle;
+ status = OciCalls.OCIBindByNameRef (statement,
+ out tmpHandle,
+ connection.ErrorHandle,
+ ParameterName,
+ ParameterName.Length,
+ ref bindValue,
+ bindSize,
+ bindType,
+ ref indicator,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ 0,
+ IntPtr.Zero,
+ 0);
+ }
+ else if (bytes != null) {
+ status = OciCalls.OCIBindByNameBytes (statement,
+ out tmpHandle,
+ connection.ErrorHandle,
+ ParameterName,
+ ParameterName.Length,
+ bytes,
+ bindSize,
+ bindType,
+ ref indicator,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ 0,
+ IntPtr.Zero,
+ 0);
+ }
+ else {
+ status = OciCalls.OCIBindByName (statement,
+ out tmpHandle,
+ connection.ErrorHandle,
+ ParameterName,
+ ParameterName.Length,
+ bindValue,
+ bindSize,
+ bindType,
+ ref indicator,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ 0,
+ IntPtr.Zero,
+ 0);
+ }
if (status != 0) {
OciErrorInfo info = connection.ErrorHandle.HandleError ();
bindHandle.SetHandle (tmpHandle);
}
- [MonoTODO]
object ICloneable.Clone ()
{
- throw new NotImplementedException ();
+ return new OracleParameter(this);
}
private void InferOracleType (object value)
case "System.String":
SetOracleType (OracleType.VarChar);
break;
- case "System.DataType":
+ case "System.DateTime":
SetOracleType (OracleType.DateTime);
break;
case "System.Decimal":
}
}
- [MonoTODO ("different size depending on type.")]
private int InferSize ()
{
- return value.ToString ().Length;
+ int newSize = 0;
+
+ switch (ociType) {
+ case OciDataType.VarChar2:
+ case OciDataType.String:
+ case OciDataType.VarChar:
+ case OciDataType.Char:
+ case OciDataType.CharZ:
+ case OciDataType.OciString:
+ case OciDataType.Long:
+ case OciDataType.LongVarChar:
+ if (value == null || value == DBNull.Value)
+ newSize = 0;
+ else
+ newSize = value.ToString ().Length;
+ break;
+ case OciDataType.RowIdDescriptor:
+ newSize = 10;
+ break;
+ case OciDataType.Integer:
+ case OciDataType.Number:
+ case OciDataType.Float:
+ newSize = 22;
+ break;
+ case OciDataType.Date:
+ newSize = 7;
+ break;
+ case OciDataType.TimeStamp:
+ newSize = 11;
+ break;
+ case OciDataType.Blob:
+ case OciDataType.Clob:
+ newSize = -1;
+ break;
+ default:
+ if (value == null || value == DBNull.Value)
+ newSize = 0;
+ else
+ newSize = value.ToString ().Length;
+ break;
+ }
+
+ sizeSet = true;
+
+ return newSize;
}
private void SetDbType (DbType type)
switch (type) {
case OracleType.BFile:
case OracleType.Blob:
+ dbType = DbType.Binary;
+ ociType = OciDataType.Blob;
+ break;
case OracleType.LongRaw:
case OracleType.Raw:
dbType = DbType.Binary;
break;
case OracleType.Byte:
dbType = DbType.Byte;
- ociType = OciDataType.Integer;
+ ociType = OciDataType.Number;
break;
case OracleType.Char:
- dbType = DbType.AnsiStringFixedLength;
+ dbType = DbType.AnsiString;
ociType = OciDataType.Char;
break;
case OracleType.Clob:
+ dbType = DbType.AnsiString;
+ ociType = OciDataType.Clob;
+ break;
case OracleType.LongVarChar:
case OracleType.RowId:
case OracleType.VarChar:
break;
case OracleType.Cursor:
case OracleType.IntervalDayToSecond:
- dbType = DbType.Object;
- ociType = OciDataType.Blob;
+ dbType = DbType.AnsiStringFixedLength;
+ ociType = OciDataType.Char;
break;
- case OracleType.DateTime:
case OracleType.Timestamp:
case OracleType.TimestampLocal:
case OracleType.TimestampWithTZ:
dbType = DbType.DateTime;
- ociType = OciDataType.Char;
+ ociType = OciDataType.TimeStamp;
+ break;
+ case OracleType.DateTime:
+ dbType = DbType.DateTime;
+ ociType = OciDataType.Date;
break;
case OracleType.Double:
dbType = DbType.Double;
- ociType = OciDataType.Float;
+ ociType = OciDataType.Number;
break;
case OracleType.Float:
dbType = DbType.Single;
- ociType = OciDataType.Float;
+ ociType = OciDataType.Number;
break;
case OracleType.Int16:
dbType = DbType.Int16;
- ociType = OciDataType.Integer;
+ ociType = OciDataType.Number;
break;
case OracleType.Int32:
case OracleType.IntervalYearToMonth:
dbType = DbType.Int32;
- ociType = OciDataType.Integer;
+ ociType = OciDataType.Number;
break;
case OracleType.NChar:
dbType = DbType.StringFixedLength;
case OracleType.NClob:
case OracleType.NVarChar:
dbType = DbType.String;
- ociType = OciDataType.VarChar;
+ ociType = OciDataType.Char;
break;
case OracleType.Number:
dbType = DbType.VarNumeric;
break;
case OracleType.SByte:
dbType = DbType.SByte;
- ociType = OciDataType.Integer;
+ ociType = OciDataType.Number;
break;
case OracleType.UInt16:
dbType = DbType.UInt16;
- ociType = OciDataType.Integer;
+ ociType = OciDataType.Number;
break;
case OracleType.UInt32:
dbType = DbType.UInt32;
- ociType = OciDataType.Integer;
+ ociType = OciDataType.Number;
break;
default:
throw new ArgumentException (exception);
return ParameterName;
}
+ internal void Update (OracleCommand cmd)
+ {
+ // used to update the parameter value
+ // for Output, the output of InputOutput, and Return parameters
+ value = DBNull.Value;
+ if (indicator == -1)
+ return;
+
+ byte[] buffer = null;
+ object tmp = null;
+
+ switch (ociType) {
+ case OciDataType.VarChar2:
+ case OciDataType.String:
+ case OciDataType.VarChar:
+ case OciDataType.Char:
+ 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);
+
+ // Get string
+ StringBuilder ret = new StringBuilder(rsize);
+ OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
+
+ value = ret.ToString ();
+ 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));
+ break;
+ case OciDataType.TimeStamp:
+ value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
+ break;
+ case OciDataType.Date:
+ value = UnpackDate (bindOutValue);
+ break;
+ case OciDataType.Blob:
+ case OciDataType.Clob:
+ OracleLob lob = new OracleLob (lobLocator, ociType);
+ lob.connection = connection;
+ value = lob;
+ break;
+ default:
+ throw new NotImplementedException ();
+ }
+ }
+
+ // copied from OciDefineHandle
+ [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
+ internal DateTime UnpackDate (IntPtr dateValue)
+ {
+ 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);
+
+ return new DateTime ((century - 100) * 100 + (year - 100),
+ month,
+ day,
+ hour - 1,
+ minute - 1,
+ second - 1);
+
+ }
+
+ internal byte[] PackDate (DateTime dateValue)
+ {
+ byte[] buffer = new byte[7];
+
+ buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
+ buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
+ buffer[2] = (byte)dateValue.Month;
+ buffer[3] = (byte)dateValue.Day;
+ buffer[4] = (byte)(dateValue.Hour+1);
+ buffer[5] = (byte)(dateValue.Minute+1);
+ buffer[6] = (byte)(dateValue.Second+1);
+
+ return buffer;
+ }
+
#endregion // Methods
internal sealed class OracleParameterConverter : ExpandableObjectConverter