// // 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 // Daniel Moragn // Hubert FONGARNAND // Veerapuram Varadhan // // 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 (); } } } }