// // OciDefineHandle.cs // // Part of managed C#/.NET library System.Data.OracleClient.dll // // Part of the Mono class libraries at // mcs/class/System.Data.OracleClient/System.Data.OracleClient.Oci // // Assembly: System.Data.OracleClient.dll // Namespace: System.Data.OracleClient.Oci // // Authors: // Tim Coleman // Daniel Morgan // // Copyright (C) Tim Coleman, 2003 // Copyright (C) Daniel Morgan, 2004, 2009 // using System; using System.Data.OracleClient; using System.Runtime.InteropServices; using System.Text; namespace System.Data.OracleClient.Oci { internal sealed class OciDefineHandle : OciHandle, IDisposable { #region Fields bool disposed = false; //IntPtr handle; IntPtr value; IntPtr indicator; //OracleType type; OciDataType ociType; OciDataType definedType; int definedSize; IntPtr rlenp; //short precision; short scale; Type fieldType; string name; // Oracle defines the LONG VARCHAR and LONG VARRAW to have a size of 2 to the 31 power - 5 // see DefineLongVarChar and DefineLongVarRaw // TODO: see OCI Programmers Guide on how to do a piece-wise operations // instead of using the below. Or better yet, convert // your LONG/LONG VARCHAR to CLOB and LONG RAW/LONG VARRAW to BLOB. internal static int LongVarCharMaxValue = (int) Int16.MaxValue - 5; internal static int LongVarRawMaxValue = (int) Int16.MaxValue - 5; OciErrorHandle errorHandle; OciLobLocator lobLocator; OciDateTimeDescriptor dateTimeDesc; OciIntervalDescriptor intervalDesc; #endregion // Fields #region Constructors internal OciDefineHandle (OciHandle parent, IntPtr newHandle) : base (OciHandleType.Define, parent, newHandle) { } internal void DefineByPosition (int position, OracleConnection connection) { OciParameterDescriptor parameter = ((OciStatementHandle) Parent).GetParameter (position); name = parameter.GetName (); definedType = parameter.GetDataType (); definedSize = parameter.GetDataSize (); //precision = parameter.GetPrecision (); scale = parameter.GetScale (); rlenp = OciCalls.AllocateClear (sizeof(short)); indicator = OciCalls.AllocateClear (sizeof(short)); Define (position, connection); parameter.Dispose (); } #endregion // Constructors #region Properties internal OciDataType DataType { get { return definedType; } } internal Type FieldType { get { return fieldType; } } internal int DefinedSize { get { return definedSize; } } internal OciErrorHandle ErrorHandle { get { return errorHandle; } set { errorHandle = value; } } internal bool IsNull { get { return (Indicator == -1); } } internal short Scale { get { return scale; } } internal short Size { get { return(Marshal.ReadInt16(rlenp)); } set { Marshal.WriteInt16(rlenp, value); } } internal short Indicator { get { return(Marshal.ReadInt16(indicator)); } set { Marshal.WriteInt16(indicator, value); } } internal IntPtr Value { get { return value; } } #endregion #region Methods void Define (int position, OracleConnection connection) { switch (definedType) { case OciDataType.Date: DefineDate (position, connection); return; case OciDataType.TimeStamp: DefineTimeStamp (position, connection); return; case OciDataType.Clob: case OciDataType.Blob: DefineLob (position, definedType, connection); return; case OciDataType.Raw: case OciDataType.VarRaw: DefineRaw( position, connection); return; case OciDataType.LongRaw: case OciDataType.LongVarRaw: DefineLongVarRaw (position, connection); return; case OciDataType.RowIdDescriptor: definedSize = 10; DefineChar (position, connection); return; case OciDataType.Integer: case OciDataType.Number: case OciDataType.Float: case OciDataType.VarNum: case OciDataType.UnsignedInt: DefineNumber (position, connection); return; case OciDataType.Long: case OciDataType.LongVarChar: DefineLongVarChar (position, connection); return; case OciDataType.IntervalDayToSecond: case OciDataType.IntervalYearToMonth: DefineInterval (position, definedType, connection); return; default: DefineChar (position, connection); // HANDLE ALL OTHERS AS CHAR FOR NOW return; } } void DefineTimeStamp (int position, OracleConnection connection) { definedSize = -1; ociType = OciDataType.TimeStamp; fieldType = typeof(System.DateTime); dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp); if (dateTimeDesc == null) { OciErrorInfo info = connection.ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } value = dateTimeDesc.Handle; dateTimeDesc.ErrorHandle = ErrorHandle; int status = 0; status = OciCalls.OCIDefineByPosPtr (Parent, out handle, ErrorHandle, position + 1, ref value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); definedSize = 11; if (status != 0) { OciErrorInfo info = connection.ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineDate (int position, OracleConnection connection) { definedSize = 7; ociType = OciDataType.Date; fieldType = typeof(System.DateTime); value = OciCalls.AllocateClear (definedSize); int status = 0; status = OciCalls.OCIDefineByPos (Parent, out handle, ErrorHandle, position + 1, value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); if (status != 0) { OciErrorInfo info = ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineLongVarChar (int position, OracleConnection connection) { fieldType = typeof (System.String); // LONG VARCHAR max length is 2 to the 31 power - 5 // the first 4 bytes of a LONG VARCHAR value contains the length // Int32.MaxValue - 5 causes out of memory in mono on win32 // because I do not have 2GB of memory available // so Int16.MaxValue - 5 is used instead. // LAMESPEC for Oracle OCI - you can not get the length of the LONG VARCHAR value // until after you get the value. This could be why Oracle deprecated LONG VARCHAR. // If you specify a definedSize less then the length of the column value, // then you will get an OCI_ERROR ORA-01406: fetched column value was truncated // TODO: get via piece-wise - a chunk at a time definedSize = LongVarCharMaxValue; value = OciCalls.AllocateClear (definedSize); ociType = OciDataType.LongVarChar; int status = 0; status = OciCalls.OCIDefineByPos (Parent, out handle, ErrorHandle, position + 1, value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); Size = (short) definedSize; if (status != 0) { OciErrorInfo info = ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineChar (int position, OracleConnection connection) { fieldType = typeof (System.String); int maxByteCount = Encoding.UTF8.GetMaxByteCount (definedSize); value = OciCalls.AllocateClear (maxByteCount); ociType = OciDataType.Char; int status = 0; status = OciCalls.OCIDefineByPos (Parent, out handle, ErrorHandle, position + 1, value, maxByteCount, ociType, indicator, rlenp, IntPtr.Zero, 0); OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status); } void DefineNumber (int position, OracleConnection connection) { fieldType = typeof (System.Decimal); value = OciCalls.AllocateClear (definedSize); ociType = OciDataType.Char; int status = 0; status = OciCalls.OCIDefineByPos (Parent, out handle, ErrorHandle, position + 1, value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); if (status != 0) { OciErrorInfo info = ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineLob (int position, OciDataType type, OracleConnection connection) { ociType = type; if (ociType == OciDataType.Clob) fieldType = typeof(System.String); else if (ociType == OciDataType.Blob) fieldType = typeof(byte[]); int status = 0; definedSize = -1; lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator); if (lobLocator == null) { OciErrorInfo info = connection.ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } value = lobLocator.Handle; lobLocator.ErrorHandle = connection.ErrorHandle; lobLocator.Service = connection.ServiceContext; lobLocator.Environment = connection.Environment; status = OciCalls.OCIDefineByPosPtr (Parent, out handle, ErrorHandle, position + 1, ref value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); definedSize = Int32.MaxValue; if (status != 0) { OciErrorInfo info = connection.ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineRaw (int position, OracleConnection connection) { ociType = OciDataType.Raw; fieldType = typeof (byte[]); value = OciCalls.AllocateClear (definedSize); int status = 0; status = OciCalls.OCIDefineByPos (Parent, out handle, ErrorHandle, position + 1, value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); if (status != 0) { OciErrorInfo info = ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineLongVarRaw (int position, OracleConnection connection) { ociType = OciDataType.LongVarRaw; fieldType = typeof (byte[]); // TODO: get via piece-wise - a chunk at a time definedSize = LongVarRawMaxValue; value = OciCalls.AllocateClear (definedSize); int status = 0; status = OciCalls.OCIDefineByPos (Parent, out handle, ErrorHandle, position + 1, value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); if (status != 0) { OciErrorInfo info = ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } void DefineInterval (int position, OciDataType type, OracleConnection connection) { ociType = type; fieldType = typeof(string); definedSize = -1; switch (type) { case OciDataType.IntervalDayToSecond: definedSize = 11; intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalDayToSecond); break; case OciDataType.IntervalYearToMonth: intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalYearToMonth); definedSize = 5; break; } if (intervalDesc == null) { OciErrorInfo info = connection.ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } value = intervalDesc.Handle; intervalDesc.ErrorHandle = ErrorHandle; int status = 0; status = OciCalls.OCIDefineByPosPtr (Parent, out handle, ErrorHandle, position + 1, ref value, definedSize, ociType, indicator, rlenp, IntPtr.Zero, 0); if (status != 0) { OciErrorInfo info = connection.ErrorHandle.HandleError (); throw new OracleException (info.ErrorCode, info.ErrorMessage); } } protected override void Dispose (bool disposing) { if (!disposed) { try { switch (definedType) { case OciDataType.Clob: case OciDataType.Blob: case OciDataType.TimeStamp: case OciDataType.IntervalDayToSecond: case OciDataType.IntervalYearToMonth: break; default: Marshal.FreeHGlobal (value); break; } disposed = true; } finally { Marshal.FreeHGlobal (indicator); Marshal.FreeHGlobal (rlenp); base.Dispose (disposing); value = IntPtr.Zero; } } } internal OracleLob GetOracleLob () { return new OracleLob (lobLocator, ociType); } internal object GetValue (IFormatProvider formatProvider, OracleConnection conn) { object tmp; byte [] buffer = null; switch (DataType) { 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 (Value, buffer, 0, Size); // Get length of returned string int rsize = 0; //IntPtr env = Parent.Parent; // Parent is statement, grandparent is environment IntPtr env = conn.Environment; int status = OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize); OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status); // Get string StringBuilder ret = new StringBuilder(rsize); status = OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize); OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status); return ret.ToString (); case OciDataType.LongVarChar: case OciDataType.Long: buffer = new byte [LongVarCharMaxValue]; Marshal.Copy (Value, buffer, 0, buffer.Length); int longSize = 0; if (BitConverter.IsLittleEndian) longSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0); else longSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0); ASCIIEncoding encoding = new ASCIIEncoding (); string e = encoding.GetString (buffer, 4, longSize); return e; case OciDataType.Integer: case OciDataType.Number: case OciDataType.Float: case OciDataType.VarNum: case OciDataType.UnsignedInt: tmp = Marshal.PtrToStringAnsi (Value, Size); if (tmp != null) return Decimal.Parse (String.Copy ((string) tmp), formatProvider); break; case OciDataType.TimeStamp: return dateTimeDesc.GetDateTime (conn.Environment, dateTimeDesc.ErrorHandle); case OciDataType.Date: return UnpackDate (); case OciDataType.Raw: case OciDataType.VarRaw: byte [] raw_buffer = new byte [Size]; Marshal.Copy (Value, raw_buffer, 0, Size); return raw_buffer; case OciDataType.LongRaw: case OciDataType.LongVarRaw: buffer = new byte [LongVarRawMaxValue]; Marshal.Copy (Value, buffer, 0, buffer.Length); int longrawSize = 0; if (BitConverter.IsLittleEndian) longrawSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0); else longrawSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0); byte[] longraw_buffer = new byte [longrawSize]; Array.ConstrainedCopy (buffer, 4, longraw_buffer, 0, longrawSize); return longraw_buffer; case OciDataType.Blob: case OciDataType.Clob: return GetOracleLob (); case OciDataType.IntervalDayToSecond: return new OracleTimeSpan (intervalDesc.GetDayToSecond (conn.Environment, intervalDesc.ErrorHandle)); case OciDataType.IntervalYearToMonth: return new OracleMonthSpan (intervalDesc.GetYearToMonth (conn.Environment, intervalDesc.ErrorHandle)); default: throw new Exception("OciDataType not implemented: " + DataType.ToString ()); } return DBNull.Value; } internal object GetOracleValue (IFormatProvider formatProvider, OracleConnection conn) { object ovalue = GetValue (formatProvider, conn); switch (DataType) { case OciDataType.Raw: case OciDataType.VarRaw: case OciDataType.LongRaw: case OciDataType.LongVarRaw: return new OracleBinary ((byte[]) ovalue); case OciDataType.Date: case OciDataType.TimeStamp: return new OracleDateTime ((DateTime) ovalue); case OciDataType.Blob: case OciDataType.Clob: OracleLob lob = (OracleLob) ovalue; return lob; case OciDataType.Integer: case OciDataType.Number: case OciDataType.Float: case OciDataType.VarNum: case OciDataType.UnsignedInt: return new OracleNumber ((decimal) ovalue); case OciDataType.VarChar2: case OciDataType.String: case OciDataType.VarChar: case OciDataType.Char: case OciDataType.CharZ: case OciDataType.OciString: case OciDataType.LongVarChar: case OciDataType.Long: case OciDataType.RowIdDescriptor: return new OracleString ((string) ovalue); case OciDataType.IntervalDayToSecond: return new OracleTimeSpan ((OracleTimeSpan) ovalue); case OciDataType.IntervalYearToMonth: return new OracleMonthSpan ((OracleMonthSpan) ovalue); default: // TODO: do other types throw new NotImplementedException (); } } [MonoTODO ("Be able to handle negative dates... i.e. BCE.")] internal DateTime UnpackDate () { byte century = Marshal.ReadByte (value, 0); byte year = Marshal.ReadByte (value, 1); byte month = Marshal.ReadByte (value, 2); byte day = Marshal.ReadByte (value, 3); byte hour = Marshal.ReadByte (value, 4); byte minute = Marshal.ReadByte (value, 5); byte second = Marshal.ReadByte (value, 6); if (hour == 0) hour ++; if (minute == 0) minute ++; if (second == 0) second ++; return new DateTime ((century - 100) * 100 + (year - 100), month, day, hour - 1, minute - 1, second - 1); } #endregion // Methods } }