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 ();
79 Define (position
, connection
);
84 #endregion // Constructors
88 internal OciDataType DataType
{
89 get { return definedType; }
92 internal Type FieldType
{
93 get { return fieldType; }
96 internal int DefinedSize
{
97 get { return definedSize; }
100 internal OciErrorHandle ErrorHandle
{
101 get { return errorHandle; }
102 set { errorHandle = value; }
105 internal bool IsNull
{
106 get { return (indicator == -1); }
109 internal short Scale
{
110 get { return scale; }
113 internal short Size
{
114 get { return rlenp; }
117 internal IntPtr Value
{
118 get { return value; }
125 void Define (int position
, OracleConnection connection
)
127 switch (definedType
) {
128 case OciDataType
.Date
:
129 DefineDate (position
, connection
);
131 case OciDataType
.TimeStamp
:
132 DefineTimeStamp (position
, connection
);
134 case OciDataType
.Clob
:
135 case OciDataType
.Blob
:
136 DefineLob (position
, definedType
, connection
);
138 case OciDataType
.Raw
:
139 case OciDataType
.VarRaw
:
140 DefineRaw( position
, connection
);
142 case OciDataType
.LongRaw
:
143 case OciDataType
.LongVarRaw
:
144 DefineLongVarRaw (position
, connection
);
146 case OciDataType
.RowIdDescriptor
:
148 DefineChar (position
, connection
);
150 case OciDataType
.Integer
:
151 case OciDataType
.Number
:
152 case OciDataType
.Float
:
153 case OciDataType
.VarNum
:
154 case OciDataType
.UnsignedInt
:
155 DefineNumber (position
, connection
);
157 case OciDataType
.Long
:
158 case OciDataType
.LongVarChar
:
159 DefineLongVarChar (position
, connection
);
161 case OciDataType
.IntervalDayToSecond
:
162 case OciDataType
.IntervalYearToMonth
:
163 DefineInterval (position
, definedType
, connection
);
166 DefineChar (position
, connection
); // HANDLE ALL OTHERS AS CHAR FOR NOW
171 void DefineTimeStamp (int position
, OracleConnection connection
)
174 ociType
= OciDataType
.TimeStamp
;
175 fieldType
= typeof(System
.DateTime
);
177 dateTimeDesc
= (OciDateTimeDescriptor
) connection
.Environment
.Allocate (OciHandleType
.TimeStamp
);
178 if (dateTimeDesc
== null) {
179 OciErrorInfo info
= connection
.ErrorHandle
.HandleError ();
180 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
183 value = dateTimeDesc
.Handle
;
184 dateTimeDesc
.ErrorHandle
= ErrorHandle
;
188 status
= OciCalls
.OCIDefineByPosPtr (Parent
,
203 OciErrorInfo info
= connection
.ErrorHandle
.HandleError ();
204 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
208 void DefineDate (int position
, OracleConnection connection
)
211 ociType
= OciDataType
.Date
;
212 fieldType
= typeof(System
.DateTime
);
214 value = OciCalls
.AllocateClear (definedSize
);
218 status
= OciCalls
.OCIDefineByPos (Parent
,
231 OciErrorInfo info
= ErrorHandle
.HandleError ();
232 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
236 void DefineLongVarChar (int position
, OracleConnection connection
)
238 fieldType
= typeof (System
.String
);
240 // LONG VARCHAR max length is 2 to the 31 power - 5
241 // the first 4 bytes of a LONG VARCHAR value contains the length
242 // Int32.MaxValue - 5 causes out of memory in mono on win32
243 // because I do not have 2GB of memory available
244 // so Int16.MaxValue - 5 is used instead.
245 // LAMESPEC for Oracle OCI - you can not get the length of the LONG VARCHAR value
246 // until after you get the value. This could be why Oracle deprecated LONG VARCHAR.
247 // If you specify a definedSize less then the length of the column value,
248 // then you will get an OCI_ERROR ORA-01406: fetched column value was truncated
250 // TODO: get via piece-wise - a chunk at a time
251 definedSize
= LongVarCharMaxValue
;
253 value = OciCalls
.AllocateClear (definedSize
);
254 ociType
= OciDataType
.LongVarChar
;
257 status
= OciCalls
.OCIDefineByPos (Parent
,
268 rlenp
= (short) definedSize
;
271 OciErrorInfo info
= ErrorHandle
.HandleError ();
272 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
276 void DefineChar (int position
, OracleConnection connection
)
278 fieldType
= typeof (System
.String
);
280 int maxByteCount
= Encoding
.UTF8
.GetMaxByteCount (definedSize
);
281 value = OciCalls
.AllocateClear (maxByteCount
);
283 ociType
= OciDataType
.Char
;
287 status
= OciCalls
.OCIDefineByPos (Parent
,
298 OciErrorHandle
.ThrowExceptionIfError (ErrorHandle
, status
);
301 void DefineNumber (int position
, OracleConnection connection
)
303 fieldType
= typeof (System
.Decimal
);
304 value = OciCalls
.AllocateClear (definedSize
);
306 ociType
= OciDataType
.Char
;
310 status
= OciCalls
.OCIDefineByPos (Parent
,
323 OciErrorInfo info
= ErrorHandle
.HandleError ();
324 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
328 void DefineLob (int position
, OciDataType type
, OracleConnection connection
)
332 if (ociType
== OciDataType
.Clob
)
333 fieldType
= typeof(System
.String
);
334 else if (ociType
== OciDataType
.Blob
)
335 fieldType
= typeof(byte[]);
341 lobLocator
= (OciLobLocator
) connection
.Environment
.Allocate (OciHandleType
.LobLocator
);
343 if (lobLocator
== null) {
344 OciErrorInfo info
= connection
.ErrorHandle
.HandleError ();
345 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
348 value = lobLocator
.Handle
;
349 lobLocator
.ErrorHandle
= connection
.ErrorHandle
;
350 lobLocator
.Service
= connection
.ServiceContext
;
351 lobLocator
.Environment
= connection
.Environment
;
353 status
= OciCalls
.OCIDefineByPosPtr (Parent
,
365 definedSize
= Int32
.MaxValue
;
368 OciErrorInfo info
= connection
.ErrorHandle
.HandleError ();
369 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
373 void DefineRaw (int position
, OracleConnection connection
)
375 ociType
= OciDataType
.Raw
;
376 fieldType
= typeof (byte[]);
378 value = OciCalls
.AllocateClear (definedSize
);
382 status
= OciCalls
.OCIDefineByPos (Parent
,
394 OciErrorInfo info
= ErrorHandle
.HandleError ();
395 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
399 void DefineLongVarRaw (int position
, OracleConnection connection
)
401 ociType
= OciDataType
.LongVarRaw
;
402 fieldType
= typeof (byte[]);
404 // TODO: get via piece-wise - a chunk at a time
405 definedSize
= LongVarRawMaxValue
;
407 value = OciCalls
.AllocateClear (definedSize
);
411 status
= OciCalls
.OCIDefineByPos (Parent
,
423 OciErrorInfo info
= ErrorHandle
.HandleError ();
424 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
428 void DefineInterval (int position
, OciDataType type
, OracleConnection connection
)
431 fieldType
= typeof(string);
435 case OciDataType
.IntervalDayToSecond
:
437 intervalDesc
= (OciIntervalDescriptor
) connection
.Environment
.Allocate (OciHandleType
.IntervalDayToSecond
);
439 case OciDataType
.IntervalYearToMonth
:
440 intervalDesc
= (OciIntervalDescriptor
) connection
.Environment
.Allocate (OciHandleType
.IntervalYearToMonth
);
445 if (intervalDesc
== null) {
446 OciErrorInfo info
= connection
.ErrorHandle
.HandleError ();
447 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
450 value = intervalDesc
.Handle
;
451 intervalDesc
.ErrorHandle
= ErrorHandle
;
455 status
= OciCalls
.OCIDefineByPosPtr (Parent
,
468 OciErrorInfo info
= connection
.ErrorHandle
.HandleError ();
469 throw new OracleException (info
.ErrorCode
, info
.ErrorMessage
);
473 protected override void Dispose (bool disposing
)
477 switch (definedType
) {
478 case OciDataType
.Clob
:
479 case OciDataType
.Blob
:
480 case OciDataType
.TimeStamp
:
481 case OciDataType
.IntervalDayToSecond
:
482 case OciDataType
.IntervalYearToMonth
:
485 Marshal
.FreeHGlobal (value);
490 base.Dispose (disposing
);
496 internal OracleLob
GetOracleLob ()
498 return new OracleLob (lobLocator
, ociType
);
501 internal object GetValue (IFormatProvider formatProvider
, OracleConnection conn
)
505 byte [] buffer
= null;
508 case OciDataType
.VarChar2
:
509 case OciDataType
.String
:
510 case OciDataType
.VarChar
:
511 case OciDataType
.Char
:
512 case OciDataType
.CharZ
:
513 case OciDataType
.OciString
:
514 case OciDataType
.RowIdDescriptor
:
515 buffer
= new byte [Size
];
516 Marshal
.Copy (Value
, buffer
, 0, Size
);
518 // Get length of returned string
520 //IntPtr env = Parent.Parent; // Parent is statement, grandparent is environment
521 IntPtr env
= conn
.Environment
;
522 int status
= OciCalls
.OCICharSetToUnicode (env
, null, buffer
, out rsize
);
523 OciErrorHandle
.ThrowExceptionIfError (ErrorHandle
, status
);
526 StringBuilder ret
= new StringBuilder(rsize
);
527 status
= OciCalls
.OCICharSetToUnicode (env
, ret
, buffer
, out rsize
);
528 OciErrorHandle
.ThrowExceptionIfError (ErrorHandle
, status
);
530 return ret
.ToString ();
531 case OciDataType
.LongVarChar
:
532 case OciDataType
.Long
:
533 buffer
= new byte [LongVarCharMaxValue
];
534 Marshal
.Copy (Value
, buffer
, 0, buffer
.Length
);
537 if (BitConverter
.IsLittleEndian
)
538 longSize
= BitConverter
.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}
, 0);
540 longSize
= BitConverter
.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}
, 0);
542 ASCIIEncoding encoding
= new ASCIIEncoding ();
543 string e
= encoding
.GetString (buffer
, 4, longSize
);
545 case OciDataType
.Integer
:
546 case OciDataType
.Number
:
547 case OciDataType
.Float
:
548 case OciDataType
.VarNum
:
549 case OciDataType
.UnsignedInt
:
550 tmp
= Marshal
.PtrToStringAnsi (Value
, Size
);
552 return Decimal
.Parse (String
.Copy ((string) tmp
), formatProvider
);
554 case OciDataType
.TimeStamp
:
555 return dateTimeDesc
.GetDateTime (conn
.Environment
, dateTimeDesc
.ErrorHandle
);
556 case OciDataType
.Date
:
557 return UnpackDate ();
558 case OciDataType
.Raw
:
559 case OciDataType
.VarRaw
:
560 byte [] raw_buffer
= new byte [Size
];
561 Marshal
.Copy (Value
, raw_buffer
, 0, Size
);
563 case OciDataType
.LongRaw
:
564 case OciDataType
.LongVarRaw
:
565 buffer
= new byte [LongVarRawMaxValue
];
566 Marshal
.Copy (Value
, buffer
, 0, buffer
.Length
);
569 if (BitConverter
.IsLittleEndian
)
570 longrawSize
= BitConverter
.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}
, 0);
572 longrawSize
= BitConverter
.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}
, 0);
574 byte[] longraw_buffer
= new byte [longrawSize
];
575 Array
.ConstrainedCopy (buffer
, 4, longraw_buffer
, 0, longrawSize
);
576 return longraw_buffer
;
577 case OciDataType
.Blob
:
578 case OciDataType
.Clob
:
579 return GetOracleLob ();
580 case OciDataType
.IntervalDayToSecond
:
581 return new OracleTimeSpan (intervalDesc
.GetDayToSecond (conn
.Environment
, intervalDesc
.ErrorHandle
));
582 case OciDataType
.IntervalYearToMonth
:
583 return new OracleMonthSpan (intervalDesc
.GetYearToMonth (conn
.Environment
, intervalDesc
.ErrorHandle
));
585 throw new Exception("OciDataType not implemented: " + DataType
.ToString ());
591 internal object GetOracleValue (IFormatProvider formatProvider
, OracleConnection conn
)
593 object ovalue
= GetValue (formatProvider
, conn
);
596 case OciDataType
.Raw
:
597 case OciDataType
.VarRaw
:
598 case OciDataType
.LongRaw
:
599 case OciDataType
.LongVarRaw
:
600 return new OracleBinary ((byte[]) ovalue
);
601 case OciDataType
.Date
:
602 case OciDataType
.TimeStamp
:
603 return new OracleDateTime ((DateTime
) ovalue
);
604 case OciDataType
.Blob
:
605 case OciDataType
.Clob
:
606 OracleLob lob
= (OracleLob
) ovalue
;
608 case OciDataType
.Integer
:
609 case OciDataType
.Number
:
610 case OciDataType
.Float
:
611 case OciDataType
.VarNum
:
612 case OciDataType
.UnsignedInt
:
613 return new OracleNumber ((decimal) ovalue
);
614 case OciDataType
.VarChar2
:
615 case OciDataType
.String
:
616 case OciDataType
.VarChar
:
617 case OciDataType
.Char
:
618 case OciDataType
.CharZ
:
619 case OciDataType
.OciString
:
620 case OciDataType
.LongVarChar
:
621 case OciDataType
.Long
:
622 case OciDataType
.RowIdDescriptor
:
623 return new OracleString ((string) ovalue
);
624 case OciDataType
.IntervalDayToSecond
:
625 return new OracleTimeSpan ((OracleTimeSpan
) ovalue
);
626 case OciDataType
.IntervalYearToMonth
:
627 return new OracleMonthSpan ((OracleMonthSpan
) ovalue
);
629 // TODO: do other types
630 throw new NotImplementedException ();
634 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
635 internal DateTime
UnpackDate ()
637 byte century
= Marshal
.ReadByte (value, 0);
638 byte year
= Marshal
.ReadByte (value, 1);
639 byte month
= Marshal
.ReadByte (value, 2);
640 byte day
= Marshal
.ReadByte (value, 3);
641 byte hour
= Marshal
.ReadByte (value, 4);
642 byte minute
= Marshal
.ReadByte (value, 5);
643 byte second
= Marshal
.ReadByte (value, 6);
652 return new DateTime ((century
- 100) * 100 + (year
- 100),
661 #endregion // Methods