Merge pull request #799 from kebby/master
[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                                                 if (cursor != IntPtr.Zero) {
825                                                         OciCalls.OCIHandleFree (cursor,
826                                                                 OciHandleType.Statement);
827                                                         cursor = IntPtr.Zero;
828                                                 }
829                                                 OciCalls.OCIHandleAlloc (connection.Environment,
830                                                         out cursor,
831                                                         OciHandleType.Statement,
832                                                         0,
833                                                         IntPtr.Zero);
834                                                         bindSize = 0;
835                                                 bindType = OciDataType.RSet;
836                                         } else
837                                                 throw new NotImplementedException ("data type Ref Cursor not implemented for Input parameters");
838                                         break;
839                                 default:
840                                         throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
841                                 }                       
842                         }
843                         
844                         // Now, call the appropriate OCI Bind function;
845
846                         if (useRef == true) {
847                                 if (bindType == OciDataType.TimeStamp) {
848                                         bindValue = dateTimeDesc.Handle;
849                                         status = OciCalls.OCIBindByNameRef (statement,
850                                                 out tmpHandle,
851                                                 connection.ErrorHandle,
852                                                 ParameterName,
853                                                 ParameterName.Length,
854                                                 ref bindValue,
855                                                 bindSize,
856                                                 bindType,
857                                                 ref indicator,
858                                                 IntPtr.Zero,
859                                                 IntPtr.Zero,
860                                                 0,
861                                                 IntPtr.Zero,
862                                                 0);
863                                 }
864                                 else {
865                                         status = OciCalls.OCIBindByNameRef (statement,
866                                                 out tmpHandle,
867                                                 connection.ErrorHandle,
868                                                 ParameterName,
869                                                 ParameterName.Length,
870                                                 ref bindValue,
871                                                 bindSize,
872                                                 bindType,
873                                                 ref indicator,
874                                                 IntPtr.Zero,
875                                                 IntPtr.Zero,
876                                                 0,
877                                                 IntPtr.Zero,
878                                                 0);
879                                 }
880                         }
881                         else if (bindType == OciDataType.RSet) {
882                                 status = OciCalls.OCIBindByNameRef (statement,
883                                         out tmpHandle,
884                                         connection.ErrorHandle,
885                                         ParameterName,
886                                         ParameterName.Length,
887                                         ref cursor,
888                                         bindSize,
889                                         bindType,
890                                         ref indicator,
891                                         IntPtr.Zero,
892                                         IntPtr.Zero,
893                                         0,
894                                         IntPtr.Zero,
895                                         0);
896                         }
897                         else if (bytes != null) {
898                                 status = OciCalls.OCIBindByNameBytes (statement,
899                                         out tmpHandle,
900                                         connection.ErrorHandle,
901                                         ParameterName,
902                                         ParameterName.Length,
903                                         bytes,
904                                         bindSize,
905                                         bindType,
906                                         ref indicator,
907                                         IntPtr.Zero,
908                                         IntPtr.Zero,
909                                         0,
910                                         IntPtr.Zero,
911                                         0);
912                         }
913                         else {
914                                 status = OciCalls.OCIBindByName (statement,
915                                         out tmpHandle,
916                                         connection.ErrorHandle,
917                                         ParameterName,
918                                         ParameterName.Length, // FIXME: this should be in bytes!
919                                         bindValue,
920                                         bindSize,
921                                         bindType,
922                                         ref indicator,
923                                         IntPtr.Zero,
924                                         IntPtr.Zero,
925                                         0,
926                                         IntPtr.Zero,
927                                         0);
928                         }
929                         OciErrorHandle.ThrowExceptionIfError (connection.ErrorHandle, status);
930
931                         bindHandle.SetHandle (tmpHandle);
932                 }
933
934                 object ICloneable.Clone ()
935                 {
936                         return new OracleParameter(this);
937                 }
938
939                 private void InferOracleType (object value)
940                 {
941                         // Should we throw an exception here?
942                         if (value == null || value == DBNull.Value)
943                                 return;
944                         
945                         Type type = value.GetType ();
946                         string exception = String.Format ("The parameter data type of {0} is invalid.", type.FullName);
947                         switch (type.FullName) {
948                         case "System.Int64":
949                                 SetOracleType (OracleType.Number, true);
950                                 break;
951                         case "System.Boolean":
952                         case "System.Byte":
953                                 SetOracleType (OracleType.Byte, true);
954                                 break;
955                         case "System.String":
956                         case "System.Data.OracleClient.OracleString":
957                                 SetOracleType (OracleType.VarChar, true);
958                                 break;
959                         case "System.Data.OracleClient.OracleDateTime":
960                         case "System.DateTime":
961                                 SetOracleType (OracleType.DateTime, true);
962                                 break;
963                         case "System.Decimal":
964                         case "System.Data.OracleClient.OracleNumber":
965                                 SetOracleType (OracleType.Number, true);
966                                 break;
967                         case "System.Double":
968                                 SetOracleType (OracleType.Double, true);
969                                 break;
970                         case "System.Byte[]":
971                         case "System.Guid":
972                                 SetOracleType (OracleType.Raw, true);
973                                 break;
974                         case "System.Int32":
975                                 SetOracleType (OracleType.Int32, true);
976                                 break;
977                         case "System.Single":
978                                 SetOracleType (OracleType.Float, true);
979                                 break;
980                         case "System.Int16":
981                                 SetOracleType (OracleType.Int16, true);
982                                 break;
983                         case "System.DBNull":
984                                 break; //unable to guess type
985                         case "System.Data.OracleClient.OracleLob":
986                                 SetOracleType (((OracleLob) value).LobType, true); 
987                                 break;
988                         default:
989                                 throw new ArgumentException (exception);
990                         }
991                 }
992
993                 private int InferSize ()
994                 {
995                         int newSize = 0;
996
997                         switch (ociType) {
998                         case OciDataType.VarChar2:
999                         case OciDataType.String:
1000                         case OciDataType.VarChar:
1001                         case OciDataType.Char:
1002                         case OciDataType.CharZ:
1003                         case OciDataType.OciString:
1004                         case OciDataType.Long:
1005                         case OciDataType.LongVarChar:
1006                                 if (sizeManuallySet == true)
1007                                         return size;
1008                                 if (value == null || value == DBNull.Value)
1009                                         newSize = 0;
1010                                 else
1011                                         newSize = value.ToString ().Length;
1012                                 break;
1013                         case OciDataType.RowIdDescriptor:
1014                                 newSize = 10;
1015                                 break;
1016                         case OciDataType.Integer:
1017                         case OciDataType.Number:
1018                         case OciDataType.Float:
1019                                 newSize = 22;
1020                                 break;
1021                         case OciDataType.Date:
1022                                 newSize = 7;
1023                                 break;
1024                         case OciDataType.TimeStamp:
1025                                 newSize = 11;
1026                                 break;
1027                         case OciDataType.Blob:
1028                         case OciDataType.Clob:
1029                         case OciDataType.RSet: // REF CURSOR
1030                                 newSize = -1;
1031                                 break;
1032                         case OciDataType.Raw:
1033                                 if (dbType == DbType.Guid)
1034                                         newSize = ((Guid)value).ToByteArray().Length;
1035                                 else
1036                                         newSize = (value as byte[]).Length;
1037                                 break;
1038                         default:
1039                                 if (value == null || value == DBNull.Value)
1040                                         newSize = 0;
1041                                 else
1042                                         newSize = value.ToString ().Length;
1043                                 break;
1044                         }
1045
1046                         sizeSet = true;
1047
1048                         return newSize;
1049                 }
1050
1051                 private void SetDbType (DbType type)
1052                 {
1053                         string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
1054                         switch (type) {
1055                         case DbType.AnsiString:
1056                                 oracleType = OracleType.VarChar;
1057                                 ociType = OciDataType.VarChar;
1058                                 break;
1059                         case DbType.AnsiStringFixedLength:
1060                                 oracleType = OracleType.Char;
1061                                 ociType = OciDataType.Char;
1062                                 break;
1063                         case DbType.Binary:
1064                         case DbType.Guid:
1065                                 oracleType = OracleType.Raw;
1066                                 ociType = OciDataType.Raw;
1067                                 break;
1068                         case DbType.Boolean:
1069                         case DbType.Byte:
1070                                 oracleType = OracleType.Byte;
1071                                 ociType = OciDataType.Integer;
1072                                 break;
1073                         case DbType.Currency:
1074                         case DbType.Decimal:
1075                         case DbType.Int64:
1076                                 oracleType = OracleType.Number;
1077                                 ociType = OciDataType.Number;
1078                                 break;
1079                         case DbType.Date:
1080                         case DbType.DateTime:
1081                         case DbType.Time:
1082                                 oracleType = OracleType.DateTime;
1083                                 ociType = OciDataType.Char;
1084                                 break;
1085                         case DbType.Double:
1086                                 oracleType = OracleType.Double;
1087                                 ociType = OciDataType.Float;
1088                                 break;
1089                         case DbType.Int16:
1090                                 oracleType = OracleType.Int16;
1091                                 ociType = OciDataType.Integer;
1092                                 break;
1093                         case DbType.Int32:
1094                                 oracleType = OracleType.Int32;
1095                                 ociType = OciDataType.Integer;
1096                                 break;
1097                         case DbType.Object:
1098                                 oracleType = OracleType.Blob;
1099                                 ociType = OciDataType.Blob;
1100                                 break;
1101                         case DbType.Single:
1102                                 oracleType = OracleType.Float;
1103                                 ociType = OciDataType.Float;
1104                                 break;
1105                         case DbType.String:
1106                                 oracleType = OracleType.NVarChar;
1107                                 ociType = OciDataType.VarChar;
1108                                 break;
1109                         case DbType.StringFixedLength:
1110                                 oracleType = OracleType.NChar;
1111                                 ociType = OciDataType.Char;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
1112
1113                                 break;
1114                         default:
1115                                 throw new ArgumentException (exception);
1116                         }
1117                         dbType = type;
1118                 }
1119
1120                 private void SetOracleType (OracleType type, bool inferring)
1121                 {
1122                         Type valType;
1123                         FreeHandle ();
1124
1125                         if (value == null)
1126                                 valType = typeof(System.DBNull);
1127                         else
1128                                 valType = value.GetType ();
1129
1130                         string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
1131                         switch (type) {
1132                         case OracleType.BFile:
1133                         case OracleType.Blob:
1134                                 dbType = DbType.Binary;
1135                                 ociType = OciDataType.Blob;
1136                                 break;
1137                         case OracleType.LongRaw:
1138                         case OracleType.Raw:
1139                                 if (valType.FullName == "System.Guid")
1140                                         dbType = DbType.Guid;
1141                                 else
1142                                         dbType = DbType.Binary;
1143                                 ociType = OciDataType.Raw;
1144                                 break;
1145                         case OracleType.Byte:
1146                                 dbType = DbType.Byte;
1147                                 ociType = OciDataType.Number;
1148                                 break;
1149                         case OracleType.Char:
1150                                 dbType = DbType.AnsiString;
1151                                 ociType = OciDataType.Char;
1152                                 break;
1153                         case OracleType.Clob:
1154                                 dbType = DbType.AnsiString;
1155                                 ociType = OciDataType.Clob;
1156                                 break;
1157                         case OracleType.LongVarChar:
1158                         case OracleType.RowId:
1159                         case OracleType.VarChar:
1160                                 dbType = DbType.AnsiString;
1161                                 ociType = OciDataType.VarChar;
1162                                 break;
1163                         case OracleType.Cursor: // REF CURSOR
1164                                 ociType = OciDataType.RSet;
1165                                 dbType = DbType.Object;
1166                                 break;
1167                         case OracleType.IntervalDayToSecond:
1168                                 dbType = DbType.AnsiStringFixedLength;
1169                                 ociType = OciDataType.Char;
1170                                 break;
1171                         case OracleType.Timestamp:
1172                         case OracleType.TimestampLocal:
1173                         case OracleType.TimestampWithTZ:
1174                                 dbType = DbType.DateTime;
1175                                 ociType = OciDataType.TimeStamp;
1176                                 break;
1177                         case OracleType.DateTime:
1178                                 dbType = DbType.DateTime;
1179                                 ociType = OciDataType.Date;
1180                                 break;
1181                         case OracleType.Double:
1182                                 dbType = DbType.Double;
1183                                 ociType = OciDataType.Number;
1184                                 break;
1185                         case OracleType.Float:
1186                                 dbType = DbType.Single;
1187                                 ociType = OciDataType.Number;
1188                                 break;
1189                         case OracleType.Int16:
1190                                 dbType = DbType.Int16;
1191                                 ociType = OciDataType.Number;
1192                                 break;
1193                         case OracleType.Int32:
1194                         case OracleType.IntervalYearToMonth:
1195                                 dbType = DbType.Int32;
1196                                 ociType = OciDataType.Number;
1197                                 break;
1198                         case OracleType.NChar:
1199                                 dbType = DbType.StringFixedLength;
1200                                 ociType = OciDataType.Char;
1201                                 break;
1202                         case OracleType.NClob:
1203                         case OracleType.NVarChar:
1204                                 dbType = DbType.String;
1205                                 ociType = OciDataType.Char;
1206                                 break;
1207                         case OracleType.Number:
1208                                 dbType = DbType.VarNumeric;
1209                                 ociType = OciDataType.Number;
1210                                 break;
1211                         case OracleType.SByte:
1212                                 dbType = DbType.SByte;
1213                                 ociType = OciDataType.Number;
1214                                 break;
1215                         case OracleType.UInt16:
1216                                 dbType = DbType.UInt16;
1217                                 ociType = OciDataType.Number;
1218                                 break;
1219                         case OracleType.UInt32:
1220                                 dbType = DbType.UInt32;
1221                                 ociType = OciDataType.Number;
1222                                 break;
1223                         default:
1224                                 throw new ArgumentException (exception);
1225                         }
1226
1227                         if (!oracleTypeSet || !inferring )
1228                                 oracleType = type;
1229                 }
1230
1231 #if NET_2_0
1232                 public override void ResetDbType ()
1233                 {
1234                         ResetOracleType ();
1235                 }
1236
1237                 public void ResetOracleType ()
1238                 {
1239                         oracleTypeSet = false;
1240                         InferOracleType (value);
1241                 }
1242 #endif // NET_2_0
1243
1244                 public override string ToString ()
1245                 {
1246                         return ParameterName;
1247                 }
1248
1249                 private void GetOutValue (OracleCommand cmd)
1250                 {
1251                         // used to update the parameter value
1252                         // for Output, the output of InputOutput, and Return parameters
1253                         value = DBNull.Value;
1254                         if (indicator == -1)
1255                                 return;
1256
1257                         int rsize = 0;
1258                         IntPtr env = IntPtr.Zero;
1259                         StringBuilder ret = null;
1260
1261                         // FIXME: redo all types - see how Char, Number, and Date are done
1262                         // here and in Bind()
1263
1264                         switch (ociType) {
1265                         case OciDataType.VarChar2:
1266                         case OciDataType.String:
1267                         case OciDataType.VarChar:
1268                         case OciDataType.Char:
1269                         case OciDataType.CharZ:
1270                         case OciDataType.OciString:
1271                         case OciDataType.RowIdDescriptor:
1272                                 // Get length of returned string
1273                                 rsize = 0;
1274                                 env = cmd.Connection.Environment;
1275                                 OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
1276
1277                                 // Get string
1278                                 ret = new StringBuilder(rsize);
1279                                 OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
1280
1281                                 value = ret.ToString ();
1282                                 break;
1283                         case OciDataType.Long:
1284                         case OciDataType.LongVarChar:
1285                                 int longSize = 0;
1286                                 if (BitConverter.IsLittleEndian)
1287                                         longSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
1288                                 else
1289                                         longSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
1290
1291                                 ASCIIEncoding encoding = new ASCIIEncoding ();
1292                                 value = encoding.GetString (bytes, 4, longSize);
1293                                 encoding = null;
1294                                 break;
1295                         case OciDataType.LongRaw:
1296                         case OciDataType.LongVarRaw:
1297                                 int longrawSize = 0;
1298                                 if (BitConverter.IsLittleEndian)
1299                                         longrawSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
1300                                 else
1301                                         longrawSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
1302
1303                                 byte[] longraw_buffer = new byte [longrawSize];
1304                                 Array.ConstrainedCopy (bytes, 4, longraw_buffer, 0, longrawSize);
1305                                 value = longraw_buffer;
1306                                 break;
1307                         case OciDataType.Raw:
1308                         case OciDataType.VarRaw:
1309                                 int rawSize = 0;
1310                                 if (BitConverter.IsLittleEndian)
1311                                         rawSize = (int) BitConverter.ToInt16 (new byte [] {bytes [0], bytes [1]}, 0);
1312                                 else
1313                                         rawSize = (int) BitConverter.ToInt16 (new byte [] {bytes [1], bytes [0]}, 0);
1314
1315                                 byte[] raw_buffer = new byte [rawSize];
1316                                 Array.ConstrainedCopy (bytes, 2, raw_buffer, 0, rawSize);
1317                                 value = raw_buffer;
1318                                 break;
1319                         case OciDataType.Integer:
1320                         case OciDataType.Number:
1321                         case OciDataType.Float:
1322                                 rsize = 0;
1323                                 env = cmd.Connection.Environment;
1324                                 OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
1325
1326                                 // Get string
1327                                 ret = new StringBuilder(rsize);
1328                                 OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
1329
1330                                 // if not empty, parse string as a decimal using session format
1331                                 if (ret.Length > 0) {
1332                                         switch (dbType) {
1333                                         case DbType.UInt16: 
1334                                                 value = UInt16.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1335                                                 break;
1336                                         case DbType.UInt32: 
1337                                                 value = UInt32.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1338                                                 break;
1339                                         case DbType.Int16:
1340                                                 value = Int16.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1341                                                 break;                                                  
1342                                         case DbType.Int32:
1343                                                 value = Int32.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1344                                                 break;
1345                                         default:
1346                                                 value = Decimal.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
1347                                                 break;
1348                                         }
1349                                 }
1350                                 break;
1351                         case OciDataType.TimeStamp:
1352                                 value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
1353                                 break;
1354                         case OciDataType.Date:
1355                                 value = UnpackDate (bytes);
1356                                 break;
1357                         case OciDataType.Blob:
1358                         case OciDataType.Clob:
1359                                 if (value != null && value is OracleLob && value != OracleLob.Null) {
1360                                         OracleLob lob2 = (OracleLob) value;
1361                                         lob2.connection = connection;
1362                                 }
1363                                 else {
1364                                         OracleLob lob = new OracleLob (lobLocator, ociType);
1365                                         lob.connection = connection;
1366                                         value = lob;
1367                                 }
1368                                 break;
1369                         case OciDataType.RSet: // REF CURSOR                            
1370                                 OciStatementHandle cursorStatement = GetOutRefCursor (cmd);
1371                                 value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
1372                                 break;
1373                         default:
1374                                 throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
1375                         }
1376                 }
1377
1378                 internal OciStatementHandle GetOutRefCursor (OracleCommand cmd) 
1379                 {
1380                                 OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.ServiceContext, cursor);
1381
1382                                 cursorStatement.ErrorHandle = cmd.ErrorHandle;
1383                                 cursorStatement.Command = cmd;
1384                                 cursorStatement.SetupRefCursorResult (cmd.Connection);
1385                                 cursorStatement.Service = cmd.Connection.ServiceContext;
1386                                 cursor = IntPtr.Zero;
1387                                 return cursorStatement;                 
1388                 }
1389
1390                 internal void Update (OracleCommand cmd)
1391                 {
1392                         if (Direction != ParameterDirection.Input)
1393                                 GetOutValue (cmd);
1394
1395                         FreeHandle ();
1396                 }
1397
1398                 internal void FreeHandle ()
1399                 {
1400                         switch (ociType) {
1401                         case OciDataType.Clob:
1402                         case OciDataType.Blob:
1403                                 lobLocator = null;
1404                                 break;
1405                         case OciDataType.TimeStamp:
1406                                 break;
1407                         default:
1408                                 Marshal.FreeHGlobal (bindOutValue);
1409                                 break;
1410                         }
1411
1412                         bindOutValue = IntPtr.Zero;
1413                         bindValue = IntPtr.Zero;
1414
1415                         bindHandle = null;
1416                         connection = null;
1417                 }
1418
1419                 // copied from OciDefineHandle
1420                 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
1421                 private DateTime UnpackDate (byte[] bytes)
1422                 {
1423                         byte century = bytes [0];
1424                         byte year    = bytes [1];
1425                         byte month   = bytes [2];
1426                         byte day     = bytes [3];
1427                         byte hour    = bytes [4];
1428                         byte minute  = bytes [5];
1429                         byte second  = bytes [6];
1430
1431
1432                         return new DateTime ((century - 100) * 100 + (year - 100),
1433                                                 month,
1434                                                 day,
1435                                                 hour - 1,
1436                                                 minute - 1,
1437                                                 second - 1);
1438
1439                 }
1440
1441                 private byte[] PackDate (DateTime dateValue)
1442                 {
1443                         byte[] buffer = new byte[7];
1444
1445                         buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
1446                         buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
1447                         buffer[2] = (byte)dateValue.Month;
1448                         buffer[3] = (byte)dateValue.Day;
1449                         buffer[4] = (byte)(dateValue.Hour+1);
1450                         buffer[5] = (byte)(dateValue.Minute+1);
1451                         buffer[6] = (byte)(dateValue.Second+1);
1452
1453                         return buffer;
1454                 }
1455
1456                 #endregion // Methods
1457
1458                 internal sealed class OracleParameterConverter : ExpandableObjectConverter
1459                 {
1460                         public OracleParameterConverter ()
1461                         {
1462                         }
1463
1464                         [MonoTODO]
1465                         public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
1466                         {
1467                                 throw new NotImplementedException ();
1468                         }
1469
1470                         [MonoTODO]
1471                         public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
1472                         {
1473                                 throw new NotImplementedException ();
1474                         }
1475                 }
1476         }
1477 }