New test.
[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 <danielmorgan@verizon.net>
13 //    Hubert FONGARNAND <informatique.internet@fiducial.fr>
14 //
15 // Copyright (C) Tim Coleman , 2003
16 // Copyright (C) Daniel Morgan, 2005
17 // Copyright (C) Hubert FONGARNAND, 2005
18 //
19 // Licensed under the MIT/X11 License.
20 //
21
22 using System;
23 using System.Collections;
24 using System.ComponentModel;
25 using System.Data;
26 using System.Data.SqlTypes;
27 using System.Data.OracleClient.Oci;
28 using System.Globalization;
29 using System.Runtime.InteropServices;
30 using System.Text;
31
32 namespace System.Data.OracleClient {
33         [TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
34         public sealed class OracleParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
35         {
36                 #region Fields
37
38                 string name;
39                 OracleType oracleType = OracleType.VarChar;
40                 OciDataType ociType;
41                 int size;
42                 ParameterDirection direction = ParameterDirection.Input;
43                 bool isNullable;
44                 byte precision;
45                 byte scale;
46                 string srcColumn;
47                 DataRowVersion srcVersion;
48                 DbType dbType = DbType.AnsiString;
49                 int offset = 0;
50                 bool sizeSet = false;
51                 object value = DBNull.Value;
52                 OciLobLocator lobLocator = null;  // only if Blob or Clob
53                 IntPtr bindOutValue = IntPtr.Zero;
54                 OciDateTimeDescriptor dateTimeDesc = null;
55                 IntPtr cursor = IntPtr.Zero;
56
57                 OracleParameterCollection container = null;
58                 OciBindHandle bindHandle;
59                 OciErrorHandle errorHandle;
60                 OracleConnection connection;
61                 byte[] bytes = null;
62                 IntPtr bindValue = IntPtr.Zero;
63                 bool useRef = false;
64                 OciDataType bindType;
65
66                 short indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
67                 int bindSize = 0;
68                 uint position = 0;
69
70                 #endregion // Fields
71
72                 #region Constructors
73
74                 // constructor for cloning the object
75                 internal OracleParameter (OracleParameter value) {
76                         this.name = value.name;
77                         this.oracleType = value.oracleType;
78                         this.ociType = value.ociType;
79                         this.size = value.size;
80                         this.direction = value.direction;
81                         this.isNullable = value.isNullable;
82                         this.precision = value.precision;
83                         this.scale = value.scale;
84                         this.srcColumn = value.srcColumn;
85                         this.srcVersion = value.srcVersion;
86                         this.dbType = value.dbType;
87                         this.offset = value.offset;
88                         this.sizeSet = value.sizeSet;
89                         this.value = value.value;
90                         this.lobLocator = value.lobLocator;
91                 }
92
93                 public OracleParameter ()
94                         : this (String.Empty, OracleType.VarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
95                 {
96                 }
97
98                 public OracleParameter (string name, object value)
99                 {
100                         this.name = name;
101                         this.value = value;
102                         SourceVersion = DataRowVersion.Current;
103                         InferOracleType (value);
104                 }
105
106                 public OracleParameter (string name, OracleType dataType)
107                         : this (name, dataType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
108                 {
109                 }
110
111                 public OracleParameter (string name, OracleType dataType, int size)
112                         : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
113                 {
114                 }
115
116                 public OracleParameter (string name, OracleType dataType, int size, string srcColumn)
117                         : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
118                 {
119                 }
120
121                 public OracleParameter (string name, OracleType dataType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
122                 {
123                         this.name = name;
124                         this.size = size;
125                         this.value = value;
126
127                         OracleType = dataType;
128                         Direction = direction;
129                         SourceColumn = srcColumn;
130                         SourceVersion = srcVersion;
131                 }
132
133                 #endregion // Constructors
134
135                 #region Properties
136
137                 internal OracleParameterCollection Container {
138                         get { return container; }
139                         set { container = value; }
140                 }
141
142                 [Browsable (false)]
143                 [RefreshProperties (RefreshProperties.All)]
144                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
145                 public DbType DbType {
146                         get { return dbType; }
147                         set { SetDbType (value); }
148                 }
149
150                 [DefaultValue (ParameterDirection.Input)]
151                 [RefreshProperties (RefreshProperties.All)]
152                 public ParameterDirection Direction {
153                         get { return direction; }
154                         set { direction = value; }
155                 }
156
157                 [Browsable (false)]
158                 [DesignOnly (true)]
159                 [DefaultValue (false)]
160                 [EditorBrowsable (EditorBrowsableState.Never)]
161                 public bool IsNullable {
162                         get { return isNullable; }
163                         set { isNullable = value; }
164                 }
165
166                 [DefaultValue (0)]
167                 [Browsable (false)]
168                 public int Offset {
169                         get { return offset; }
170                         set { offset = value; }
171                 }
172
173                 [DefaultValue (OracleType.VarChar)]
174                 [RefreshProperties (RefreshProperties.All)]
175                 public OracleType OracleType {
176                         get { return oracleType; }
177                         set { SetOracleType (value); }
178                 }
179                 
180                 [DefaultValue ("")]
181                 public string ParameterName {
182                         get { return name; }
183                         set { name = value; }
184                 }
185
186                 [DefaultValue (0)]
187                 public byte Precision {
188                         get { return precision; }
189                         set { /* NO EFFECT*/ }
190                 }
191
192                 [DefaultValue (0)]
193                 public byte Scale {
194                         get { return scale; }
195                         set { /* NO EFFECT*/ }
196                 }
197
198                 [DefaultValue (0)]
199                 public int Size {
200                         get { return size; }
201                         set { 
202                                 sizeSet = true;
203                                 size = value; 
204                         }
205                 }
206
207                 [DefaultValue ("")]
208                 public string SourceColumn {
209                         get { return srcColumn; }
210                         set { srcColumn = value; }
211                 }
212
213                 [DefaultValue ("Current")]
214                 public DataRowVersion SourceVersion {
215                         get { return srcVersion; }
216                         set { srcVersion = value; }
217                 }
218
219                 [DefaultValue (null)]
220                 [RefreshProperties (RefreshProperties.All)]
221                 [TypeConverter (typeof(StringConverter))]
222                 public object Value {
223                         get { return this.value; }
224                         set { this.value = value; }
225                 }
226
227                 #endregion // Properties
228
229                 #region Methods
230
231                 private void AssertSizeIsSet ()
232                 {
233                         switch (ociType) {
234                         case OciDataType.VarChar2:
235                         case OciDataType.String:
236                         case OciDataType.VarChar:
237                         case OciDataType.Char:
238                         case OciDataType.CharZ:
239                         case OciDataType.OciString:
240                                 if (!sizeSet)
241                                         throw new Exception ("Size must be set.");
242                                 break;
243                         default:
244                                 break;
245                         }
246                 }
247
248                 internal void Bind (OciStatementHandle statement, OracleConnection con, uint pos)
249                 {
250                         connection = con;
251
252                         errorHandle = connection.ErrorHandle;
253
254                         if (bindHandle == null)
255                                 bindHandle = new OciBindHandle ((OciHandle) statement);
256
257                         IntPtr tmpHandle = bindHandle.Handle;
258
259                         position = pos;
260
261                         if (Direction != ParameterDirection.Input)
262                                 AssertSizeIsSet ();
263                         if (!sizeSet)
264                                 size = InferSize ();
265
266                         bindSize = size;
267                         object v = value;
268                         int status = 0;
269                         bindType = ociType;
270                         int rsize = 0;
271
272                         bool isnull = false;
273                         if (direction == ParameterDirection.Input || direction == ParameterDirection.InputOutput) {
274                                 try {
275                                         Type nullable = v.GetType ().GetInterface ("System.Data.SqlTypes.INullable", false);
276                                         if (nullable != null) {
277                                                 INullable mynullable = (INullable)v;
278                                                 isnull = mynullable.IsNull;
279                                         }
280                                 }
281                                 catch(Exception e) {
282                                         System.IO.TextWriter.Null.WriteLine(e.Message);
283                                 }
284                         }
285
286                         // TODO: handle InputOutput and Return parameters
287                         if (direction == ParameterDirection.Output) {
288                                 // TODO: need to figure out how OracleParameter
289                                 //       which uses OciBindHandle to share code
290                                 //       with OciDefineHandle
291                                 switch(ociType) {
292                                         case OciDataType.VarChar2:
293                                         case OciDataType.String:
294                                         case OciDataType.VarChar:
295                                         case OciDataType.Char:
296                                         case OciDataType.CharZ:
297                                         case OciDataType.OciString:
298                                                 bindType = OciDataType.Char;
299                                                 bindSize = size * 2;
300                                                 bindOutValue = Marshal.AllocHGlobal (bindSize);
301                                                 bindValue = bindOutValue;
302                                                 break;
303                                         case OciDataType.RowIdDescriptor:
304                                                 size = 10;
305                                                 bindType = OciDataType.Char;
306                                                 bindSize = size * 2;
307                                                 bindOutValue = Marshal.AllocHGlobal (bindSize);
308                                                 bindValue = bindOutValue;
309                                                 break;
310                                         case OciDataType.Date:
311                                                 bindSize = 7;
312                                                 bindType = OciDataType.Date;
313                                                 bindOutValue = Marshal.AllocHGlobal (bindSize);
314                                                 bindValue = bindOutValue;
315                                                 break;
316                                         case OciDataType.TimeStamp:
317                                                 dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
318                                                 if (dateTimeDesc == null) {
319                                                         OciErrorInfo info = connection.ErrorHandle.HandleError ();
320                                                         throw new OracleException (info.ErrorCode, info.ErrorMessage);
321                                                 }
322                                                 dateTimeDesc.ErrorHandle = connection.ErrorHandle;
323                                                 bindSize = 11;
324                                                 bindType = OciDataType.TimeStamp;
325                                                 bindOutValue = dateTimeDesc.Handle;
326                                                 bindValue = dateTimeDesc.Handle;
327                                                 useRef = true;
328                                                 break;
329                                         case OciDataType.Number:
330                                                 bindSize = 22;
331                                                 bindType = OciDataType.Char;
332                                                 bindOutValue = Marshal.AllocHGlobal (bindSize);
333                                                 bindValue = bindOutValue;
334                                                 break;
335                                         case OciDataType.Long:
336                                         case OciDataType.LongVarChar:
337                                                 // LAMESPEC: you don't know size until you get it;
338                                                 // therefore, you must allocate an insane size
339                                                 // see OciDefineHandle
340                                                 bindSize = OciDefineHandle.LongVarCharMaxValue;
341                                                 bindOutValue = Marshal.AllocHGlobal (bindSize);
342                                                 bindType = OciDataType.LongVarChar;
343                                                 bindValue = bindOutValue;
344                                                 break;
345                                         case OciDataType.Blob:
346                                         case OciDataType.Clob:
347                                                 bindSize = -1;
348                                                 lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
349                                                 if (lobLocator == null) {
350                                                         OciErrorInfo info = connection.ErrorHandle.HandleError ();
351                                                         throw new OracleException (info.ErrorCode, info.ErrorMessage);
352                                                 }
353                                                 bindOutValue = lobLocator.Handle;
354                                                 bindValue = lobLocator.Handle;
355                                                 lobLocator.ErrorHandle = connection.ErrorHandle;
356                                                 lobLocator.Service = statement.Service;
357                                                 useRef = true;
358                                                 break;                                  
359                                         case OciDataType.RSet: // REF CURSOR
360                                                 cursor = IntPtr.Zero;
361                                                 OciCalls.OCIHandleAlloc (connection.Environment,
362                                                         out cursor,
363                                                         OciHandleType.Statement,
364                                                         0,
365                                                         IntPtr.Zero);
366                                                         
367                                                 bindSize = 0;
368                                                 bindType = OciDataType.RSet;
369                                                 break;                                  
370                                         default:
371                                                 // define other types
372                                                 throw new NotImplementedException ();
373                                 }
374                                 bindValue = bindOutValue;
375                         }
376                         else if ((v == DBNull.Value || v == null || isnull == true) && direction == ParameterDirection.Input) {
377                                 indicator = 0;
378                                 bindType = OciDataType.VarChar2;
379                                 bindSize = 0;
380                         }
381                         else {
382                                 // TODO: do other data types and oracle data types
383                                 // should I be using IConvertible to convert?
384                                 string sDate = "";
385                                 DateTime dt = DateTime.MinValue;
386                                 if (oracleType == OracleType.Timestamp){
387                                         bindType = OciDataType.TimeStamp;
388                                         bindSize = 11;
389                                         dt = DateTime.MinValue;
390                                         sDate = "";
391                                         if (v is String){
392                                                 sDate = (string) v;
393                                                 dt = DateTime.Parse (sDate);
394                                         }
395                                         else if (v is DateTime)
396                                                 dt = (DateTime) v;
397                                         else if (v is OracleString){
398                                                 sDate = (string) v;
399                                                 dt = DateTime.Parse (sDate);
400                                         }
401                                         else if (v is OracleDateTime) {
402                                                 OracleDateTime odt = (OracleDateTime) v;
403                                                 dt = (DateTime) odt.Value;
404                                         }
405                                         else
406                                                 throw new NotImplementedException (); // ?
407                                         
408                                         short year = (short) dt.Year;
409                                         byte month = (byte) dt.Month;
410                                         byte day = (byte) dt.Day;
411                                         byte hour = (byte) dt.Hour;
412                                         byte min = (byte) dt.Minute;
413                                         byte sec = (byte) dt.Second;
414                                         uint fsec = (uint) dt.Millisecond;
415                                         string timezone = "";
416                                         dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
417                                         if (dateTimeDesc == null) {
418                                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
419                                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
420                                         }
421                                         dateTimeDesc.ErrorHandle = connection.ErrorHandle;
422                                         dateTimeDesc.SetDateTime (connection.Session,
423                                                 connection.ErrorHandle, 
424                                                 year, month, day, hour, min, sec, fsec,
425                                                 timezone);
426                                         useRef = true;
427                                 }
428                                 else if (oracleType == OracleType.DateTime) {
429                                         sDate = "";
430                                         dt = DateTime.MinValue;
431                                         if (v is String) {
432                                                 sDate = (string) v;
433                                                 dt = DateTime.Parse (sDate);
434                                         }
435                                         else if (v is DateTime)
436                                                 dt = (DateTime) v;
437                                         else if (v is OracleString) {
438                                                 sDate = (string) v;
439                                                 dt = DateTime.Parse (sDate);
440                                         }
441                                         else if (v is OracleDateTime) {
442                                                 OracleDateTime odt = (OracleDateTime) v;
443                                                 dt = (DateTime) odt.Value;
444                                         }
445                                         else
446                                                 throw new NotImplementedException (); // ?
447
448                                         bytes = PackDate (dt);
449                                         bindType = OciDataType.Date;
450                                         bindSize = bytes.Length;
451                                 }
452                                 else if (oracleType == OracleType.Blob) {
453                                         bytes = (byte[]) v;
454                                         bindType = OciDataType.LongRaw;
455                                         bindSize = bytes.Length;
456                                 }
457                                 else if (oracleType == OracleType.Clob) {
458                                         string sv = v.ToString();
459                                         rsize = 0;
460                         
461                                         // Get size of buffer
462                                         OciCalls.OCIUnicodeToCharSet (statement.Parent, null, sv, out rsize);
463                         
464                                         // Fill buffer
465                                         bytes = new byte[rsize];
466                                         OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, sv, out rsize);
467
468                                         bindType = OciDataType.Long;
469                                         bindSize = bytes.Length;
470                                 }
471                                 else if (oracleType == OracleType.Raw) {
472                                         byte[] val = v as byte[];
473                                         bindValue = Marshal.AllocHGlobal (val.Length);
474                                         Marshal.Copy (val, 0, bindValue, val.Length);
475                                         bindSize = val.Length;
476                                 }
477                                 else if (oracleType == OracleType.Number
478                                         || oracleType == OracleType.Double
479                                         || oracleType == OracleType.Int32
480                                         || oracleType == OracleType.Int16
481                                         || oracleType == OracleType.Byte
482                                         || oracleType == OracleType.Float) {
483                                         string svalue = null;
484                                         if(v is IFormattable)
485                                                 svalue = ((IFormattable)v).ToString (null, con.SessionFormatProvider);
486                                         else
487                                                 svalue = v.ToString();
488                                         rsize = 0;
489                         
490                                         // Get size of buffer
491                                         OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
492                         
493                                         // Fill buffer
494                                         bytes = new byte[rsize];
495                                         OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
496
497                                         bindType = OciDataType.VarChar2;
498                                         bindSize = svalue.Length;
499                                 }
500                                 else {
501                                         string svalue = v.ToString () + '\0';
502                                         rsize = 0;
503                         
504                                         // Get size of buffer
505                                         OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
506                         
507                                         // Fill buffer
508                                         bytes = new byte[rsize];
509                                         OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
510
511                                         bindType = OciDataType.String;
512                                         bindSize = svalue.Length;
513                                 }
514                         }
515
516                         // Now, call the appropriate OCI Bind function
517
518                         if (useRef == true) {
519                                 if (bindType == OciDataType.TimeStamp) {
520                                         bindValue = dateTimeDesc.Handle;
521                                         status = OciCalls.OCIBindByNameRef (statement,
522                                                 out tmpHandle,
523                                                 connection.ErrorHandle,
524                                                 ParameterName,
525                                                 ParameterName.Length,
526                                                 ref bindValue,
527                                                 bindSize,
528                                                 bindType,
529                                                 ref indicator,
530                                                 IntPtr.Zero,
531                                                 IntPtr.Zero,
532                                                 0,
533                                                 IntPtr.Zero,
534                                                 0);
535                                 }
536                                 else {
537                                         status = OciCalls.OCIBindByNameRef (statement,
538                                                 out tmpHandle,
539                                                 connection.ErrorHandle,
540                                                 ParameterName,
541                                                 ParameterName.Length,
542                                                 ref bindValue,
543                                                 bindSize,
544                                                 bindType,
545                                                 ref indicator,
546                                                 IntPtr.Zero,
547                                                 IntPtr.Zero,
548                                                 0,
549                                                 IntPtr.Zero,
550                                                 0);
551                                 }
552                         }
553                         else if (bindType == OciDataType.RSet) {
554                                 status = OciCalls.OCIBindByNameRef (statement,
555                                         out tmpHandle,
556                                         connection.ErrorHandle,
557                                         ParameterName,
558                                         ParameterName.Length,
559                                         ref cursor,
560                                         bindSize,
561                                         bindType,
562                                         ref indicator,
563                                         IntPtr.Zero,
564                                         IntPtr.Zero,
565                                         0,
566                                         IntPtr.Zero,
567                                         0);                                     
568                         }
569                         else if (bytes != null) {
570                                 status = OciCalls.OCIBindByNameBytes (statement,
571                                         out tmpHandle,
572                                         connection.ErrorHandle,
573                                         ParameterName,
574                                         ParameterName.Length,
575                                         bytes,
576                                         bindSize,
577                                         bindType,
578                                         ref indicator,
579                                         IntPtr.Zero,
580                                         IntPtr.Zero,
581                                         0,
582                                         IntPtr.Zero,
583                                         0);
584                         }
585                         else {
586                                 status = OciCalls.OCIBindByName (statement,
587                                         out tmpHandle,
588                                         connection.ErrorHandle,
589                                         ParameterName,
590                                         ParameterName.Length,
591                                         bindValue,
592                                         bindSize,
593                                         bindType,
594                                         ref indicator,
595                                         IntPtr.Zero,
596                                         IntPtr.Zero,
597                                         0,
598                                         IntPtr.Zero,
599                                         0);
600                         }
601
602                         if (status != 0) {
603                                 OciErrorInfo info = connection.ErrorHandle.HandleError ();
604                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
605                         }
606
607                         bindHandle.SetHandle (tmpHandle);
608                 }
609
610                 object ICloneable.Clone ()
611                 {
612                         return new OracleParameter(this);
613                 }
614
615                 private void InferOracleType (object value)
616                 {
617                         Type type = value.GetType ();
618                         string exception = String.Format ("The parameter data type of {0} is invalid.", type.Name);
619                         switch (type.FullName) {
620                         case "System.Int64":
621                                 SetOracleType (OracleType.Number);
622                                 break;
623                         case "System.Boolean":
624                         case "System.Byte":
625                                 SetOracleType (OracleType.Byte);
626                                 break;
627                         case "System.String":
628                                 SetOracleType (OracleType.VarChar);
629                                 break;
630                         case "System.DateTime":
631                                 SetOracleType (OracleType.DateTime);
632                                 break;
633                         case "System.Decimal":
634                                 SetOracleType (OracleType.Number);
635                                 //scale = ((decimal) value).Scale;
636                                 break;
637                         case "System.Double":
638                                 SetOracleType (OracleType.Double);
639                                 break;
640                         case "System.Byte[]":
641                         case "System.Guid":
642                                 SetOracleType (OracleType.Raw);
643                                 break;
644                         case "System.Int32":
645                                 SetOracleType (OracleType.Int32);
646                                 break;
647                         case "System.Single":
648                                 SetOracleType (OracleType.Float);
649                                 break;
650                         case "System.Int16":
651                                 SetOracleType (OracleType.Int16);
652                                 break;
653                         default:
654                                 throw new ArgumentException (exception);
655                         }
656                 }
657
658                 private int InferSize ()
659                 {
660                         int newSize = 0;
661                         
662                         switch (ociType) {
663                         case OciDataType.VarChar2:
664                         case OciDataType.String:
665                         case OciDataType.VarChar:
666                         case OciDataType.Char:
667                         case OciDataType.CharZ:
668                         case OciDataType.OciString:
669                         case OciDataType.Long:
670                         case OciDataType.LongVarChar:
671                                 if (value == null || value == DBNull.Value)
672                                         newSize = 0;
673                                 else
674                                         newSize = value.ToString ().Length;
675                                 break;
676                         case OciDataType.RowIdDescriptor:
677                                 newSize = 10;
678                                 break;
679                         case OciDataType.Integer:
680                         case OciDataType.Number:
681                         case OciDataType.Float:
682                                 newSize = 22;
683                                 break;
684                         case OciDataType.Date:
685                                 newSize = 7;
686                                 break;
687                         case OciDataType.TimeStamp:
688                                 newSize = 11;
689                                 break;          
690                         case OciDataType.Blob:
691                         case OciDataType.Clob:
692                         case OciDataType.RSet: // REF CURSOR
693                                 newSize = -1;
694                                 break;                                  
695                         default:
696                                 if (value == null || value == DBNull.Value)
697                                         newSize = 0;
698                                 else
699                                         newSize = value.ToString ().Length;
700                                 break;
701                         }
702
703                         sizeSet = true;
704
705                         return newSize;
706                 }
707
708                 private void SetDbType (DbType type)
709                 {
710                         string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
711                         switch (type) {
712                         case DbType.AnsiString:
713                                 oracleType = OracleType.VarChar;
714                                 ociType = OciDataType.VarChar;
715                                 break;
716                         case DbType.AnsiStringFixedLength:
717                                 oracleType = OracleType.Char;
718                                 ociType = OciDataType.Char;
719                                 break;
720                         case DbType.Binary:
721                         case DbType.Guid:
722                                 oracleType = OracleType.Raw;
723                                 ociType = OciDataType.Raw;
724                                 break;
725                         case DbType.Boolean:
726                         case DbType.Byte:
727                                 oracleType = OracleType.Byte;
728                                 ociType = OciDataType.Integer;
729                                 break;
730                         case DbType.Currency:
731                         case DbType.Decimal:
732                         case DbType.Int64:
733                                 oracleType = OracleType.Number;
734                                 ociType = OciDataType.Number;
735                                 break;
736                         case DbType.Date:
737                         case DbType.DateTime:
738                         case DbType.Time:
739                                 oracleType = OracleType.DateTime;
740                                 ociType = OciDataType.Char;
741                                 break;
742                         case DbType.Double:
743                                 oracleType = OracleType.Double;
744                                 ociType = OciDataType.Float;
745                                 break;
746                         case DbType.Int16:
747                                 oracleType = OracleType.Int16;
748                                 ociType = OciDataType.Integer;
749                                 break;
750                         case DbType.Int32:
751                                 oracleType = OracleType.Int32;
752                                 ociType = OciDataType.Integer;
753                                 break;
754                         case DbType.Object:
755                                 oracleType = OracleType.Blob;
756                                 ociType = OciDataType.Blob;
757                                 break;
758                         case DbType.Single:
759                                 oracleType = OracleType.Float;
760                                 ociType = OciDataType.Float;
761                                 break;
762                         case DbType.String:
763                                 oracleType = OracleType.NVarChar;
764                                 ociType = OciDataType.VarChar;
765                                 break;
766                         case DbType.StringFixedLength:
767                                 oracleType = OracleType.NChar;
768                                 ociType = OciDataType.Char;
769                                 break;
770                         default:
771                                 throw new ArgumentException (exception);
772                         }
773                         dbType = type;
774
775                 }
776
777                 private void SetOracleType (OracleType type)
778                 {
779                         FreeHandle ();
780                         string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
781                         switch (type) {
782                         case OracleType.BFile:
783                         case OracleType.Blob:
784                                 dbType = DbType.Binary;
785                                 ociType = OciDataType.Blob;
786                                 break;
787                         case OracleType.LongRaw:
788                         case OracleType.Raw:
789                                 dbType = DbType.Binary;
790                                 ociType = OciDataType.Raw;
791                                 break;
792                         case OracleType.Byte:
793                                 dbType = DbType.Byte;
794                                 ociType = OciDataType.Number;
795                                 break;
796                         case OracleType.Char:
797                                 dbType = DbType.AnsiString;
798                                 ociType = OciDataType.Char;
799                                 break;
800                         case OracleType.Clob:
801                                 dbType = DbType.AnsiString;
802                                 ociType = OciDataType.Clob;
803                                 break;
804                         case OracleType.LongVarChar:
805                         case OracleType.RowId:
806                         case OracleType.VarChar:
807                                 dbType = DbType.AnsiString;
808                                 ociType = OciDataType.VarChar;
809                                 break;
810                         case OracleType.Cursor: // REF CURSOR
811                                 ociType = OciDataType.RSet;
812                                 dbType = DbType.Object;
813                                 break;
814                         case OracleType.IntervalDayToSecond:
815                                 dbType = DbType.AnsiStringFixedLength;
816                                 ociType = OciDataType.Char;
817                                 break;
818                         case OracleType.Timestamp:
819                         case OracleType.TimestampLocal:
820                         case OracleType.TimestampWithTZ:
821                                 dbType = DbType.DateTime;
822                                 ociType = OciDataType.TimeStamp;
823                                 break;
824                         case OracleType.DateTime:
825                                 dbType = DbType.DateTime;
826                                 ociType = OciDataType.Date;
827                                 break;
828                         case OracleType.Double:
829                                 dbType = DbType.Double;
830                                 ociType = OciDataType.Number;
831                                 break;
832                         case OracleType.Float:
833                                 dbType = DbType.Single;
834                                 ociType = OciDataType.Number;
835                                 break;
836                         case OracleType.Int16:
837                                 dbType = DbType.Int16;
838                                 ociType = OciDataType.Number;
839                                 break;
840                         case OracleType.Int32:
841                         case OracleType.IntervalYearToMonth:
842                                 dbType = DbType.Int32;
843                                 ociType = OciDataType.Number;
844                                 break;
845                         case OracleType.NChar:
846                                 dbType = DbType.StringFixedLength;
847                                 ociType = OciDataType.Char;
848                                 break;
849                         case OracleType.NClob:
850                         case OracleType.NVarChar:
851                                 dbType = DbType.String;
852                                 ociType = OciDataType.Char;
853                                 break;
854                         case OracleType.Number:
855                                 dbType = DbType.VarNumeric;
856                                 ociType = OciDataType.Number;
857                                 break;
858                         case OracleType.SByte:
859                                 dbType = DbType.SByte;
860                                 ociType = OciDataType.Number;
861                                 break;
862                         case OracleType.UInt16:
863                                 dbType = DbType.UInt16;
864                                 ociType = OciDataType.Number;
865                                 break;
866                         case OracleType.UInt32:
867                                 dbType = DbType.UInt32;
868                                 ociType = OciDataType.Number;
869                                 break;
870                         default:
871                                 throw new ArgumentException (exception);
872                         }
873
874                         oracleType = type;
875                 }
876
877                 public override string ToString ()
878                 {
879                         return ParameterName;
880                 }
881
882                 private void GetOutValue (OracleCommand cmd) 
883                 {
884                         // used to update the parameter value
885                         // for Output, the output of InputOutput, and Return parameters
886                         value = DBNull.Value;
887                         if (indicator == -1)
888                                 return;
889
890                         byte[] buffer = null;
891                         object tmp = null;
892
893                         switch (ociType) {
894                         case OciDataType.VarChar2:
895                         case OciDataType.String:
896                         case OciDataType.VarChar:
897                         case OciDataType.Char:
898                         case OciDataType.CharZ:
899                         case OciDataType.OciString:
900                         case OciDataType.RowIdDescriptor:
901                                 buffer = new byte [Size];
902                                 Marshal.Copy (bindOutValue, buffer, 0, Size);
903                                 
904                                 // Get length of returned string
905                                 int     rsize = 0;
906                                 IntPtr  env = cmd.Connection.Environment;
907                                 OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
908                         
909                                 // Get string
910                                 StringBuilder ret = new StringBuilder(rsize);
911                                 OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
912         
913                                 value = ret.ToString ();
914                                 break;
915                         case OciDataType.Integer:
916                         case OciDataType.Number:
917                         case OciDataType.Float:
918                                 tmp = Marshal.PtrToStringAnsi (bindOutValue, bindSize);
919                                 if (tmp != null)
920                                         value = Decimal.Parse (String.Copy ((string) tmp), cmd.Connection.SessionFormatProvider);
921                                 break;
922                         case OciDataType.TimeStamp:
923                                 value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
924                                 break;
925                         case OciDataType.Date:
926                                 value = UnpackDate (bindOutValue);
927                                 break;  
928                         case OciDataType.Blob:
929                         case OciDataType.Clob:
930                                 OracleLob lob = new OracleLob (lobLocator, ociType);
931                                 lob.connection = connection;
932                                 value = lob;
933                                 break;
934                         case OciDataType.RSet: // REF CURSOR
935                                 OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.Environment, cursor);
936                                 cursorStatement.ErrorHandle = cmd.ErrorHandle;
937                                 cursorStatement.Command = cmd.Connection.CreateCommand ();
938                                 cursorStatement.SetupRefCursorResult ();
939                                 value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
940                                 cursor = IntPtr.Zero;
941                                 cursorStatement = null;
942                                 break;
943                         default:
944                                 throw new NotImplementedException ();
945                         }
946                         tmp = null;
947                         buffer = null;
948                 }
949
950                 internal void Update (OracleCommand cmd) 
951                 {
952                         if (Direction != ParameterDirection.Input)
953                                 GetOutValue (cmd);
954                         
955                         FreeHandle ();
956                 }
957
958                 internal void FreeHandle ()
959                 {
960                         switch (ociType) {
961                         case OciDataType.Clob:
962                         case OciDataType.Blob:
963                                 lobLocator = null;
964                                 break;
965                         case OciDataType.Raw:
966                                 Marshal.FreeHGlobal (bindValue);
967                                 break;
968                         case OciDataType.TimeStamp:
969                                 break;
970                         default:
971                                 Marshal.FreeHGlobal (bindOutValue);
972                                 break;
973                         } 
974
975                         bindOutValue = IntPtr.Zero;
976                         bindValue = IntPtr.Zero;
977
978                         bindHandle = null;
979                         errorHandle = null;
980                         connection = null;
981                 }
982
983                 // copied from OciDefineHandle
984                 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
985                 internal DateTime UnpackDate (IntPtr dateValue)
986                 {
987                         byte century = Marshal.ReadByte (dateValue, 0);
988                         byte year = Marshal.ReadByte (dateValue, 1);
989                         byte month = Marshal.ReadByte (dateValue, 2);
990                         byte day = Marshal.ReadByte (dateValue, 3);
991                         byte hour = Marshal.ReadByte (dateValue, 4);
992                         byte minute = Marshal.ReadByte (dateValue, 5);
993                         byte second = Marshal.ReadByte (dateValue, 6);
994
995                         return new DateTime ((century - 100) * 100 + (year - 100),
996                                                 month,
997                                                 day,
998                                                 hour - 1,
999                                                 minute - 1,
1000                                                 second - 1);
1001
1002                 }
1003
1004                 internal byte[] PackDate (DateTime dateValue) 
1005                 {
1006                         byte[] buffer = new byte[7];
1007
1008                         buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
1009                         buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
1010                         buffer[2] = (byte)dateValue.Month;
1011                         buffer[3] = (byte)dateValue.Day;
1012                         buffer[4] = (byte)(dateValue.Hour+1);
1013                         buffer[5] = (byte)(dateValue.Minute+1);
1014                         buffer[6] = (byte)(dateValue.Second+1);
1015
1016                         return buffer;
1017                 }
1018
1019                 #endregion // Methods
1020
1021                 internal sealed class OracleParameterConverter : ExpandableObjectConverter
1022                 {
1023                         public OracleParameterConverter ()
1024                         {
1025                         }
1026
1027                         [MonoTODO]
1028                         public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
1029                         {
1030                                 throw new NotImplementedException ();
1031                         }
1032
1033                         [MonoTODO]
1034                         public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
1035                         {
1036                                 throw new NotImplementedException ();
1037                         }
1038                 }
1039         }
1040 }
1041