4 // Part of managed C#/.NET library System.Data.OracleClient.dll
6 // Part of the Mono class libraries at
7 // mcs/class/System.Data.OracleClient/System.Data.OracleClient.Oci
9 // Assembly: System.Data.OracleClient.dll
10 // Namespace: System.Data.OracleClient.Oci
13 // Tim Coleman <tim@timcoleman.com>
14 // Daniel Morgan <monodanmorg@yahoo.com>
16 // Copyright (C) Tim Coleman, 2003
17 // Copyright (C) Daniel Morgan, 2004, 2009
21 using System.Data.OracleClient;
22 using System.Runtime.InteropServices;
25 namespace System.Data.OracleClient.Oci
27 internal sealed class OciDefineHandle : OciHandle, IDisposable
31 bool disposed = false;
38 OciDataType definedType;
46 // Oracle defines the LONG VARCHAR and LONG VARRAW to have a size of 2 to the 31 power - 5
47 // see DefineLongVarChar and DefineLongVarRaw
48 // TODO: see OCI Programmers Guide on how to do a piece-wise operations
49 // instead of using the below. Or better yet, convert
50 // your LONG/LONG VARCHAR to CLOB and LONG RAW/LONG VARRAW to BLOB.
51 internal static int LongVarCharMaxValue = (int) Int16.MaxValue - 5;
52 internal static int LongVarRawMaxValue = (int) Int16.MaxValue - 5;
54 OciErrorHandle errorHandle;
56 OciLobLocator lobLocator;
57 OciDateTimeDescriptor dateTimeDesc;
58 OciIntervalDescriptor intervalDesc;
64 internal OciDefineHandle (OciHandle parent, IntPtr newHandle)
65 : base (OciHandleType.Define, parent, newHandle)
69 internal void DefineByPosition (int position, OracleConnection connection)
71 OciParameterDescriptor parameter = ((OciStatementHandle) Parent).GetParameter (position);
73 name = parameter.GetName ();
74 definedType = parameter.GetDataType ();
75 definedSize = parameter.GetDataSize ();
76 //precision = parameter.GetPrecision ();
77 scale = parameter.GetScale ();
78 rlenp = OciCalls.AllocateClear (sizeof(short));
79 indicator = OciCalls.AllocateClear (sizeof(short));
81 Define (position, connection);
86 #endregion // Constructors
90 internal OciDataType DataType {
91 get { return definedType; }
94 internal Type FieldType {
95 get { return fieldType; }
98 internal int DefinedSize {
99 get { return definedSize; }
102 internal OciErrorHandle ErrorHandle {
103 get { return errorHandle; }
104 set { errorHandle = value; }
107 internal bool IsNull {
108 get { return (Indicator == -1); }
111 internal short Scale {
112 get { return scale; }
115 internal short Size {
116 get { return(Marshal.ReadInt16(rlenp)); }
117 set { Marshal.WriteInt16(rlenp, value); }
120 internal short Indicator {
121 get { return(Marshal.ReadInt16(indicator)); }
122 set { Marshal.WriteInt16(indicator, value); }
125 internal IntPtr Value {
126 get { return value; }
133 void Define (int position, OracleConnection connection)
135 switch (definedType) {
136 case OciDataType.Date:
137 DefineDate (position, connection);
139 case OciDataType.TimeStamp:
140 DefineTimeStamp (position, connection);
142 case OciDataType.Clob:
143 case OciDataType.Blob:
144 DefineLob (position, definedType, connection);
146 case OciDataType.Raw:
147 case OciDataType.VarRaw:
148 DefineRaw( position, connection);
150 case OciDataType.LongRaw:
151 case OciDataType.LongVarRaw:
152 DefineLongVarRaw (position, connection);
154 case OciDataType.RowIdDescriptor:
156 DefineChar (position, connection);
158 case OciDataType.Integer:
159 case OciDataType.Number:
160 case OciDataType.Float:
161 case OciDataType.VarNum:
162 case OciDataType.UnsignedInt:
163 DefineNumber (position, connection);
165 case OciDataType.Long:
166 case OciDataType.LongVarChar:
167 DefineLongVarChar (position, connection);
169 case OciDataType.IntervalDayToSecond:
170 case OciDataType.IntervalYearToMonth:
171 DefineInterval (position, definedType, connection);
174 DefineChar (position, connection); // HANDLE ALL OTHERS AS CHAR FOR NOW
179 void DefineTimeStamp (int position, OracleConnection connection)
182 ociType = OciDataType.TimeStamp;
183 fieldType = typeof(System.DateTime);
185 dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
186 if (dateTimeDesc == null) {
187 OciErrorInfo info = connection.ErrorHandle.HandleError ();
188 throw new OracleException (info.ErrorCode, info.ErrorMessage);
191 value = dateTimeDesc.Handle;
192 dateTimeDesc.ErrorHandle = ErrorHandle;
196 status = OciCalls.OCIDefineByPosPtr (Parent,
211 OciErrorInfo info = connection.ErrorHandle.HandleError ();
212 throw new OracleException (info.ErrorCode, info.ErrorMessage);
216 void DefineDate (int position, OracleConnection connection)
219 ociType = OciDataType.Date;
220 fieldType = typeof(System.DateTime);
222 value = OciCalls.AllocateClear (definedSize);
226 status = OciCalls.OCIDefineByPos (Parent,
239 OciErrorInfo info = ErrorHandle.HandleError ();
240 throw new OracleException (info.ErrorCode, info.ErrorMessage);
244 void DefineLongVarChar (int position, OracleConnection connection)
246 fieldType = typeof (System.String);
248 // LONG VARCHAR max length is 2 to the 31 power - 5
249 // the first 4 bytes of a LONG VARCHAR value contains the length
250 // Int32.MaxValue - 5 causes out of memory in mono on win32
251 // because I do not have 2GB of memory available
252 // so Int16.MaxValue - 5 is used instead.
253 // LAMESPEC for Oracle OCI - you can not get the length of the LONG VARCHAR value
254 // until after you get the value. This could be why Oracle deprecated LONG VARCHAR.
255 // If you specify a definedSize less then the length of the column value,
256 // then you will get an OCI_ERROR ORA-01406: fetched column value was truncated
258 // TODO: get via piece-wise - a chunk at a time
259 definedSize = LongVarCharMaxValue;
261 value = OciCalls.AllocateClear (definedSize);
262 ociType = OciDataType.LongVarChar;
265 status = OciCalls.OCIDefineByPos (Parent,
276 Size = (short) definedSize;
279 OciErrorInfo info = ErrorHandle.HandleError ();
280 throw new OracleException (info.ErrorCode, info.ErrorMessage);
284 void DefineChar (int position, OracleConnection connection)
286 fieldType = typeof (System.String);
288 int maxByteCount = Encoding.UTF8.GetMaxByteCount (definedSize);
289 value = OciCalls.AllocateClear (maxByteCount);
291 ociType = OciDataType.Char;
295 status = OciCalls.OCIDefineByPos (Parent,
306 OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
309 void DefineNumber (int position, OracleConnection connection)
311 fieldType = typeof (System.Decimal);
312 value = OciCalls.AllocateClear (definedSize);
314 ociType = OciDataType.Char;
318 status = OciCalls.OCIDefineByPos (Parent,
331 OciErrorInfo info = ErrorHandle.HandleError ();
332 throw new OracleException (info.ErrorCode, info.ErrorMessage);
336 void DefineLob (int position, OciDataType type, OracleConnection connection)
340 if (ociType == OciDataType.Clob)
341 fieldType = typeof(System.String);
342 else if (ociType == OciDataType.Blob)
343 fieldType = typeof(byte[]);
349 lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
351 if (lobLocator == null) {
352 OciErrorInfo info = connection.ErrorHandle.HandleError ();
353 throw new OracleException (info.ErrorCode, info.ErrorMessage);
356 value = lobLocator.Handle;
357 lobLocator.ErrorHandle = connection.ErrorHandle;
358 lobLocator.Service = connection.ServiceContext;
359 lobLocator.Environment = connection.Environment;
361 status = OciCalls.OCIDefineByPosPtr (Parent,
373 definedSize = Int32.MaxValue;
376 OciErrorInfo info = connection.ErrorHandle.HandleError ();
377 throw new OracleException (info.ErrorCode, info.ErrorMessage);
381 void DefineRaw (int position, OracleConnection connection)
383 ociType = OciDataType.Raw;
384 fieldType = typeof (byte[]);
386 value = OciCalls.AllocateClear (definedSize);
390 status = OciCalls.OCIDefineByPos (Parent,
402 OciErrorInfo info = ErrorHandle.HandleError ();
403 throw new OracleException (info.ErrorCode, info.ErrorMessage);
407 void DefineLongVarRaw (int position, OracleConnection connection)
409 ociType = OciDataType.LongVarRaw;
410 fieldType = typeof (byte[]);
412 // TODO: get via piece-wise - a chunk at a time
413 definedSize = LongVarRawMaxValue;
415 value = OciCalls.AllocateClear (definedSize);
419 status = OciCalls.OCIDefineByPos (Parent,
431 OciErrorInfo info = ErrorHandle.HandleError ();
432 throw new OracleException (info.ErrorCode, info.ErrorMessage);
436 void DefineInterval (int position, OciDataType type, OracleConnection connection)
439 fieldType = typeof(string);
443 case OciDataType.IntervalDayToSecond:
445 intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalDayToSecond);
447 case OciDataType.IntervalYearToMonth:
448 intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalYearToMonth);
453 if (intervalDesc == null) {
454 OciErrorInfo info = connection.ErrorHandle.HandleError ();
455 throw new OracleException (info.ErrorCode, info.ErrorMessage);
458 value = intervalDesc.Handle;
459 intervalDesc.ErrorHandle = ErrorHandle;
463 status = OciCalls.OCIDefineByPosPtr (Parent,
476 OciErrorInfo info = connection.ErrorHandle.HandleError ();
477 throw new OracleException (info.ErrorCode, info.ErrorMessage);
481 protected override void Dispose (bool disposing)
485 switch (definedType) {
486 case OciDataType.Clob:
487 case OciDataType.Blob:
488 case OciDataType.TimeStamp:
489 case OciDataType.IntervalDayToSecond:
490 case OciDataType.IntervalYearToMonth:
493 Marshal.FreeHGlobal (value);
498 Marshal.FreeHGlobal (indicator);
499 Marshal.FreeHGlobal (rlenp);
500 base.Dispose (disposing);
506 internal OracleLob GetOracleLob ()
508 return new OracleLob (lobLocator, ociType);
511 internal object GetValue (IFormatProvider formatProvider, OracleConnection conn)
515 byte [] buffer = null;
518 case OciDataType.VarChar2:
519 case OciDataType.String:
520 case OciDataType.VarChar:
521 case OciDataType.Char:
522 case OciDataType.CharZ:
523 case OciDataType.OciString:
524 case OciDataType.RowIdDescriptor:
525 buffer = new byte [Size];
526 Marshal.Copy (Value, buffer, 0, Size);
528 // Get length of returned string
530 //IntPtr env = Parent.Parent; // Parent is statement, grandparent is environment
531 IntPtr env = conn.Environment;
532 int status = OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
533 OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
536 StringBuilder ret = new StringBuilder(rsize);
537 status = OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
538 OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
540 return ret.ToString ();
541 case OciDataType.LongVarChar:
542 case OciDataType.Long:
543 buffer = new byte [LongVarCharMaxValue];
544 Marshal.Copy (Value, buffer, 0, buffer.Length);
547 if (BitConverter.IsLittleEndian)
548 longSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
550 longSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0);
552 ASCIIEncoding encoding = new ASCIIEncoding ();
553 string e = encoding.GetString (buffer, 4, longSize);
555 case OciDataType.Integer:
556 case OciDataType.Number:
557 case OciDataType.Float:
558 case OciDataType.VarNum:
559 case OciDataType.UnsignedInt:
560 tmp = Marshal.PtrToStringAnsi (Value, Size);
562 return Decimal.Parse (String.Copy ((string) tmp), formatProvider);
564 case OciDataType.TimeStamp:
565 return dateTimeDesc.GetDateTime (conn.Environment, dateTimeDesc.ErrorHandle);
566 case OciDataType.Date:
567 return UnpackDate ();
568 case OciDataType.Raw:
569 case OciDataType.VarRaw:
570 byte [] raw_buffer = new byte [Size];
571 Marshal.Copy (Value, raw_buffer, 0, Size);
573 case OciDataType.LongRaw:
574 case OciDataType.LongVarRaw:
575 buffer = new byte [LongVarRawMaxValue];
576 Marshal.Copy (Value, buffer, 0, buffer.Length);
579 if (BitConverter.IsLittleEndian)
580 longrawSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
582 longrawSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0);
584 byte[] longraw_buffer = new byte [longrawSize];
585 Array.ConstrainedCopy (buffer, 4, longraw_buffer, 0, longrawSize);
586 return longraw_buffer;
587 case OciDataType.Blob:
588 case OciDataType.Clob:
589 return GetOracleLob ();
590 case OciDataType.IntervalDayToSecond:
591 return new OracleTimeSpan (intervalDesc.GetDayToSecond (conn.Environment, intervalDesc.ErrorHandle));
592 case OciDataType.IntervalYearToMonth:
593 return new OracleMonthSpan (intervalDesc.GetYearToMonth (conn.Environment, intervalDesc.ErrorHandle));
595 throw new Exception("OciDataType not implemented: " + DataType.ToString ());
601 internal object GetOracleValue (IFormatProvider formatProvider, OracleConnection conn)
603 object ovalue = GetValue (formatProvider, conn);
606 case OciDataType.Raw:
607 case OciDataType.VarRaw:
608 case OciDataType.LongRaw:
609 case OciDataType.LongVarRaw:
610 return new OracleBinary ((byte[]) ovalue);
611 case OciDataType.Date:
612 case OciDataType.TimeStamp:
613 return new OracleDateTime ((DateTime) ovalue);
614 case OciDataType.Blob:
615 case OciDataType.Clob:
616 OracleLob lob = (OracleLob) ovalue;
618 case OciDataType.Integer:
619 case OciDataType.Number:
620 case OciDataType.Float:
621 case OciDataType.VarNum:
622 case OciDataType.UnsignedInt:
623 return new OracleNumber ((decimal) ovalue);
624 case OciDataType.VarChar2:
625 case OciDataType.String:
626 case OciDataType.VarChar:
627 case OciDataType.Char:
628 case OciDataType.CharZ:
629 case OciDataType.OciString:
630 case OciDataType.LongVarChar:
631 case OciDataType.Long:
632 case OciDataType.RowIdDescriptor:
633 return new OracleString ((string) ovalue);
634 case OciDataType.IntervalDayToSecond:
635 return new OracleTimeSpan ((OracleTimeSpan) ovalue);
636 case OciDataType.IntervalYearToMonth:
637 return new OracleMonthSpan ((OracleMonthSpan) ovalue);
639 // TODO: do other types
640 throw new NotImplementedException ();
644 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
645 internal DateTime UnpackDate ()
647 byte century = Marshal.ReadByte (value, 0);
648 byte year = Marshal.ReadByte (value, 1);
649 byte month = Marshal.ReadByte (value, 2);
650 byte day = Marshal.ReadByte (value, 3);
651 byte hour = Marshal.ReadByte (value, 4);
652 byte minute = Marshal.ReadByte (value, 5);
653 byte second = Marshal.ReadByte (value, 6);
662 return new DateTime ((century - 100) * 100 + (year - 100),
671 #endregion // Methods