Merge pull request #1304 from slluis/mac-proxy-autoconfig
[mono.git] / mcs / class / System.Data.OracleClient / System.Data.OracleClient.Oci / OciDefineHandle.cs
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
11 //
12 // Authors:
13 //     Tim Coleman <tim@timcoleman.com>
14 //     Daniel Morgan <monodanmorg@yahoo.com>
15 //
16 // Copyright (C) Tim Coleman, 2003
17 // Copyright (C) Daniel Morgan, 2004, 2009
18 //
19
20 using System;
21 using System.Data.OracleClient;
22 using System.Runtime.InteropServices;
23 using System.Text;
24
25 namespace System.Data.OracleClient.Oci
26 {
27         internal sealed class OciDefineHandle : OciHandle, IDisposable
28         {
29                 #region Fields
30
31                 bool disposed = false;
32
33                 //IntPtr handle;
34                 IntPtr value;
35                 IntPtr indicator;
36                 //OracleType type;
37                 OciDataType ociType;
38                 OciDataType definedType;
39                 int definedSize;
40                 IntPtr rlenp;
41                 //short precision;
42                 short scale;
43                 Type fieldType;
44                 string name;
45
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;
53                 
54                 OciErrorHandle errorHandle;
55
56                 OciLobLocator lobLocator;
57                 OciDateTimeDescriptor dateTimeDesc;
58                 OciIntervalDescriptor intervalDesc;
59
60                 #endregion // Fields
61
62                 #region Constructors
63
64                 internal OciDefineHandle (OciHandle parent, IntPtr newHandle)
65                         : base (OciHandleType.Define, parent, newHandle)
66                 {
67                 }
68
69                 internal void DefineByPosition (int position, OracleConnection connection)
70                 {
71                         OciParameterDescriptor parameter = ((OciStatementHandle) Parent).GetParameter (position);
72
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));
80
81                         Define (position, connection);
82
83                         parameter.Dispose ();
84                 }
85
86                 #endregion // Constructors
87
88                 #region Properties
89
90                 internal OciDataType DataType {
91                         get { return definedType; }
92                 }
93
94                 internal Type FieldType {
95                         get { return fieldType; }
96                 }
97
98                 internal int DefinedSize {
99                         get { return definedSize; }
100                 }
101
102                 internal OciErrorHandle ErrorHandle {
103                         get { return errorHandle; }
104                         set { errorHandle = value; }
105                 }
106
107                 internal bool IsNull {
108                         get { return (Indicator == -1); }
109                 }
110
111                 internal short Scale {
112                         get { return scale; }
113                 }
114
115                 internal short Size {
116                         get { return(Marshal.ReadInt16(rlenp)); }
117                         set { Marshal.WriteInt16(rlenp, value); }
118                 }
119
120                 internal short Indicator {
121                         get { return(Marshal.ReadInt16(indicator)); }
122                         set { Marshal.WriteInt16(indicator, value); }
123                 }
124
125                 internal IntPtr Value {
126                         get { return value; }
127                 }
128
129                 #endregion
130
131                 #region Methods
132
133                 void Define (int position, OracleConnection connection)
134                 {
135                         switch (definedType) {
136                         case OciDataType.Date:
137                                 DefineDate (position, connection);
138                                 return;
139                         case OciDataType.TimeStamp:
140                                 DefineTimeStamp (position, connection);
141                                 return;
142                         case OciDataType.Clob:
143                         case OciDataType.Blob:
144                                 DefineLob (position, definedType, connection);
145                                 return;
146                         case OciDataType.Raw:
147                         case OciDataType.VarRaw:
148                                 DefineRaw( position, connection);
149                                 return;
150                         case OciDataType.LongRaw:
151                         case OciDataType.LongVarRaw:
152                                 DefineLongVarRaw (position, connection);
153                                 return;
154                         case OciDataType.RowIdDescriptor:
155                                 definedSize = 10;
156                                 DefineChar (position, connection);
157                                 return;
158                         case OciDataType.Integer:
159                         case OciDataType.Number:
160                         case OciDataType.Float:
161                         case OciDataType.VarNum:
162                         case OciDataType.UnsignedInt:
163                                 DefineNumber (position, connection);
164                                 return;
165                         case OciDataType.Long:
166                         case OciDataType.LongVarChar:
167                                 DefineLongVarChar (position, connection);
168                                 return;
169                         case OciDataType.IntervalDayToSecond:
170                         case OciDataType.IntervalYearToMonth:
171                                 DefineInterval (position, definedType, connection);
172                                 return;
173                         default:
174                                 DefineChar (position, connection); // HANDLE ALL OTHERS AS CHAR FOR NOW
175                                 return;
176                         }
177                 }
178
179                 void DefineTimeStamp (int position, OracleConnection connection)
180                 {
181                         definedSize = -1;
182                         ociType = OciDataType.TimeStamp;
183                         fieldType = typeof(System.DateTime);
184
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);
189                         }
190
191                         value = dateTimeDesc.Handle;
192                         dateTimeDesc.ErrorHandle = ErrorHandle;
193
194                         int status = 0;
195
196                         status = OciCalls.OCIDefineByPosPtr (Parent,
197                                 out handle,
198                                 ErrorHandle,
199                                 position + 1,
200                                 ref value,
201                                 definedSize,
202                                 ociType,
203                                 indicator,
204                                 rlenp,
205                                 IntPtr.Zero,
206                                 0);
207
208                         definedSize = 11;
209
210                         if (status != 0) {
211                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
212                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
213                         }
214                 }
215
216                 void DefineDate (int position, OracleConnection connection)
217                 {
218                         definedSize = 7;
219                         ociType = OciDataType.Date;
220                         fieldType = typeof(System.DateTime);
221
222                         value = OciCalls.AllocateClear (definedSize);
223
224                         int status = 0;
225
226                         status = OciCalls.OCIDefineByPos (Parent,
227                                                 out handle,
228                                                 ErrorHandle,
229                                                 position + 1,
230                                                 value,
231                                                 definedSize,
232                                                 ociType,
233                                                 indicator,
234                                                 rlenp,
235                                                 IntPtr.Zero,
236                                                 0);
237
238                         if (status != 0) {
239                                 OciErrorInfo info = ErrorHandle.HandleError ();
240                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
241                         }
242                 }
243
244                 void DefineLongVarChar (int position, OracleConnection connection)
245                 {
246                         fieldType = typeof (System.String);
247
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
257                         
258                         // TODO: get via piece-wise - a chunk at a time
259                         definedSize = LongVarCharMaxValue;
260
261                         value = OciCalls.AllocateClear (definedSize);
262                         ociType = OciDataType.LongVarChar;
263
264                         int status = 0;
265                         status = OciCalls.OCIDefineByPos (Parent,
266                                 out handle,
267                                 ErrorHandle,
268                                 position + 1,
269                                 value,
270                                 definedSize,
271                                 ociType,
272                                 indicator,
273                                 rlenp,
274                                 IntPtr.Zero, 0);
275
276                         Size = (short) definedSize;
277
278                         if (status != 0) {
279                                 OciErrorInfo info = ErrorHandle.HandleError ();
280                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
281                         }
282                 }
283
284                 void DefineChar (int position, OracleConnection connection)
285                 {
286                         fieldType = typeof (System.String);
287
288                         int maxByteCount = Encoding.UTF8.GetMaxByteCount (definedSize);
289                         value = OciCalls.AllocateClear (maxByteCount);
290
291                         ociType = OciDataType.Char;
292
293                         int status = 0;
294
295                         status = OciCalls.OCIDefineByPos (Parent,
296                                                 out handle,
297                                                 ErrorHandle,
298                                                 position + 1,
299                                                 value,
300                                                 maxByteCount,
301                                                 ociType,
302                                                 indicator,
303                                                 rlenp,
304                                                 IntPtr.Zero,
305                                                 0);
306                         OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
307                 }
308
309                 void DefineNumber (int position, OracleConnection connection)
310                 {
311                         fieldType = typeof (System.Decimal);
312                         value = OciCalls.AllocateClear (definedSize);
313
314                         ociType = OciDataType.Char;
315
316                         int status = 0;
317
318                         status = OciCalls.OCIDefineByPos (Parent,
319                                 out handle,
320                                 ErrorHandle,
321                                 position + 1,
322                                 value,
323                                 definedSize,
324                                 ociType,
325                                 indicator,
326                                 rlenp,
327                                 IntPtr.Zero,
328                                 0);
329
330                         if (status != 0) {
331                                 OciErrorInfo info = ErrorHandle.HandleError ();
332                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
333                         }
334                 }
335
336                 void DefineLob (int position, OciDataType type, OracleConnection connection)
337                 {
338                         ociType = type;
339
340                         if (ociType == OciDataType.Clob)
341                                 fieldType = typeof(System.String);
342                         else if (ociType == OciDataType.Blob)
343                                 fieldType = typeof(byte[]);
344
345                         int status = 0;
346
347                         definedSize = -1;
348
349                         lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
350
351                         if (lobLocator == null) {
352                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
353                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
354                         }
355
356                         value = lobLocator.Handle;
357                         lobLocator.ErrorHandle = connection.ErrorHandle;
358                         lobLocator.Service = connection.ServiceContext;
359                         lobLocator.Environment = connection.Environment;
360
361                         status = OciCalls.OCIDefineByPosPtr (Parent,
362                                                         out handle,
363                                                         ErrorHandle,
364                                                         position + 1,
365                                                         ref value,
366                                                         definedSize,
367                                                         ociType,
368                                                         indicator,
369                                                         rlenp,
370                                                         IntPtr.Zero,
371                                                         0);
372
373                         definedSize = Int32.MaxValue;
374
375                         if (status != 0) {
376                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
377                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
378                         }
379                 }
380
381                 void DefineRaw (int position, OracleConnection connection)
382                 {
383                         ociType = OciDataType.Raw;
384                         fieldType = typeof (byte[]);
385
386                         value = OciCalls.AllocateClear (definedSize);
387
388                         int status = 0;
389
390                         status = OciCalls.OCIDefineByPos (Parent,
391                                                         out handle,
392                                                         ErrorHandle,
393                                                         position + 1,
394                                                         value,
395                                                         definedSize,
396                                                         ociType,
397                                                         indicator,
398                                                         rlenp,
399                                                         IntPtr.Zero, 0);
400
401                         if (status != 0) {
402                                 OciErrorInfo info = ErrorHandle.HandleError ();
403                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
404                         }
405                 }
406
407                 void DefineLongVarRaw (int position, OracleConnection connection)
408                 {
409                         ociType = OciDataType.LongVarRaw;
410                         fieldType = typeof (byte[]);
411
412                         // TODO: get via piece-wise - a chunk at a time
413                         definedSize = LongVarRawMaxValue;
414
415                         value = OciCalls.AllocateClear (definedSize);
416
417                         int status = 0;
418
419                         status = OciCalls.OCIDefineByPos (Parent,
420                                                         out handle,
421                                                         ErrorHandle,
422                                                         position + 1,
423                                                         value,
424                                                         definedSize,
425                                                         ociType,
426                                                         indicator,
427                                                         rlenp,
428                                                         IntPtr.Zero, 0);
429
430                         if (status != 0) {
431                                 OciErrorInfo info = ErrorHandle.HandleError ();
432                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
433                         }
434                 }
435
436                 void DefineInterval (int position, OciDataType type, OracleConnection connection)
437                 {
438                         ociType = type;
439                         fieldType = typeof(string);
440                         definedSize = -1;
441                         
442                         switch (type) {
443                                 case OciDataType.IntervalDayToSecond:
444                                         definedSize = 11;
445                                         intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalDayToSecond);
446                                         break;
447                                 case OciDataType.IntervalYearToMonth:
448                                         intervalDesc = (OciIntervalDescriptor) connection.Environment.Allocate (OciHandleType.IntervalYearToMonth);
449                                         definedSize = 5;
450                                         break;
451                         }
452                         
453                         if (intervalDesc == null) {
454                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
455                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
456                         }
457
458                         value = intervalDesc.Handle;
459                         intervalDesc.ErrorHandle = ErrorHandle;
460
461                         int status = 0;
462
463                         status = OciCalls.OCIDefineByPosPtr (Parent,
464                                 out handle,
465                                 ErrorHandle,
466                                 position + 1,
467                                 ref value,
468                                 definedSize,
469                                 ociType,
470                                 indicator,
471                                 rlenp,
472                                 IntPtr.Zero,
473                                 0);
474
475                         if (status != 0) {
476                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
477                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
478                         }
479                 }
480                 
481                 protected override void Dispose (bool disposing)
482                 {
483                         if (!disposed) {
484                                 try {
485                                         switch (definedType) {
486                                         case OciDataType.Clob:
487                                         case OciDataType.Blob:
488                                         case OciDataType.TimeStamp:
489                                         case OciDataType.IntervalDayToSecond:
490                                         case OciDataType.IntervalYearToMonth:
491                                                 break;
492                                         default:
493                                                 Marshal.FreeHGlobal (value);
494                                                 break;
495                                         }
496                                         disposed = true;
497                                 } finally {
498                                         Marshal.FreeHGlobal (indicator);
499                                         Marshal.FreeHGlobal (rlenp);
500                                         base.Dispose (disposing);
501                                         value = IntPtr.Zero;
502                                 }
503                         }
504                 }
505
506                 internal OracleLob GetOracleLob ()
507                 {
508                         return new OracleLob (lobLocator, ociType);
509                 }
510
511                 internal object GetValue (IFormatProvider formatProvider, OracleConnection conn)
512                 {
513                         object tmp;
514
515                         byte [] buffer = null;
516
517                         switch (DataType) {
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);
527
528                                 // Get length of returned string
529                                 int     rsize = 0;
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);
534
535                                 // Get string
536                                 StringBuilder ret = new StringBuilder(rsize);
537                                 status = OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
538                                 OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
539
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);
545
546                                 int longSize = 0;
547                                 if (BitConverter.IsLittleEndian)
548                                         longSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
549                                 else
550                                         longSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0);
551
552                                 ASCIIEncoding encoding = new ASCIIEncoding ();
553                                 string e = encoding.GetString (buffer, 4, longSize);
554                                 return e;
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);
561                                 if (tmp != null)
562                                         return Decimal.Parse (String.Copy ((string) tmp), formatProvider);
563                                 break;
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);
572                                 return raw_buffer;
573                         case OciDataType.LongRaw:
574                         case OciDataType.LongVarRaw:
575                                 buffer = new byte [LongVarRawMaxValue];
576                                 Marshal.Copy (Value, buffer, 0, buffer.Length);
577
578                                 int longrawSize = 0;
579                                 if (BitConverter.IsLittleEndian)
580                                         longrawSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
581                                 else
582                                         longrawSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0);
583
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));
594                         default:
595                                 throw new Exception("OciDataType not implemented: " + DataType.ToString ());
596                         }
597
598                         return DBNull.Value;
599                 }
600
601                 internal object GetOracleValue (IFormatProvider formatProvider, OracleConnection conn)
602                 {
603                         object ovalue = GetValue (formatProvider, conn);
604
605                         switch (DataType) {
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;
617                                 return lob;
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);
638                         default:
639                                 // TODO: do other types
640                                 throw new NotImplementedException ();
641                         }
642                 }
643
644                 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
645                 internal DateTime UnpackDate ()
646                 {
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);
654
655                         if (hour == 0)
656                                 hour ++;
657                         if (minute == 0)
658                                 minute ++;
659                         if (second == 0)
660                                 second ++;
661
662                         return new DateTime ((century - 100) * 100 + (year - 100),
663                                                 month,
664                                                 day,
665                                                 hour - 1,
666                                                 minute - 1,
667                                                 second - 1);
668
669                 }
670
671                 #endregion // Methods
672         }
673 }