[bugfix]667855 - Fix handling of oracle raw data types sanely.
[mono.git] / mcs / class / System.Data.OracleClient / System.Data.OracleClient / OracleParameter.cs
1 //
2 // OracleParameter.cs
3 //
4 // Part of the Mono class libraries at
5 // mcs/class/System.Data.OracleClient/System.Data.OracleClient
6 //
7 // Assembly: System.Data.OracleClient.dll
8 // Namespace: System.Data.OracleClient
9 //
10 // Authors:
11 //    Tim Coleman <tim@timcoleman.com>
12 //    Daniel Moragn <monodanmorg@yahoo.com>
13 //    Hubert FONGARNAND <informatique.internet@fiducial.fr>
14 //        Veerapuram Varadhan  <vvaradhan@novell.com>   
15 //
16 // Copyright (C) Tim Coleman , 2003
17 // Copyright (C) Daniel Morgan, 2005, 2008, 2009
18 // Copyright (C) Hubert FONGARNAND, 2005
19 // Copyright (C) Novell Inc, 2009
20 //
21 // Licensed under the MIT/X11 License.
22 //
23
24 using System;
25 using System.Collections;
26 using System.ComponentModel;
27 using System.Data;
28 #if NET_2_0
29 using System.Data.Common;
30 #endif
31 using System.Data.SqlTypes;
32 using System.Data.OracleClient.Oci;
33 using System.Globalization;
34 using System.Runtime.InteropServices;
35 using System.Text;
36
37 namespace System.Data.OracleClient
38 {
39         [TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
40         public sealed class OracleParameter :
41 #if NET_2_0
42                 DbParameter, IDbDataParameter, ICloneable
43 #else
44                 MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
45 #endif
46         {
47                 #region Fields
48
49                 string name;
50                 OracleType oracleType = OracleType.VarChar;
51                 OciDataType ociType;
52                 int size;
53                 ParameterDirection direction = ParameterDirection.Input;
54                 bool isNullable;
55                 byte precision;
56                 byte scale;
57                 string srcColumn;
58 #if NET_2_0
59                 bool sourceColumnNullMapping;
60 #endif
61                 DataRowVersion srcVersion;
62                 DbType dbType = DbType.AnsiString;
63                 int offset;
64                 bool sizeSet;
65                 bool oracleTypeSet;
66                 object value = DBNull.Value;
67                 OciLobLocator lobLocator;  // only if Blob or Clob
68                 IntPtr bindOutValue = IntPtr.Zero;
69                 OciDateTimeDescriptor dateTimeDesc;
70                 IntPtr cursor = IntPtr.Zero;
71
72                 OracleParameterCollection container;
73                 OciBindHandle bindHandle;
74                 OracleConnection connection;
75                 byte[] bytes;
76                 IntPtr bindValue = IntPtr.Zero;
77                 bool useRef;
78                 OciDataType bindType;
79
80                 short indicator; 
81                 int bindSize;
82                 bool sizeManuallySet;
83
84                 #endregion // Fields
85
86                 #region Constructors
87
88                 // constructor for cloning the object
89                 private OracleParameter (OracleParameter value)
90                 {
91                         this.name = value.name;
92                         this.oracleType = value.oracleType;
93                         this.ociType = value.ociType;
94                         this.size = value.size;
95                         this.direction = value.direction;
96                         this.isNullable = value.isNullable;
97                         this.precision = value.precision;
98                         this.scale = value.scale;
99                         this.srcColumn = value.srcColumn;
100                         this.srcVersion = value.srcVersion;
101                         this.dbType = value.dbType;
102                         this.offset = value.offset;
103                         this.sizeSet = value.sizeSet;
104                         this.value = value.value;
105                         this.lobLocator = value.lobLocator;
106                         this.oracleTypeSet = value.oracleTypeSet;
107                 }
108
109                 public OracleParameter ()
110                 {
111                         this.name = String.Empty;
112                         this.oracleType = OracleType.VarChar;
113                         this.size = 0;
114                         this.direction = ParameterDirection.Input;
115                         this.isNullable = false;
116                         this.precision = 0;
117                         this.scale = 0;
118                         this.srcColumn = String.Empty;
119                         this.srcVersion = DataRowVersion.Current;
120                         this.value = null;
121                         this.oracleTypeSet = false;
122                 }
123
124                 public OracleParameter (string name, object value)
125                 {
126                         this.name = name;
127                         this.value = value;
128
129                         srcColumn = string.Empty;
130                         SourceVersion = DataRowVersion.Current;
131                         InferOracleType (value);                        
132 #if NET_2_0
133                         // Find the OciType before inferring for the size
134                         if (value != null && value != DBNull.Value) {
135                                 this.sizeSet = true;
136                                 this.size = InferSize ();
137                         }
138 #endif
139                 }
140
141                 public OracleParameter (string name, OracleType oracleType)
142                         : this (name, oracleType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
143                 {
144                 }
145
146                 public OracleParameter (string name, OracleType oracleType, int size)
147                         : this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
148                 {
149                 }
150
151                 public OracleParameter (string name, OracleType oracleType, int size, string srcColumn)
152                         : this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
153                 {
154                 }
155
156 #if NET_2_0
157                 public OracleParameter (string name, OracleType oracleType, int size, ParameterDirection direction, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value)
158                 {
159                         this.name = name;
160                         if (size < 0)
161                                 throw new ArgumentException("Size must be not be negative.");
162                         
163                         this.value = value;
164                         this.size = size;
165                         Direction = direction;
166
167                         // set sizeSet to true iff value is not-null or non-zero size value
168                         if (((value != null && value != DBNull.Value) || Direction == ParameterDirection.Output) && 
169                             size > 0)                       
170                                 this.sizeSet = true;
171
172                         SourceColumnNullMapping = sourceColumnNullMapping;
173                         OracleType = oracleType;
174                         SourceColumn = sourceColumn;
175                         SourceVersion = sourceVersion;
176                 }
177 #endif
178
179                 public OracleParameter (string name, OracleType oracleType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
180                 {
181                         this.name = name;
182                         if (size < 0)
183                                 throw new ArgumentException("Size must be not be negative.");
184                         
185                         this.value = value;
186                         this.size = size;
187
188                         Direction = direction;
189                         
190                         // set sizeSet to true iff value is not-null or non-zero size value
191                         if (((value != null && value != DBNull.Value) || Direction == ParameterDirection.Output) && 
192                             size > 0)                       
193                                 this.sizeSet = true;
194
195                         this.isNullable = isNullable;
196                         this.precision = precision;
197                         this.scale = scale;
198
199                         OracleType = oracleType;
200                         SourceColumn = srcColumn;
201                         SourceVersion = srcVersion;
202                 }
203
204                 #endregion // Constructors
205
206                 #region Properties
207
208                 internal OracleParameterCollection Container {
209                         get { return container; }
210                         set { container = value; }
211                 }
212
213 #if !NET_2_0
214                 [Browsable (false)]
215                 [RefreshProperties (RefreshProperties.All)]
216                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
217 #endif
218                 public
219 #if NET_2_0
220                 override
221 #endif
222                 DbType DbType {
223                         get { return dbType; }
224                         set { SetDbType (value); }
225                 }
226
227 #if !NET_2_0
228                 [DefaultValue (ParameterDirection.Input)]
229 #endif
230                 [RefreshProperties (RefreshProperties.All)]
231                 public
232 #if NET_2_0
233                 override
234 #endif
235                 ParameterDirection Direction {
236                         get { return direction; }
237                         set { 
238                                 direction = value; 
239                                 if (this.size > 0 && direction == ParameterDirection.Output)
240                                         this.sizeSet = true;
241                         }
242                 }
243
244 #if !NET_2_0
245                 [Browsable (false)]
246                 [DesignOnly (true)]
247                 [DefaultValue (false)]
248                 [EditorBrowsable (EditorBrowsableState.Never)]
249 #endif
250                 public
251 #if NET_2_0
252                 override
253 #endif
254                 bool IsNullable {
255                         get { return isNullable; }
256                         set { isNullable = value; }
257                 }
258
259 #if NET_2_0
260                 [EditorBrowsable (EditorBrowsableState.Advanced)]
261 #else
262                 [DefaultValue (0)]
263 #endif
264                 [Browsable (false)]
265                 public int Offset {
266                         get { return offset; }
267                         set { offset = value; }
268                 }
269
270                 [DefaultValue (OracleType.VarChar)]
271                 [RefreshProperties (RefreshProperties.All)]
272 #if NET_2_0
273                 [DbProviderSpecificTypeProperty (true)]
274 #endif
275                 public OracleType OracleType {
276                         get { return oracleType; }
277                         set { 
278                                 oracleTypeSet = true;
279                                 SetOracleType (value, false); 
280                         }
281                 }
282
283 #if !NET_2_0
284                 [DefaultValue ("")]
285 #endif
286                 public
287 #if NET_2_0
288                 override
289 #endif
290                 string ParameterName {
291                         get {
292                                 if (name == null)
293                                         return string.Empty;
294                                 return name;
295                         }
296                         set { name = value; }
297                 }
298
299 #if NET_2_0
300                 [Browsable (false)]
301                 [EditorBrowsable (EditorBrowsableState.Never)]
302                 [Obsolete("Set the precision of a decimal use the Math classes.")]
303 #else
304                 [DefaultValue (0)]
305 #endif
306                 public byte Precision {
307                         get { return precision; }
308                         set { /* NO EFFECT*/ }
309                 }
310
311 #if NET_2_0
312                 [Browsable (false)]
313                 [EditorBrowsable (EditorBrowsableState.Never)]
314                 [Obsolete("Set the precision of a decimal use the Math classes.")]
315 #else
316                 [DefaultValue (0)]
317 #endif
318                 public byte Scale {
319                         get { return scale; }
320                         set { /* NO EFFECT*/ }
321                 }
322
323 #if !NET_2_0
324                 [DefaultValue (0)]
325 #endif
326                 public
327 #if NET_2_0
328                 override
329 #endif
330                 int Size {
331                         get { return size; }
332                         set {
333                                 sizeSet = true;
334                                 size = value;
335                                 sizeManuallySet = true;
336                         }
337                 }
338
339 #if !NET_2_0
340                 [DefaultValue ("")]
341 #endif
342                 public
343 #if NET_2_0
344                 override
345 #endif
346                 string SourceColumn {
347                         get { return srcColumn; }
348                         set { srcColumn = value; }
349                 }
350
351 #if NET_2_0
352                 [MonoTODO]
353                 public override bool SourceColumnNullMapping {
354                         get { return sourceColumnNullMapping; }
355                         set { sourceColumnNullMapping = value; }
356                 }
357 #endif
358
359 #if !NET_2_0
360                 [DefaultValue ("Current")]
361 #endif
362                 public
363 #if NET_2_0
364                 override
365 #endif
366                 DataRowVersion SourceVersion {
367                         get { return srcVersion; }
368                         set { srcVersion = value; }
369                 }
370
371 #if !NET_2_0
372                 [DefaultValue (null)]
373 #endif
374                 [RefreshProperties (RefreshProperties.All)]
375                 [TypeConverter (typeof(StringConverter))]
376                 public
377 #if NET_2_0
378                 override
379 #endif
380                 object Value {
381                         get { return this.value; }
382                         set {
383                                 this.value = value;
384                                 if (!oracleTypeSet)
385                                         InferOracleType (value);
386 #if NET_2_0
387                                 if (value != null && value != DBNull.Value) {
388                                         this.size = InferSize ();
389                                         this.sizeSet = true;
390                                 }
391 #endif
392                         }
393                 }
394
395                 #endregion // Properties
396
397                 #region Methods
398
399                 private void AssertSizeIsSet ()
400                 {
401                         switch (ociType) {
402                         case OciDataType.VarChar2:
403                         case OciDataType.String:
404                         case OciDataType.VarChar:
405                         case OciDataType.Char:
406                         case OciDataType.CharZ:
407                         case OciDataType.OciString:
408                                 if (!sizeSet)
409                                         throw new Exception ("Size must be set.");
410                                 break;
411                         default:
412                                 break;
413                         }
414                 }
415
416                 internal void Bind (OciStatementHandle statement, OracleConnection con, uint pos)
417                 {
418                         connection = con;
419
420                         if (bindHandle == null)
421                                 bindHandle = new OciBindHandle ((OciHandle) statement);
422
423                         IntPtr tmpHandle = bindHandle.Handle;
424
425                         if (Direction != ParameterDirection.Input)
426                                 AssertSizeIsSet ();
427                         if (!sizeSet)
428                                 size = InferSize ();
429
430                         bindSize = size;
431                         object v = value;
432                         int status = 0;
433                         bindType = ociType;
434                         int rsize = 0;
435
436                         string svalue;
437                         string sDate;
438                         DateTime dt;
439                         bool isnull = false;
440                         int byteCount;
441                         byte[] byteArrayLen;
442
443                         if (direction == ParameterDirection.Input || direction == ParameterDirection.InputOutput) {
444                                 if (v == null)
445                                         isnull = true;
446                                 else if (v is DBNull)
447                                         isnull = true;
448                                 else {
449                                         INullable mynullable = v as INullable;
450                                         if (mynullable != null)
451                                                 isnull = mynullable.IsNull;
452                                 }                                       
453                         } 
454
455                         if (isnull == true && direction == ParameterDirection.Input) {
456                                 indicator = 0;
457                                 bindType = OciDataType.VarChar2;
458                                 bindSize = 0;
459                         } else {
460                                 switch(ociType) {
461                                 case OciDataType.VarChar2:
462                                 case OciDataType.String:
463                                 case OciDataType.VarChar:
464                                 case OciDataType.Char:
465                                 case OciDataType.CharZ:
466                                 case OciDataType.OciString:
467                                         bindType = OciDataType.String;
468                                         indicator = 0;
469                                         svalue = "\0";
470                                         // convert value from managed type to type to marshal
471                                         if (direction == ParameterDirection.Input || 
472                                                 direction == ParameterDirection.InputOutput) {
473
474                                                 svalue = v.ToString ();
475
476                                                 if (direction == ParameterDirection.Input && size > 0 && svalue.Length > size)
477                                                         svalue = svalue.Substring(0, size);
478
479                                                 svalue = svalue.ToString () + '\0';
480                                                 
481                                                 // convert managed type to memory allocated earlier
482                                                 // in this case using OCIUnicodeToCharSet
483                                                 rsize = 0;
484                                                 // Get size of buffer
485                                                 status = OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
486
487                                                 if (direction == ParameterDirection.Input)
488                                                         bindSize = rsize;
489                                                 else {
490                                                         // this cannot be rsize because you need room for the output after the execute
491                                                         bindSize = Encoding.UTF8.GetMaxByteCount (Size + 1);
492                                                 }
493
494                                                 // allocate memory based on bind size
495                                                 bytes = new byte [bindSize];
496
497                                                 // Fill buffer
498                                                 status = OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
499                                         } else {
500                                                 // for Output and ReturnValue parameters, get size in bytes                                     
501                                                 bindSize = Encoding.UTF8.GetMaxByteCount (size + 1);
502                                                 // allocate memory for oracle to place the results for the Return or Output param                                               
503                                                 bytes = new byte [bindSize];
504                                         }
505                                         break;
506                                 case OciDataType.Date:
507                                         bindType = OciDataType.Date;
508                                         bindSize = 7;
509                                         // convert value from managed type to type to marshal
510                                         if (direction == ParameterDirection.Input || 
511                                                 direction == ParameterDirection.InputOutput) {
512
513                                                 if (isnull)
514                                                         bytes = new byte [7];
515                                                 else {
516                                                         sDate = "";
517                                                         dt = DateTime.MinValue;
518                                                         if (v is String) {
519                                                                 sDate = (string) v;
520                                                                 dt = DateTime.Parse (sDate);
521                                                         }
522                                                         else if (v is DateTime)
523                                                                 dt = (DateTime) v;
524                                                         else if (v is OracleString) {
525                                                                 sDate = v.ToString ();
526                                                                 dt = DateTime.Parse (sDate);
527                                                         }
528                                                         else if (v is OracleDateTime) {
529                                                                 OracleDateTime odt = (OracleDateTime) v;
530                                                                 dt = (DateTime) odt.Value;
531                                                         }
532                                                         else
533                                                                 throw new NotImplementedException ("For OracleType.DateTime, data type not implemented: " + v.GetType().ToString() + ".");
534
535                                                         // for Input and InputOuput, create byte array and pack DateTime into it
536                                                         bytes = PackDate (dt);
537                                                 }
538                                         } else  {
539                                                 // allocate 7-byte array for Output and ReturnValue to put date
540                                                 bytes = new byte [7];
541                                         }
542                                         break;
543                                 case OciDataType.TimeStamp:
544                                         dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
545                                         if (dateTimeDesc == null) {
546                                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
547                                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
548                                         }
549                                         dateTimeDesc.ErrorHandle = connection.ErrorHandle;
550                                         bindSize = 11;
551                                         bindType = OciDataType.TimeStamp;
552                                         bindOutValue = dateTimeDesc.Handle;
553                                         bindValue = dateTimeDesc.Handle;
554                                         useRef = true;
555                                         if (direction == ParameterDirection.Input || 
556                                                 direction == ParameterDirection.InputOutput) {
557
558                                                 dt = DateTime.MinValue;
559                                                 sDate = "";
560                                                 if (isnull)
561                                                         indicator = -1;
562                                                 else if (v is String) {
563                                                         sDate = (string) v;
564                                                         dt = DateTime.Parse (sDate);
565                                                 }
566                                                 else if (v is DateTime)
567                                                         dt = (DateTime) v;
568                                                 else if (v is OracleString) {
569                                                         sDate = (string) v;
570                                                         dt = DateTime.Parse (sDate);
571                                                 }
572                                                 else if (v is OracleDateTime) {
573                                                         OracleDateTime odt = (OracleDateTime) v;
574                                                         dt = (DateTime) odt.Value;
575                                                 }
576                                                 else
577                                                         throw new NotImplementedException ("For OracleType.Timestamp, data type not implemented: " + v.GetType().ToString()); // ?
578
579                                                 short year = (short) dt.Year;
580                                                 byte month = (byte) dt.Month;
581                                                 byte day = (byte) dt.Day;
582                                                 byte hour = (byte) dt.Hour;
583                                                 byte min = (byte) dt.Minute;
584                                                 byte sec = (byte) dt.Second;
585                                                 uint fsec = (uint) dt.Millisecond;
586                                                 string timezone = "";
587                                                 dateTimeDesc.SetDateTime (connection.Session,
588                                                         connection.ErrorHandle,
589                                                         year, month, day, hour, min, sec, fsec,
590                                                         timezone);
591                                         }
592                                         break;
593                                 case OciDataType.Integer:
594                                 case OciDataType.Float:
595                                 case OciDataType.Number:
596                                         bindType = OciDataType.String;
597                                         indicator = 0;
598                                         svalue = "\0";
599                                         // convert value from managed type to type to marshal
600                                         if (direction == ParameterDirection.Input || 
601                                                 direction == ParameterDirection.InputOutput) {
602
603                                                 svalue = null;
604                                                 if(v is IFormattable)
605                                                         svalue = ((IFormattable)v).ToString (null, con.SessionFormatProvider);
606                                                 else if (v is OracleNumber)
607                                                         svalue = ((OracleNumber)v).ToString(con.SessionFormatProvider);
608                                                 else
609                                                         svalue = v.ToString();
610
611                                                 svalue = svalue + "\0";
612
613                                                 rsize = 0;
614                                                 // Get size of buffer
615                                                 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
616
617                                                 // Fill buffer 
618                                                 
619                                                 if (direction == ParameterDirection.Input)
620                                                         bindSize = rsize;
621                                                 else
622                                                         bindSize = 30; // need room for output possibly being bigger than the input
623                                                 
624                                                 bytes = new byte [bindSize];
625                                                 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
626                                         } else {
627                                                 // Output and ReturnValue parameters allocate memory
628                                                 bindSize = 30;
629                                                 bytes = new byte [bindSize];
630                                         } 
631                                         break;
632                                 case OciDataType.Long:
633                                 case OciDataType.LongVarChar:
634                                         bindType = OciDataType.LongVarChar;
635
636                                         // FIXME: use piecewise fetching for Long, Clob, Blob, and Long Raw
637                                         // See http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/oci05bnd.htm#sthref724
638                                         
639                                         bindSize = Size + 5; // 4 bytes prepended for length, bytes, 1 byte NUL character
640
641                                         indicator = 0;
642                                         svalue = "\0";
643                                         // convert value from managed type to type to marshal
644                                         if (direction == ParameterDirection.Input || 
645                                                 direction == ParameterDirection.InputOutput) {
646
647                                                 svalue = v.ToString () + '\0';
648                                         }
649
650                                         bytes = new byte [bindSize];
651                                         // LONG is only ANSI 
652                                         ASCIIEncoding enc = new ASCIIEncoding ();
653                                         
654                                         if (direction == ParameterDirection.Input || 
655                                                 direction == ParameterDirection.InputOutput) {
656                                                 if (svalue.Length > 0) {        
657                                                         byteCount = enc.GetBytes (svalue, 4, svalue.Length, bytes, 0);
658                                                         // LONG VARCHAR prepends a 4-byte length
659                                                         if (byteCount > 0) {
660                                                                 byteArrayLen = BitConverter.GetBytes ((uint) byteCount);
661                                                                 bytes[0] = byteArrayLen[0];
662                                                                 bytes[1] = byteArrayLen[1];
663                                                                 bytes[2] = byteArrayLen[2];
664                                                                 bytes[3] = byteArrayLen[3];
665                                                         }
666                                                 }
667                                         }
668                                         break;
669                                 case OciDataType.Clob:
670                                         if (direction == ParameterDirection.Input) {
671                                                 svalue = v.ToString();
672                                                 rsize = 0;
673
674                                                 // Get size of buffer
675                                                 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
676
677                                                 // Fill buffer
678                                                 bytes = new byte[rsize];
679                                                 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
680
681                                                 bindType = OciDataType.Long;
682                                                 bindSize = bytes.Length;
683                                         } 
684                                         else if (direction == ParameterDirection.InputOutput) {
685                                                 // not the exact error that .net 2.0 throws, but this is better
686                                                 throw new NotImplementedException ("Parameters of OracleType.Clob with direction of InputOutput are not supported.");
687                                         }
688                                         else {
689                                                 // Output and Return parameters
690                                                 bindSize = -1;
691                                                 lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
692                                                 if (lobLocator == null) {
693                                                         OciErrorInfo info = connection.ErrorHandle.HandleError ();
694                                                         throw new OracleException (info.ErrorCode, info.ErrorMessage);
695                                                 }
696                                                 bindOutValue = lobLocator.Handle;
697                                                 bindValue = lobLocator.Handle;
698                                                 lobLocator.ErrorHandle = connection.ErrorHandle;
699                                                 lobLocator.Service = statement.Service;
700                                                 lobLocator.Environment = connection.Environment;
701                                                 useRef = true;
702                                         }
703                                         break;
704                                 case OciDataType.Blob:
705                                         if (direction == ParameterDirection.Input) {
706                                                 if (v is byte[]) {
707                                                         bytes = (byte[]) v;
708                                                         bindType = OciDataType.LongRaw;
709                                                         bindSize = bytes.Length;
710                                                 }
711                                                 else if (v is OracleLob) {
712                                                         OracleLob lob = (OracleLob) v;
713                                                         if (lob.LobType == OracleType.Blob) {
714                                                                 lobLocator = lob.Locator;
715                                                                 bindOutValue = lobLocator.Handle;
716                                                                 bindValue = lobLocator.Handle;
717                                                                 lobLocator.ErrorHandle = connection.ErrorHandle;
718                                                                 lobLocator.Service = connection.ServiceContext;
719                                                                 useRef = true;
720                                                         }
721                                                         else
722                                                                 throw new NotImplementedException("For OracleType.Blob, data type OracleLob of LobType Clob/NClob is not implemented.");
723                                                 }
724                                                 else
725                                                         throw new NotImplementedException ("For OracleType.Blob, data type not implemented: " + v.GetType().ToString()); // ?
726                                         }
727                                         else if (direction == ParameterDirection.InputOutput) {
728                                                 // not the exact error that .net 2.0 throws, but this is better
729                                                 throw new NotImplementedException ("Parameters of OracleType.Blob with direction of InputOutput are not supported.");
730                                         }
731                                         else {
732                                                 bindSize = -1;
733                                                 if (value != null && value is OracleLob) {
734                                                         OracleLob blob = (OracleLob) value;
735                                                         if (blob.LobType == OracleType.Blob)
736                                                                 if (value != OracleLob.Null) {
737                                                                         lobLocator = blob.Locator;
738                                                                         byte[] bs = (byte[]) blob.Value;
739                                                                         bindSize = bs.Length;
740                                                                 }
741                                                 }
742                                                 if (lobLocator == null) {
743                                                         lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
744                                                         if (lobLocator == null) {
745                                                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
746                                                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
747                                                         }
748                                                 }
749                                                 bindOutValue = lobLocator.Handle;
750                                                 bindValue = lobLocator.Handle;
751                                                 lobLocator.ErrorHandle = connection.ErrorHandle;
752                                                 lobLocator.Service = connection.ServiceContext;
753                                                 lobLocator.Environment = connection.Environment;
754                                                 useRef = true;
755                                         }
756                                         break;
757                                 case OciDataType.Raw:
758                                 case OciDataType.VarRaw:
759                                         bindType = OciDataType.VarRaw;
760                                         bindSize = Size + 2; // include 2 bytes prepended to hold the length
761                                         indicator = 0;
762                                         bytes = new byte [bindSize];
763                                         if (direction == ParameterDirection.Input || 
764                                                 direction == ParameterDirection.InputOutput) {
765                                                 byteCount = 0;
766                                                 byte[] val;
767                                                 if (dbType == DbType.Guid)
768                                                         val = ((Guid)v).ToByteArray();
769                                                 else
770                                                         val = v as byte[];
771                                                 if (val.Length > 0) {   
772                                                         byteCount = val.Length;
773                                                         // LONG VARRAW prepends a 4-byte length
774                                                         if (byteCount > 0) {
775                                                                 byteArrayLen = BitConverter.GetBytes ((ushort) byteCount);
776                                                                 bytes[0] = byteArrayLen[0];
777                                                                 bytes[1] = byteArrayLen[1];
778                                                                 Array.ConstrainedCopy (val, 0, bytes, 2, byteCount);
779                                                         }
780                                                 }
781                                         }
782                                         break;
783                                 case OciDataType.LongRaw:
784                                 case OciDataType.LongVarRaw:
785                                         bindType = OciDataType.LongVarRaw;
786                                         bindSize = Size + 4; // include 4 bytes prepended to hold the length
787                                         indicator = 0;
788                                         bytes = new byte [bindSize];
789                                         if (direction == ParameterDirection.Input || 
790                                                 direction == ParameterDirection.InputOutput) {
791                                                 byteCount = 0;
792                                                 byte[] val = v as byte[];
793                                                 if (val.Length > 0) {   
794                                                         byteCount = val.Length;
795                                                         // LONG VARRAW prepends a 4-byte length
796                                                         if (byteCount > 0) {
797                                                                 byteArrayLen = BitConverter.GetBytes ((uint) byteCount);
798                                                                 bytes[0] = byteArrayLen[0];
799                                                                 bytes[1] = byteArrayLen[1];
800                                                                 bytes[2] = byteArrayLen[2];
801                                                                 bytes[3] = byteArrayLen[3];
802                                                                 Array.ConstrainedCopy (val, 0, bytes, 4, byteCount);
803                                                         }
804                                                 }
805                                         }
806                                         break;
807                                 case OciDataType.RowIdDescriptor:
808                                         if (direction == ParameterDirection.Output || 
809                                                 direction == ParameterDirection.InputOutput || 
810                                                 direction == ParameterDirection.ReturnValue) {
811
812                                         size = 10;
813                                         bindType = OciDataType.Char;
814                                         bindSize = size * 2;
815                                         bindOutValue = OciCalls.AllocateClear (bindSize);
816                                         bindValue = bindOutValue;
817                                         } else
818                                                 throw new NotImplementedException("data type RowIdDescriptor as Intput parameters");
819                                         break;
820                                 case OciDataType.RSet: // REF CURSOR
821                                         if (direction == ParameterDirection.Output || 
822                                                 direction == ParameterDirection.InputOutput || 
823                                                 direction == ParameterDirection.ReturnValue) {
824
825                                                 cursor = IntPtr.Zero;
826                                                 OciCalls.OCIHandleAlloc (connection.Environment,
827                                                         out cursor,
828                                                         OciHandleType.Statement,
829                                                         0,
830                                                         IntPtr.Zero);
831                                                         bindSize = 0;
832                                                 bindType = OciDataType.RSet;
833                                         } else
834                                                 throw new NotImplementedException ("data type Ref Cursor not implemented for Input parameters");
835                                         break;
836                                 default:
837                                         throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
838                                 }                       
839                         }
840                         
841                         // Now, call the appropriate OCI Bind function;
842
843                         if (useRef == true) {
844                                 if (bindType == OciDataType.TimeStamp) {
845                                         bindValue = dateTimeDesc.Handle;
846                                         status = OciCalls.OCIBindByNameRef (statement,
847                                                 out tmpHandle,
848                                                 connection.ErrorHandle,
849                                                 ParameterName,
850                                                 ParameterName.Length,
851                                                 ref bindValue,
852                                                 bindSize,
853                                                 bindType,
854                                                 ref indicator,
855                                                 IntPtr.Zero,
856                                                 IntPtr.Zero,
857                                                 0,
858                                                 IntPtr.Zero,
859                                                 0);
860                                 }
861                                 else {
862                                         status = OciCalls.OCIBindByNameRef (statement,
863                                                 out tmpHandle,
864                                                 connection.ErrorHandle,
865                                                 ParameterName,
866                                                 ParameterName.Length,
867                                                 ref bindValue,
868                                                 bindSize,
869                                                 bindType,
870                                                 ref indicator,
871                                                 IntPtr.Zero,
872                                                 IntPtr.Zero,
873                                                 0,
874                                                 IntPtr.Zero,
875                                                 0);
876                                 }
877                         }
878                         else if (bindType == OciDataType.RSet) {
879                                 status = OciCalls.OCIBindByNameRef (statement,
880                                         out tmpHandle,
881                                         connection.ErrorHandle,
882                                         ParameterName,
883                                         ParameterName.Length,
884                                         ref cursor,
885                                         bindSize,
886                                         bindType,
887                                         ref indicator,
888                                         IntPtr.Zero,
889                                         IntPtr.Zero,
890                                         0,
891                                         IntPtr.Zero,
892                                         0);
893                         }
894                         else if (bytes != null) {
895                                 status = OciCalls.OCIBindByNameBytes (statement,
896                                         out tmpHandle,
897                                         connection.ErrorHandle,
898                                         ParameterName,
899                                         ParameterName.Length,
900                                         bytes,
901                                         bindSize,
902                                         bindType,
903                                         ref indicator,
904                                         IntPtr.Zero,
905                                         IntPtr.Zero,
906                                         0,
907                                         IntPtr.Zero,
908                                         0);
909                         }
910                         else {
911                                 status = OciCalls.OCIBindByName (statement,
912                                         out tmpHandle,
913                                         connection.ErrorHandle,
914                                         ParameterName,
915                                         ParameterName.Length, // FIXME: this should be in bytes!
916                                         bindValue,
917                                         bindSize,
918                                         bindType,
919                                         ref indicator,
920                                         IntPtr.Zero,
921                                         IntPtr.Zero,
922                                         0,
923                                         IntPtr.Zero,
924                                         0);
925                         }
926                         OciErrorHandle.ThrowExceptionIfError (connection.ErrorHandle, status);
927
928                         bindHandle.SetHandle (tmpHandle);
929                 }
930
931                 object ICloneable.Clone ()
932                 {
933                         return new OracleParameter(this);
934                 }
935
936                 private void InferOracleType (object value)
937                 {
938                         // Should we throw an exception here?
939                         if (value == null || value == DBNull.Value)
940                                 return;
941                         
942                         Type type = value.GetType ();
943                         string exception = String.Format ("The parameter data type of {0} is invalid.", type.FullName);
944                         switch (type.FullName) {
945                         case "System.Int64":
946                                 SetOracleType (OracleType.Number, true);
947                                 break;
948                         case "System.Boolean":
949                         case "System.Byte":
950                                 SetOracleType (OracleType.Byte, true);
951                                 break;
952                         case "System.String":
953                         case "System.Data.OracleClient.OracleString":
954                                 SetOracleType (OracleType.VarChar, true);
955                                 break;
956                         case "System.Data.OracleClient.OracleDateTime":
957                         case "System.DateTime":
958                                 SetOracleType (OracleType.DateTime, true);
959                                 break;
960                         case "System.Decimal":
961                         case "System.Data.OracleClient.OracleNumber":
962                                 SetOracleType (OracleType.Number, true);
963                                 break;
964                         case "System.Double":
965                                 SetOracleType (OracleType.Double, true);
966                                 break;
967                         case "System.Byte[]":
968                         case "System.Guid":
969                                 SetOracleType (OracleType.Raw, true);
970                                 break;
971                         case "System.Int32":
972                                 SetOracleType (OracleType.Int32, true);
973                                 break;
974                         case "System.Single":
975                                 SetOracleType (OracleType.Float, true);
976                                 break;
977                         case "System.Int16":
978                                 SetOracleType (OracleType.Int16, true);
979                                 break;
980                         case "System.DBNull":
981                                 break; //unable to guess type
982                         case "System.Data.OracleClient.OracleLob":
983                                 SetOracleType (((OracleLob) value).LobType, true); 
984                                 break;
985                         default:
986                                 throw new ArgumentException (exception);
987                         }
988                 }
989
990                 private int InferSize ()
991                 {
992                         int newSize = 0;
993
994                         switch (ociType) {
995                         case OciDataType.VarChar2:
996                         case OciDataType.String:
997                         case OciDataType.VarChar:
998                         case OciDataType.Char:
999                         case OciDataType.CharZ:
1000                         case OciDataType.OciString:
1001                         case OciDataType.Long:
1002                         case OciDataType.LongVarChar:
1003                                 if (sizeManuallySet == true)
1004                                         return size;
1005                                 if (value == null || value == DBNull.Value)
1006                                         newSize = 0;
1007                                 else
1008                                         newSize = value.ToString ().Length;
1009                                 break;
1010                         case OciDataType.RowIdDescriptor:
1011                                 newSize = 10;
1012                                 break;
1013                         case OciDataType.Integer:
1014                         case OciDataType.Number:
1015                         case OciDataType.Float:
1016                                 newSize = 22;
1017                                 break;
1018                         case OciDataType.Date:
1019                                 newSize = 7;
1020                                 break;
1021                         case OciDataType.TimeStamp:
1022                                 newSize = 11;
1023                                 break;
1024                         case OciDataType.Blob:
1025                         case OciDataType.Clob:
1026                         case OciDataType.RSet: // REF CURSOR
1027                                 newSize = -1;
1028                                 break;
1029                         case OciDataType.Raw:
1030                                 if (dbType == DbType.Guid)
1031                                         newSize = ((Guid)value).ToByteArray().Length;
1032                                 else
1033                                         newSize = (value as byte[]).Length;
1034                                 break;
1035                         default:
1036                                 if (value == null || value == DBNull.Value)
1037                                         newSize = 0;
1038                                 else
1039                                         newSize = value.ToString ().Length;
1040                                 break;
1041                         }
1042
1043                         sizeSet = true;
1044
1045                         return newSize;
1046                 }
1047
1048                 private void SetDbType (DbType type)
1049                 {
1050                         string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
1051                         switch (type) {
1052                         case DbType.AnsiString:
1053                                 oracleType = OracleType.VarChar;
1054                                 ociType = OciDataType.VarChar;
1055                                 break;
1056                         case DbType.AnsiStringFixedLength:
1057                                 oracleType = OracleType.Char;
1058                                 ociType = OciDataType.Char;
1059                                 break;
1060                         case DbType.Binary:
1061                         case DbType.Guid:
1062                                 oracleType = OracleType.Raw;
1063                                 ociType = OciDataType.Raw;
1064                                 break;
1065                         case DbType.Boolean:
1066                         case DbType.Byte:
1067                                 oracleType = OracleType.Byte;
1068                                 ociType = OciDataType.Integer;
1069                                 break;
1070                         case DbType.Currency:
1071                         case DbType.Decimal:
1072                         case DbType.Int64:
1073                                 oracleType = OracleType.Number;
1074                                 ociType = OciDataType.Number;
1075                                 break;
1076                         case DbType.Date:
1077                         case DbType.DateTime:
1078                         case DbType.Time:
1079                                 oracleType = OracleType.DateTime;
1080                                 ociType = OciDataType.Char;
1081                                 break;
1082                         case DbType.Double:
1083                                 oracleType = OracleType.Double;
1084                                 ociType = OciDataType.Float;
1085                                 break;
1086                         case DbType.Int16:
1087                                 oracleType = OracleType.Int16;
1088                                 ociType = OciDataType.Integer;
1089                                 break;
1090                         case DbType.Int32:
1091                                 oracleType = OracleType.Int32;
1092                                 ociType = OciDataType.Integer;
1093                                 break;
1094                         case DbType.Object:
1095                                 oracleType = OracleType.Blob;
1096                                 ociType = OciDataType.Blob;
1097                                 break;
1098                         case DbType.Single:
1099                                 oracleType = OracleType.Float;
1100                                 ociType = OciDataType.Float;
1101                                 break;
1102                         case DbType.String:
1103                                 oracleType = OracleType.NVarChar;
1104                                 ociType = OciDataType.VarChar;
1105                                 break;
1106                         case DbType.StringFixedLength:
1107                                 oracleType = OracleType.NChar;
1108                                 ociType = OciDataType.Char;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
1109
1110                                 break;
1111                         default:
1112                                 throw new ArgumentException (exception);
1113                         }
1114                         dbType = type;
1115                 }
1116
1117                 private void SetOracleType (OracleType type, bool inferring)
1118                 {
1119                         FreeHandle ();
1120                         Type valType = value.GetType ();
1121                         string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
1122                         switch (type) {
1123                         case OracleType.BFile:
1124                         case OracleType.Blob:
1125                                 dbType = DbType.Binary;
1126                                 ociType = OciDataType.Blob;
1127                                 break;
1128                         case OracleType.LongRaw:
1129                         case OracleType.Raw:
1130                                 if (valType.FullName == "System.Guid")
1131                                         dbType = DbType.Guid;
1132                                 else
1133                                         dbType = DbType.Binary;
1134                                 ociType = OciDataType.Raw;
1135                                 break;
1136                         case OracleType.Byte:
1137                                 dbType = DbType.Byte;
1138                                 ociType = OciDataType.Number;
1139                                 break;
1140                         case OracleType.Char:
1141                                 dbType = DbType.AnsiString;
1142                                 ociType = OciDataType.Char;
1143                                 break;
1144                         case OracleType.Clob:
1145                                 dbType = DbType.AnsiString;
1146                                 ociType = OciDataType.Clob;
1147                                 break;
1148                         case OracleType.LongVarChar:
1149                         case OracleType.RowId:
1150                         case OracleType.VarChar:
1151                                 dbType = DbType.AnsiString;
1152                                 ociType = OciDataType.VarChar;
1153                                 break;
1154                         case OracleType.Cursor: // REF CURSOR
1155                                 ociType = OciDataType.RSet;
1156                                 dbType = DbType.Object;
1157                                 break;
1158                         case OracleType.IntervalDayToSecond:
1159                                 dbType = DbType.AnsiStringFixedLength;
1160                                 ociType = OciDataType.Char;
1161                                 break;
1162                         case OracleType.Timestamp:
1163                         case OracleType.TimestampLocal:
1164                         case OracleType.TimestampWithTZ:
1165                                 dbType = DbType.DateTime;
1166                                 ociType = OciDataType.TimeStamp;
1167                                 break;
1168                         case OracleType.DateTime:
1169                                 dbType = DbType.DateTime;
1170                                 ociType = OciDataType.Date;
1171                                 break;
1172                         case OracleType.Double:
1173                                 dbType = DbType.Double;
1174                                 ociType = OciDataType.Number;
1175                                 break;
1176                         case OracleType.Float:
1177                                 dbType = DbType.Single;
1178                                 ociType = OciDataType.Number;
1179                                 break;
1180                         case OracleType.Int16:
1181                                 dbType = DbType.Int16;
1182                                 ociType = OciDataType.Number;
1183                                 break;
1184                         case OracleType.Int32:
1185                         case OracleType.IntervalYearToMonth:
1186                                 dbType = DbType.Int32;
1187                                 ociType = OciDataType.Number;
1188                                 break;
1189                         case OracleType.NChar:
1190                                 dbType = DbType.StringFixedLength;
1191                                 ociType = OciDataType.Char;
1192                                 break;
1193                         case OracleType.NClob:
1194                         case OracleType.NVarChar:
1195                                 dbType = DbType.String;
1196                                 ociType = OciDataType.Char;
1197                                 break;
1198                         case OracleType.Number:
1199                                 dbType = DbType.VarNumeric;
1200                                 ociType = OciDataType.Number;
1201                                 break;
1202                         case OracleType.SByte:
1203                                 dbType = DbType.SByte;
1204                                 ociType = OciDataType.Number;
1205                                 break;
1206                         case OracleType.UInt16:
1207                                 dbType = DbType.UInt16;
1208                                 ociType = OciDataType.Number;
1209                                 break;
1210                         case OracleType.UInt32:
1211                                 dbType = DbType.UInt32;
1212                                 ociType = OciDataType.Number;
1213                                 break;
1214                         default:
1215                                 throw new ArgumentException (exception);
1216                         }
1217
1218                         if (!oracleTypeSet || !inferring )
1219                                 oracleType = type;
1220                 }
1221
1222 #if NET_2_0
1223                 public override void ResetDbType ()
1224                 {
1225                         ResetOracleType ();
1226                 }
1227
1228                 public void ResetOracleType ()
1229                 {
1230                         oracleTypeSet = false;
1231                         InferOracleType (value);
1232                 }
1233 #endif // NET_2_0
1234
1235                 public override string ToString ()
1236                 {
1237                         return ParameterName;
1238                 }
1239
1240                 private void GetOutValue (OracleCommand cmd)
1241                 {
1242                         // used to update the parameter value
1243                         // for Output, the output of InputOutput, and Return parameters
1244                         value = DBNull.Value;
1245                         if (indicator == -1)
1246                                 return;
1247
1248                         int rsize = 0;
1249                         IntPtr env = IntPtr.Zero;
1250                         StringBuilder ret = null;
1251
1252                         // FIXME: redo all types - see how Char, Number, and Date are done
1253                         // here and in Bind()
1254
1255                         switch (ociType) {
1256                         case OciDataType.VarChar2:
1257                         case OciDataType.String:
1258                         case OciDataType.VarChar:
1259                         case OciDataType.Char:
1260                         case OciDataType.CharZ:
1261                         case OciDataType.OciString:
1262                         case OciDataType.RowIdDescriptor:
1263                                 // Get length of returned string
1264                                 rsize = 0;
1265                                 env = cmd.Connection.Environment;
1266                                 OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
1267
1268                                 // Get string
1269                                 ret = new StringBuilder(rsize);
1270                                 OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
1271
1272                                 value = ret.ToString ();
1273                                 break;
1274                         case OciDataType.Long:
1275                         case OciDataType.LongVarChar:
1276                                 int longSize = 0;
1277                                 if (BitConverter.IsLittleEndian)
1278                                         longSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
1279                                 else
1280                                         longSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
1281
1282                                 ASCIIEncoding encoding = new ASCIIEncoding ();
1283                                 value = encoding.GetString (bytes, 4, longSize);
1284                                 encoding = null;
1285                                 break;
1286                         case OciDataType.LongRaw:
1287                         case OciDataType.LongVarRaw:
1288                                 int longrawSize = 0;
1289                                 if (BitConverter.IsLittleEndian)
1290                                         longrawSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
1291                                 else
1292                                         longrawSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
1293
1294                                 byte[] longraw_buffer = new byte [longrawSize];
1295                                 Array.ConstrainedCopy (bytes, 4, longraw_buffer, 0, longrawSize);
1296                                 value = longraw_buffer;
1297                                 break;
1298                         case OciDataType.Raw:
1299                         case OciDataType.VarRaw:
1300                                 int rawSize = 0;
1301                                 if (BitConverter.IsLittleEndian)
1302                                         rawSize = (int) BitConverter.ToInt16 (new byte [] {bytes [0], bytes [1]}, 0);
1303                                 else
1304                                         rawSize = (int) BitConverter.ToInt16 (new byte [] {bytes [1], bytes [0]}, 0);
1305
1306                                 byte[] raw_buffer = new byte [rawSize];
1307                                 Array.ConstrainedCopy (bytes, 2, raw_buffer, 0, rawSize);
1308                                 value = raw_buffer;
1309                                 break;
1310                         case OciDataType.Integer:
1311                         case OciDataType.Number:
1312                         case OciDataType.Float:
1313                                 rsize = 0;
1314                                 env = cmd.Connection.Environment;
1315                                 OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
1316
1317                                 // Get string
1318                                 ret = new StringBuilder(rsize);
1319                                 OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
1320
1321                                 // if not empty, parse string as a decimal using session format
1322                                 if (ret.Length > 0) {
1323                                         switch (dbType) {
1324                                         case DbType.UInt16: 
1325                                                 value = UInt16.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1326                                                 break;
1327                                         case DbType.UInt32: 
1328                                                 value = UInt32.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1329                                                 break;
1330                                         case DbType.Int16:
1331                                                 value = Int16.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1332                                                 break;                                                  
1333                                         case DbType.Int32:
1334                                                 value = Int32.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1335                                                 break;
1336                                         default:
1337                                                 value = Decimal.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1338                                                 break;
1339                                         }
1340                                 }
1341                                 break;
1342                         case OciDataType.TimeStamp:
1343                                 value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
1344                                 break;
1345                         case OciDataType.Date:
1346                                 value = UnpackDate (bytes);
1347                                 break;
1348                         case OciDataType.Blob:
1349                         case OciDataType.Clob:
1350                                 if (value != null && value is OracleLob && value != OracleLob.Null) {
1351                                         OracleLob lob2 = (OracleLob) value;
1352                                         lob2.connection = connection;
1353                                 }
1354                                 else {
1355                                         OracleLob lob = new OracleLob (lobLocator, ociType);
1356                                         lob.connection = connection;
1357                                         value = lob;
1358                                 }
1359                                 break;
1360                         case OciDataType.RSet: // REF CURSOR                            
1361                                 OciStatementHandle cursorStatement = GetOutRefCursor (cmd);
1362                                 value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
1363                                 break;
1364                         default:
1365                                 throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
1366                         }
1367                 }
1368
1369                 internal OciStatementHandle GetOutRefCursor (OracleCommand cmd) 
1370                 {
1371                                 OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.ServiceContext, cursor);
1372
1373                                 cursorStatement.ErrorHandle = cmd.ErrorHandle;
1374                                 cursorStatement.Command = cmd;
1375                                 cursorStatement.SetupRefCursorResult (cmd.Connection);
1376                                 cursorStatement.Service = cmd.Connection.ServiceContext;
1377                                 cursor = IntPtr.Zero;
1378                                 return cursorStatement;                 
1379                 }
1380
1381                 internal void Update (OracleCommand cmd)
1382                 {
1383                         if (Direction != ParameterDirection.Input)
1384                                 GetOutValue (cmd);
1385
1386                         FreeHandle ();
1387                 }
1388
1389                 internal void FreeHandle ()
1390                 {
1391                         switch (ociType) {
1392                         case OciDataType.Clob:
1393                         case OciDataType.Blob:
1394                                 lobLocator = null;
1395                                 break;
1396                         case OciDataType.TimeStamp:
1397                                 break;
1398                         default:
1399                                 Marshal.FreeHGlobal (bindOutValue);
1400                                 break;
1401                         }
1402
1403                         bindOutValue = IntPtr.Zero;
1404                         bindValue = IntPtr.Zero;
1405
1406                         bindHandle = null;
1407                         connection = null;
1408                 }
1409
1410                 // copied from OciDefineHandle
1411                 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
1412                 private DateTime UnpackDate (byte[] bytes)
1413                 {
1414                         byte century = bytes [0];
1415                         byte year    = bytes [1];
1416                         byte month   = bytes [2];
1417                         byte day     = bytes [3];
1418                         byte hour    = bytes [4];
1419                         byte minute  = bytes [5];
1420                         byte second  = bytes [6];
1421
1422
1423                         return new DateTime ((century - 100) * 100 + (year - 100),
1424                                                 month,
1425                                                 day,
1426                                                 hour - 1,
1427                                                 minute - 1,
1428                                                 second - 1);
1429
1430                 }
1431
1432                 private byte[] PackDate (DateTime dateValue)
1433                 {
1434                         byte[] buffer = new byte[7];
1435
1436                         buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
1437                         buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
1438                         buffer[2] = (byte)dateValue.Month;
1439                         buffer[3] = (byte)dateValue.Day;
1440                         buffer[4] = (byte)(dateValue.Hour+1);
1441                         buffer[5] = (byte)(dateValue.Minute+1);
1442                         buffer[6] = (byte)(dateValue.Second+1);
1443
1444                         return buffer;
1445                 }
1446
1447                 #endregion // Methods
1448
1449                 internal sealed class OracleParameterConverter : ExpandableObjectConverter
1450                 {
1451                         public OracleParameterConverter ()
1452                         {
1453                         }
1454
1455                         [MonoTODO]
1456                         public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
1457                         {
1458                                 throw new NotImplementedException ();
1459                         }
1460
1461                         [MonoTODO]
1462                         public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
1463                         {
1464                                 throw new NotImplementedException ();
1465                         }
1466                 }
1467         }
1468 }