2010-06-21 Marek Habersack <mhabersack@novell.com>
[mcs.git] / class / System.Data.OracleClient / System.Data.OracleClient.Oci / OciDefineHandle.cs
blobbaf90c1865f9cb91fccaea0d7c413415b1098ca2
1 //
2 // OciDefineHandle.cs
3 //
4 // Part of managed C#/.NET library System.Data.OracleClient.dll
5 //
6 // Part of the Mono class libraries at
7 // mcs/class/System.Data.OracleClient/System.Data.OracleClient.Oci
8 //
9 // Assembly: System.Data.OracleClient.dll
10 // Namespace: System.Data.OracleClient.Oci
12 // Authors:
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
20 using System;
21 using System.Data.OracleClient;
22 using System.Runtime.InteropServices;
23 using System.Text;
25 namespace System.Data.OracleClient.Oci
27 internal sealed class OciDefineHandle : OciHandle, IDisposable
29 #region Fields
31 bool disposed = false;
33 //IntPtr handle;
34 IntPtr value;
35 short indicator;
36 //OracleType type;
37 OciDataType ociType;
38 OciDataType definedType;
39 int definedSize;
40 short rlenp = 0;
41 //short precision;
42 short scale;
43 Type fieldType;
44 //string name;
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;
60 #endregion // Fields
62 #region Constructors
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);
81 parameter.Dispose ();
84 #endregion // Constructors
86 #region Properties
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; }
121 #endregion
123 #region Methods
125 void Define (int position, OracleConnection connection)
127 switch (definedType) {
128 case OciDataType.Date:
129 DefineDate (position, connection);
130 return;
131 case OciDataType.TimeStamp:
132 DefineTimeStamp (position, connection);
133 return;
134 case OciDataType.Clob:
135 case OciDataType.Blob:
136 DefineLob (position, definedType, connection);
137 return;
138 case OciDataType.Raw:
139 case OciDataType.VarRaw:
140 DefineRaw( position, connection);
141 return;
142 case OciDataType.LongRaw:
143 case OciDataType.LongVarRaw:
144 DefineLongVarRaw (position, connection);
145 return;
146 case OciDataType.RowIdDescriptor:
147 definedSize = 10;
148 DefineChar (position, connection);
149 return;
150 case OciDataType.Integer:
151 case OciDataType.Number:
152 case OciDataType.Float:
153 case OciDataType.VarNum:
154 case OciDataType.UnsignedInt:
155 DefineNumber (position, connection);
156 return;
157 case OciDataType.Long:
158 case OciDataType.LongVarChar:
159 DefineLongVarChar (position, connection);
160 return;
161 case OciDataType.IntervalDayToSecond:
162 case OciDataType.IntervalYearToMonth:
163 DefineInterval (position, definedType, connection);
164 return;
165 default:
166 DefineChar (position, connection); // HANDLE ALL OTHERS AS CHAR FOR NOW
167 return;
171 void DefineTimeStamp (int position, OracleConnection connection)
173 definedSize = -1;
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;
186 int status = 0;
188 status = OciCalls.OCIDefineByPosPtr (Parent,
189 out handle,
190 ErrorHandle,
191 position + 1,
192 ref value,
193 definedSize,
194 ociType,
195 ref indicator,
196 ref rlenp,
197 IntPtr.Zero,
200 definedSize = 11;
202 if (status != 0) {
203 OciErrorInfo info = connection.ErrorHandle.HandleError ();
204 throw new OracleException (info.ErrorCode, info.ErrorMessage);
208 void DefineDate (int position, OracleConnection connection)
210 definedSize = 7;
211 ociType = OciDataType.Date;
212 fieldType = typeof(System.DateTime);
214 value = OciCalls.AllocateClear (definedSize);
216 int status = 0;
218 status = OciCalls.OCIDefineByPos (Parent,
219 out handle,
220 ErrorHandle,
221 position + 1,
222 value,
223 definedSize,
224 ociType,
225 ref indicator,
226 ref rlenp,
227 IntPtr.Zero,
230 if (status != 0) {
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;
256 int status = 0;
257 status = OciCalls.OCIDefineByPos (Parent,
258 out handle,
259 ErrorHandle,
260 position + 1,
261 value,
262 definedSize,
263 ociType,
264 ref indicator,
265 ref rlenp,
266 IntPtr.Zero, 0);
268 rlenp = (short) definedSize;
270 if (status != 0) {
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;
285 int status = 0;
287 status = OciCalls.OCIDefineByPos (Parent,
288 out handle,
289 ErrorHandle,
290 position + 1,
291 value,
292 maxByteCount,
293 ociType,
294 ref indicator,
295 ref rlenp,
296 IntPtr.Zero,
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;
308 int status = 0;
310 status = OciCalls.OCIDefineByPos (Parent,
311 out handle,
312 ErrorHandle,
313 position + 1,
314 value,
315 definedSize * 2,
316 ociType,
317 ref indicator,
318 ref rlenp,
319 IntPtr.Zero,
322 if (status != 0) {
323 OciErrorInfo info = ErrorHandle.HandleError ();
324 throw new OracleException (info.ErrorCode, info.ErrorMessage);
328 void DefineLob (int position, OciDataType type, OracleConnection connection)
330 ociType = type;
332 if (ociType == OciDataType.Clob)
333 fieldType = typeof(System.String);
334 else if (ociType == OciDataType.Blob)
335 fieldType = typeof(byte[]);
337 int status = 0;
339 definedSize = -1;
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,
354 out handle,
355 ErrorHandle,
356 position + 1,
357 ref value,
358 definedSize,
359 ociType,
360 ref indicator,
361 ref rlenp,
362 IntPtr.Zero,
365 definedSize = Int32.MaxValue;
367 if (status != 0) {
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);
380 int status = 0;
382 status = OciCalls.OCIDefineByPos (Parent,
383 out handle,
384 ErrorHandle,
385 position + 1,
386 value,
387 definedSize,
388 ociType,
389 ref indicator,
390 ref rlenp,
391 IntPtr.Zero, 0);
393 if (status != 0) {
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);
409 int status = 0;
411 status = OciCalls.OCIDefineByPos (Parent,
412 out handle,
413 ErrorHandle,
414 position + 1,
415 value,
416 definedSize,
417 ociType,
418 ref indicator,
419 ref rlenp,
420 IntPtr.Zero, 0);
422 if (status != 0) {
423 OciErrorInfo info = ErrorHandle.HandleError ();
424 throw new OracleException (info.ErrorCode, info.ErrorMessage);
428 void DefineInterval (int position, OciDataType type, OracleConnection connection)
430 ociType = type;
431 fieldType = typeof(string);
432 definedSize = -1;
434 switch (type) {
435 case OciDataType.IntervalDayToSecond:
436 definedSize = 11;
437 intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalDayToSecond);
438 break;
439 case OciDataType.IntervalYearToMonth:
440 intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalYearToMonth);
441 definedSize = 5;
442 break;
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;
453 int status = 0;
455 status = OciCalls.OCIDefineByPosPtr (Parent,
456 out handle,
457 ErrorHandle,
458 position + 1,
459 ref value,
460 definedSize,
461 ociType,
462 ref indicator,
463 ref rlenp,
464 IntPtr.Zero,
467 if (status != 0) {
468 OciErrorInfo info = connection.ErrorHandle.HandleError ();
469 throw new OracleException (info.ErrorCode, info.ErrorMessage);
473 protected override void Dispose (bool disposing)
475 if (!disposed) {
476 try {
477 switch (definedType) {
478 case OciDataType.Clob:
479 case OciDataType.Blob:
480 case OciDataType.TimeStamp:
481 case OciDataType.IntervalDayToSecond:
482 case OciDataType.IntervalYearToMonth:
483 break;
484 default:
485 Marshal.FreeHGlobal (value);
486 break;
488 disposed = true;
489 } finally {
490 base.Dispose (disposing);
491 value = IntPtr.Zero;
496 internal OracleLob GetOracleLob ()
498 return new OracleLob (lobLocator, ociType);
501 internal object GetValue (IFormatProvider formatProvider, OracleConnection conn)
503 object tmp;
505 byte [] buffer = null;
507 switch (DataType) {
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
519 int rsize = 0;
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);
525 // Get string
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);
536 int longSize = 0;
537 if (BitConverter.IsLittleEndian)
538 longSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
539 else
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);
544 return e;
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);
551 if (tmp != null)
552 return Decimal.Parse (String.Copy ((string) tmp), formatProvider);
553 break;
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);
562 return raw_buffer;
563 case OciDataType.LongRaw:
564 case OciDataType.LongVarRaw:
565 buffer = new byte [LongVarRawMaxValue];
566 Marshal.Copy (Value, buffer, 0, buffer.Length);
568 int longrawSize = 0;
569 if (BitConverter.IsLittleEndian)
570 longrawSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
571 else
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));
584 default:
585 throw new Exception("OciDataType not implemented: " + DataType.ToString ());
588 return DBNull.Value;
591 internal object GetOracleValue (IFormatProvider formatProvider, OracleConnection conn)
593 object ovalue = GetValue (formatProvider, conn);
595 switch (DataType) {
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;
607 return lob;
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);
628 default:
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);
645 if (hour == 0)
646 hour ++;
647 if (minute == 0)
648 minute ++;
649 if (second == 0)
650 second ++;
652 return new DateTime ((century - 100) * 100 + (year - 100),
653 month,
654 day,
655 hour - 1,
656 minute - 1,
657 second - 1);
661 #endregion // Methods