Files
linux-packaging-mono/mcs/class/System.Data.OracleClient/System.Data.OracleClient/OracleParameter.cs
Jo Shields 3c1f479b9d Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
2015-04-07 09:35:12 +01:00

1431 lines
39 KiB
C#

//
// OracleParameter.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:
// Tim Coleman <tim@timcoleman.com>
// 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, 2008, 2009
// Copyright (C) Hubert FONGARNAND, 2005
// Copyright (C) Novell Inc, 2009
//
// Licensed under the MIT/X11 License.
//
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.SqlTypes;
using System.Data.OracleClient.Oci;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
namespace System.Data.OracleClient
{
[TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
public sealed class OracleParameter :
DbParameter, IDbDataParameter, ICloneable,
IDisposable
{
#region Fields
string name;
OracleType oracleType = OracleType.VarChar;
OciDataType ociType;
int size;
ParameterDirection direction = ParameterDirection.Input;
bool isNullable;
byte precision;
byte scale;
string srcColumn;
bool sourceColumnNullMapping;
DataRowVersion srcVersion;
DbType dbType = DbType.AnsiString;
int offset;
bool sizeSet;
bool oracleTypeSet;
object value = DBNull.Value;
OciLobLocator lobLocator; // only if Blob or Clob
IntPtr bindOutValue = IntPtr.Zero;
IntPtr indicator = IntPtr.Zero;
OciDateTimeDescriptor dateTimeDesc;
IntPtr cursor = IntPtr.Zero;
OracleParameterCollection container;
OciBindHandle bindHandle;
OracleConnection connection;
byte[] bytes;
IntPtr bindValue = IntPtr.Zero;
bool useRef;
OciDataType bindType;
int bindSize;
bool sizeManuallySet;
#endregion // Fields
#region Constructors
// constructor for cloning the object
private 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;
this.oracleTypeSet = value.oracleTypeSet;
this.indicator = OciCalls.AllocateClear (sizeof(short));
}
public OracleParameter ()
{
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;
this.indicator = OciCalls.AllocateClear (sizeof(short));
}
public OracleParameter (string name, object value)
{
this.name = name;
this.value = value;
srcColumn = string.Empty;
SourceVersion = DataRowVersion.Current;
InferOracleType (value);
this.indicator = OciCalls.AllocateClear (sizeof(short));
// Find the OciType before inferring for the size
if (value != null && value != DBNull.Value) {
this.sizeSet = true;
this.size = InferSize ();
}
}
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 oracleType, int size)
: this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, String.Empty, 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 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;
this.indicator = OciCalls.AllocateClear (sizeof(short));
}
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;
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;
this.indicator = OciCalls.AllocateClear (sizeof(short));
}
~OracleParameter ()
{
Dispose(false);
}
#endregion // Constructors
#region Properties
internal OracleParameterCollection Container {
get { return container; }
set { container = value; }
}
internal short Indicator {
get { return (Marshal.ReadInt16(indicator)); }
set { Marshal.WriteInt16(indicator, value); }
}
public
override
DbType DbType {
get { return dbType; }
set { SetDbType (value); }
}
[RefreshProperties (RefreshProperties.All)]
public
override
ParameterDirection Direction {
get { return direction; }
set {
direction = value;
if (this.size > 0 && direction == ParameterDirection.Output)
this.sizeSet = true;
}
}
public
override
bool IsNullable {
get { return isNullable; }
set { isNullable = value; }
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
[Browsable (false)]
public int Offset {
get { return offset; }
set { offset = value; }
}
[DefaultValue (OracleType.VarChar)]
[RefreshProperties (RefreshProperties.All)]
[DbProviderSpecificTypeProperty (true)]
public OracleType OracleType {
get { return oracleType; }
set {
oracleTypeSet = true;
SetOracleType (value, false);
}
}
public
override
string ParameterName {
get {
if (name == null)
return string.Empty;
return name;
}
set { name = value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete("Set the precision of a decimal use the Math classes.")]
public byte Precision {
get { return precision; }
set { /* NO EFFECT*/ }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete("Set the precision of a decimal use the Math classes.")]
public byte Scale {
get { return scale; }
set { /* NO EFFECT*/ }
}
public
override
int Size {
get { return size; }
set {
sizeSet = true;
size = value;
sizeManuallySet = true;
}
}
public
override
string SourceColumn {
get { return srcColumn; }
set { srcColumn = value; }
}
[MonoTODO]
public override bool SourceColumnNullMapping {
get { return sourceColumnNullMapping; }
set { sourceColumnNullMapping = value; }
}
public
override
DataRowVersion SourceVersion {
get { return srcVersion; }
set { srcVersion = value; }
}
[RefreshProperties (RefreshProperties.All)]
[TypeConverter (typeof(StringConverter))]
public
override
object Value {
get { return this.value; }
set {
this.value = value;
if (!oracleTypeSet)
InferOracleType (value);
if (value != null && value != DBNull.Value) {
this.size = InferSize ();
this.sizeSet = true;
}
}
}
#endregion // Properties
#region Methods
private void AssertSizeIsSet ()
{
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 con, uint pos)
{
connection = con;
if (bindHandle == null)
bindHandle = new OciBindHandle ((OciHandle) statement);
IntPtr tmpHandle = bindHandle.Handle;
if (Direction != ParameterDirection.Input)
AssertSizeIsSet ();
if (!sizeSet)
size = InferSize ();
bindSize = size;
object v = value;
int status = 0;
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) {
if (v == null)
isnull = true;
else if (v is DBNull)
isnull = true;
else {
INullable mynullable = v as INullable;
if (mynullable != null)
isnull = mynullable.IsNull;
}
}
if (isnull == true && direction == ParameterDirection.Input) {
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.String;
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);
}
// 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) {
OciErrorInfo info = connection.ErrorHandle.HandleError ();
throw new OracleException (info.ErrorCode, info.ErrorMessage);
}
bindOutValue = lobLocator.Handle;
bindValue = lobLocator.Handle;
lobLocator.ErrorHandle = connection.ErrorHandle;
lobLocator.Service = statement.Service;
lobLocator.Environment = connection.Environment;
useRef = true;
}
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;
bindType = OciDataType.RSet;
} else
throw new NotImplementedException ("data type Ref Cursor not implemented for Input parameters");
break;
default:
throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
}
}
// Now, call the appropriate OCI Bind function;
if (useRef == true) {
if (bindType == OciDataType.TimeStamp) {
bindValue = dateTimeDesc.Handle;
status = OciCalls.OCIBindByNameRef (statement,
out tmpHandle,
connection.ErrorHandle,
ParameterName,
ParameterName.Length,
ref bindValue,
bindSize,
bindType,
indicator,
IntPtr.Zero,
IntPtr.Zero,
0,
IntPtr.Zero,
0);
}
else {
status = OciCalls.OCIBindByNameRef (statement,
out tmpHandle,
connection.ErrorHandle,
ParameterName,
ParameterName.Length,
ref bindValue,
bindSize,
bindType,
indicator,
IntPtr.Zero,
IntPtr.Zero,
0,
IntPtr.Zero,
0);
}
}
else if (bindType == OciDataType.RSet) {
status = OciCalls.OCIBindByNameRef (statement,
out tmpHandle,
connection.ErrorHandle,
ParameterName,
ParameterName.Length,
ref cursor,
bindSize,
bindType,
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,
indicator,
IntPtr.Zero,
IntPtr.Zero,
0,
IntPtr.Zero,
0);
}
else {
status = OciCalls.OCIBindByName (statement,
out tmpHandle,
connection.ErrorHandle,
ParameterName,
ParameterName.Length, // FIXME: this should be in bytes!
bindValue,
bindSize,
bindType,
indicator,
IntPtr.Zero,
IntPtr.Zero,
0,
IntPtr.Zero,
0);
}
OciErrorHandle.ThrowExceptionIfError (connection.ErrorHandle, status);
bindHandle.SetHandle (tmpHandle);
}
object ICloneable.Clone ()
{
return new OracleParameter(this);
}
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.FullName);
switch (type.FullName) {
case "System.Int64":
SetOracleType (OracleType.Number, true);
break;
case "System.Boolean":
case "System.Byte":
SetOracleType (OracleType.Byte, true);
break;
case "System.String":
case "System.Data.OracleClient.OracleString":
SetOracleType (OracleType.VarChar, true);
break;
case "System.Data.OracleClient.OracleDateTime":
case "System.DateTime":
SetOracleType (OracleType.DateTime, true);
break;
case "System.Decimal":
case "System.Data.OracleClient.OracleNumber":
SetOracleType (OracleType.Number, true);
break;
case "System.Double":
SetOracleType (OracleType.Double, true);
break;
case "System.Byte[]":
case "System.Guid":
SetOracleType (OracleType.Raw, true);
break;
case "System.Int32":
SetOracleType (OracleType.Int32, true);
break;
case "System.Single":
SetOracleType (OracleType.Float, true);
break;
case "System.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.VarChar:
case OciDataType.Char:
case OciDataType.CharZ:
case OciDataType.OciString:
case OciDataType.Long:
case OciDataType.LongVarChar:
if (sizeManuallySet == true)
return size;
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:
case OciDataType.RSet: // REF CURSOR
newSize = -1;
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;
else
newSize = value.ToString ().Length;
break;
}
sizeSet = true;
return newSize;
}
private void SetDbType (DbType type)
{
string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
switch (type) {
case DbType.AnsiString:
oracleType = OracleType.VarChar;
ociType = OciDataType.VarChar;
break;
case DbType.AnsiStringFixedLength:
oracleType = OracleType.Char;
ociType = OciDataType.Char;
break;
case DbType.Binary:
case DbType.Guid:
oracleType = OracleType.Raw;
ociType = OciDataType.Raw;
break;
case DbType.Boolean:
case DbType.Byte:
oracleType = OracleType.Byte;
ociType = OciDataType.Integer;
break;
case DbType.Currency:
case DbType.Decimal:
case DbType.Int64:
oracleType = OracleType.Number;
ociType = OciDataType.Number;
break;
case DbType.Date:
case DbType.DateTime:
case DbType.Time:
oracleType = OracleType.DateTime;
ociType = OciDataType.Char;
break;
case DbType.Double:
oracleType = OracleType.Double;
ociType = OciDataType.Float;
break;
case DbType.Int16:
oracleType = OracleType.Int16;
ociType = OciDataType.Integer;
break;
case DbType.Int32:
oracleType = OracleType.Int32;
ociType = OciDataType.Integer;
break;
case DbType.Object:
oracleType = OracleType.Blob;
ociType = OciDataType.Blob;
break;
case DbType.Single:
oracleType = OracleType.Float;
ociType = OciDataType.Float;
break;
case DbType.String:
oracleType = OracleType.NVarChar;
ociType = OciDataType.VarChar;
break;
case DbType.StringFixedLength:
oracleType = OracleType.NChar;
ociType = OciDataType.Char;
break;
default:
throw new ArgumentException (exception);
}
dbType = type;
}
private void SetOracleType (OracleType type, bool inferring)
{
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:
case OracleType.Blob:
dbType = DbType.Binary;
ociType = OciDataType.Blob;
break;
case OracleType.LongRaw:
case OracleType.Raw:
if (valType.FullName == "System.Guid")
dbType = DbType.Guid;
else
dbType = DbType.Binary;
ociType = OciDataType.Raw;
break;
case OracleType.Byte:
dbType = DbType.Byte;
ociType = OciDataType.Number;
break;
case OracleType.Char:
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:
dbType = DbType.AnsiString;
ociType = OciDataType.VarChar;
break;
case OracleType.Cursor: // REF CURSOR
ociType = OciDataType.RSet;
dbType = DbType.Object;
break;
case OracleType.IntervalDayToSecond:
dbType = DbType.AnsiStringFixedLength;
ociType = OciDataType.Char;
break;
case OracleType.Timestamp:
case OracleType.TimestampLocal:
case OracleType.TimestampWithTZ:
dbType = DbType.DateTime;
ociType = OciDataType.TimeStamp;
break;
case OracleType.DateTime:
dbType = DbType.DateTime;
ociType = OciDataType.Date;
break;
case OracleType.Double:
dbType = DbType.Double;
ociType = OciDataType.Number;
break;
case OracleType.Float:
dbType = DbType.Single;
ociType = OciDataType.Number;
break;
case OracleType.Int16:
dbType = DbType.Int16;
ociType = OciDataType.Number;
break;
case OracleType.Int32:
case OracleType.IntervalYearToMonth:
dbType = DbType.Int32;
ociType = OciDataType.Number;
break;
case OracleType.NChar:
dbType = DbType.StringFixedLength;
ociType = OciDataType.Char;
break;
case OracleType.NClob:
case OracleType.NVarChar:
dbType = DbType.String;
ociType = OciDataType.Char;
break;
case OracleType.Number:
dbType = DbType.VarNumeric;
ociType = OciDataType.Number;
break;
case OracleType.SByte:
dbType = DbType.SByte;
ociType = OciDataType.Number;
break;
case OracleType.UInt16:
dbType = DbType.UInt16;
ociType = OciDataType.Number;
break;
case OracleType.UInt32:
dbType = DbType.UInt32;
ociType = OciDataType.Number;
break;
default:
throw new ArgumentException (exception);
}
if (!oracleTypeSet || !inferring )
oracleType = type;
}
public override void ResetDbType ()
{
ResetOracleType ();
}
public void ResetOracleType ()
{
oracleTypeSet = false;
InferOracleType (value);
}
public override string ToString ()
{
return ParameterName;
}
private void GetOutValue (OracleCommand cmd)
{
// used to update the parameter value
// for Output, the output of InputOutput, and Return parameters
value = DBNull.Value;
if (Indicator == -1)
return;
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.String:
case OciDataType.VarChar:
case OciDataType.Char:
case OciDataType.CharZ:
case OciDataType.OciString:
case OciDataType.RowIdDescriptor:
// Get length of returned string
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);
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:
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 (bytes);
break;
case OciDataType.Blob:
case OciDataType.Clob:
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 = GetOutRefCursor (cmd);
value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
break;
default:
throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
}
}
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 ()
{
switch (ociType) {
case OciDataType.Clob:
case OciDataType.Blob:
lobLocator = null;
break;
case OciDataType.TimeStamp:
break;
default:
Marshal.FreeHGlobal (bindOutValue);
break;
}
bindOutValue = IntPtr.Zero;
bindValue = IntPtr.Zero;
bindHandle = null;
connection = null;
}
// copied from OciDefineHandle
[MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
private DateTime UnpackDate (byte[] bytes)
{
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,
day,
hour - 1,
minute - 1,
second - 1);
}
private 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;
}
public void Dispose ()
{
Dispose (true);
}
void Dispose (bool disposing)
{
if (disposing) {
GC.SuppressFinalize(this);
}
if (indicator != IntPtr.Zero) {
Marshal.FreeHGlobal (indicator);
indicator = IntPtr.Zero;
}
}
#endregion // Methods
internal sealed class OracleParameterConverter : ExpandableObjectConverter
{
public OracleParameterConverter ()
{
}
[MonoTODO]
public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
{
throw new NotImplementedException ();
}
[MonoTODO]
public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException ();
}
}
}
}