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) {
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.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);
}
// 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 = 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, 2, bytes, 0, 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, 4, bytes, 0, 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) {
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,
ref 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,
ref 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,
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, // FIXME: this should be in bytes!
bindValue,
bindSize,
bindType,
ref indicator,
IntPtr.Zero,
IntPtr.Zero,
0,
IntPtr.Zero,
0);
}
OciErrorHandle.ThrowExceptionIfError (connection.ErrorHandle, status);
bindHandle.SetHandle (tmpHandle);
}