4 // Part of the Mono class libraries at
5 // mcs/class/System.Data.OracleClient/System.Data.OracleClient
7 // Assembly: System.Data.OracleClient.dll
8 // Namespace: System.Data.OracleClient
11 // Tim Coleman <tim@timcoleman.com>
12 // Daniel Moragn <danielmorgan@verizon.net>
13 // Hubert FONGARNAND <informatique.internet@fiducial.fr>
15 // Copyright (C) Tim Coleman , 2003
16 // Copyright (C) Daniel Morgan, 2005
17 // Copyright (C) Hubert FONGARNAND, 2005
19 // Licensed under the MIT/X11 License.
23 using System.Collections;
24 using System.ComponentModel;
26 using System.Data.OracleClient.Oci;
27 using System.Globalization;
28 using System.Runtime.InteropServices;
31 namespace System.Data.OracleClient {
32 [TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
33 public sealed class OracleParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
38 OracleType oracleType = OracleType.VarChar;
41 ParameterDirection direction = ParameterDirection.Input;
46 DataRowVersion srcVersion;
47 DbType dbType = DbType.AnsiString;
50 object value = DBNull.Value;
51 OciLobLocator lobLocator = null; // only if Blob or Clob
52 IntPtr bindOutValue = IntPtr.Zero;
53 OciDateTimeDescriptor dateTimeDesc = null;
55 OracleParameterCollection container = null;
56 OciBindHandle bindHandle;
57 OciErrorHandle errorHandle;
58 OracleConnection connection;
60 IntPtr bindValue = IntPtr.Zero;
62 short indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
69 // constructor for cloning the object
70 internal OracleParameter (OracleParameter value) {
71 this.name = value.name;
72 this.oracleType = value.oracleType;
73 this.ociType = value.ociType;
74 this.size = value.size;
75 this.direction = value.direction;
76 this.isNullable = value.isNullable;
77 this.precision = value.precision;
78 this.scale = value.scale;
79 this.srcColumn = value.srcColumn;
80 this.srcVersion = value.srcVersion;
81 this.dbType = value.dbType;
82 this.offset = value.offset;
83 this.sizeSet = value.sizeSet;
84 this.value = value.value;
85 this.lobLocator = value.lobLocator;
88 public OracleParameter ()
89 : this (String.Empty, OracleType.VarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
93 public OracleParameter (string name, object value)
97 SourceVersion = DataRowVersion.Current;
98 InferOracleType (value);
101 public OracleParameter (string name, OracleType dataType)
102 : this (name, dataType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
106 public OracleParameter (string name, OracleType dataType, int size)
107 : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
111 public OracleParameter (string name, OracleType dataType, int size, string srcColumn)
112 : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
116 public OracleParameter (string name, OracleType dataType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
122 OracleType = dataType;
123 Direction = direction;
124 SourceColumn = srcColumn;
125 SourceVersion = srcVersion;
128 #endregion // Constructors
132 internal OracleParameterCollection Container {
133 get { return container; }
134 set { container = value; }
138 [RefreshProperties (RefreshProperties.All)]
139 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
140 public DbType DbType {
141 get { return dbType; }
142 set { SetDbType (value); }
145 [DefaultValue (ParameterDirection.Input)]
146 [RefreshProperties (RefreshProperties.All)]
147 public ParameterDirection Direction {
148 get { return direction; }
149 set { direction = value; }
154 [DefaultValue (false)]
155 [EditorBrowsable (EditorBrowsableState.Never)]
156 public bool IsNullable {
157 get { return isNullable; }
158 set { isNullable = value; }
164 get { return offset; }
165 set { offset = value; }
168 [DefaultValue (OracleType.VarChar)]
169 [RefreshProperties (RefreshProperties.All)]
170 public OracleType OracleType {
171 get { return oracleType; }
172 set { SetOracleType (value); }
176 public string ParameterName {
178 set { name = value; }
182 public byte Precision {
183 get { return precision; }
184 set { /* NO EFFECT*/ }
189 get { return scale; }
190 set { /* NO EFFECT*/ }
203 public string SourceColumn {
204 get { return srcColumn; }
205 set { srcColumn = value; }
208 [DefaultValue ("Current")]
209 public DataRowVersion SourceVersion {
210 get { return srcVersion; }
211 set { srcVersion = value; }
214 [DefaultValue (null)]
215 [RefreshProperties (RefreshProperties.All)]
216 [TypeConverter (typeof(StringConverter))]
217 public object Value {
218 get { return this.value; }
219 set { this.value = value; }
222 #endregion // Properties
226 private void AssertSizeIsSet ()
229 case OciDataType.VarChar2:
230 case OciDataType.String:
231 case OciDataType.VarChar:
232 case OciDataType.Char:
233 case OciDataType.CharZ:
234 case OciDataType.OciString:
236 throw new Exception ("Size must be set.");
243 internal void Bind (OciStatementHandle statement, OracleConnection connection)
245 errorHandle = connection.ErrorHandle;
247 if (bindHandle == null)
248 bindHandle = new OciBindHandle ((OciHandle) statement);
250 IntPtr tmpHandle = bindHandle.Handle;
252 if (Direction != ParameterDirection.Input)
260 OciDataType bindType = ociType;
263 // TODO: handle InputOutput and Return parameters
264 if (direction == ParameterDirection.Output) {
265 // TODO: need to figure out how OracleParameter
266 // which uses OciBindHandle to share code
267 // with OciDefineHandle
269 case OciDataType.VarChar2:
270 case OciDataType.String:
271 case OciDataType.VarChar:
272 case OciDataType.Char:
273 case OciDataType.CharZ:
274 case OciDataType.OciString:
275 bindType = OciDataType.Char;
277 bindOutValue = Marshal.AllocHGlobal (bindSize);
279 case OciDataType.RowIdDescriptor:
281 bindType = OciDataType.Char;
283 bindOutValue = Marshal.AllocHGlobal (bindSize);
285 case OciDataType.Date:
287 bindType = OciDataType.Date;
288 bindOutValue = Marshal.AllocHGlobal (bindSize);
290 case OciDataType.TimeStamp:
291 dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
292 if (dateTimeDesc == null) {
293 OciErrorInfo info = connection.ErrorHandle.HandleError ();
294 throw new OracleException (info.ErrorCode, info.ErrorMessage);
296 dateTimeDesc.ErrorHandle = connection.ErrorHandle;
298 bindType = OciDataType.TimeStamp;
299 bindOutValue = dateTimeDesc.Handle;
301 case OciDataType.Number:
303 bindType = OciDataType.Char;
304 bindOutValue = Marshal.AllocHGlobal (bindSize);
306 case OciDataType.Long:
307 case OciDataType.LongVarChar:
308 // LAMESPEC: you don't know size until you get it;
309 // therefore, you must allocate an insane size
310 // see OciDefineHandle
311 bindSize = OciDefineHandle.LongVarCharMaxValue;
312 bindOutValue = Marshal.AllocHGlobal (bindSize);
313 bindType = OciDataType.LongVarChar;
315 case OciDataType.Blob:
316 case OciDataType.Clob:
318 lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
319 if (lobLocator == null) {
320 OciErrorInfo info = connection.ErrorHandle.HandleError ();
321 throw new OracleException (info.ErrorCode, info.ErrorMessage);
323 bindOutValue = lobLocator.Handle;
324 lobLocator.ErrorHandle = connection.ErrorHandle;
325 lobLocator.Service = statement.Service;
328 // define other types
329 throw new NotImplementedException ();
331 bindValue = bindOutValue;
333 else if (v == DBNull.Value || v == null) {
335 bindType = OciDataType.VarChar2;
339 // TODO: do other data types and oracle data types
340 // should I be using IConvertible to convert?
342 DateTime dt = DateTime.MinValue;
343 if (oracleType == OracleType.Timestamp){
344 bindType = OciDataType.TimeStamp;
346 dt = DateTime.MinValue;
350 dt = DateTime.Parse (sDate);
352 else if (v is DateTime)
354 else if (v is OracleString){
356 dt = DateTime.Parse (sDate);
359 throw new NotImplementedException (); // ?
361 short year = (short) dt.Year;
362 byte month = (byte) dt.Month;
363 byte day = (byte) dt.Day;
364 byte hour = (byte) dt.Hour;
365 byte min = (byte) dt.Minute;
366 byte sec = (byte) dt.Second;
367 uint fsec = (uint) dt.Millisecond;
368 string timezone = "";
369 dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
370 if (dateTimeDesc == null) {
371 OciErrorInfo info = connection.ErrorHandle.HandleError ();
372 throw new OracleException (info.ErrorCode, info.ErrorMessage);
374 dateTimeDesc.ErrorHandle = connection.ErrorHandle;
375 dateTimeDesc.SetDateTime (connection.Session,
376 connection.ErrorHandle,
377 year, month, day, hour, min, sec, fsec,
380 else if (oracleType == OracleType.DateTime) {
382 dt = DateTime.MinValue;
385 dt = DateTime.Parse (sDate);
387 else if (v is DateTime)
389 else if (v is OracleString) {
391 dt = DateTime.Parse (sDate);
393 else if (v is OracleDateTime) {
394 OracleDateTime odt = (OracleDateTime) v;
395 dt = (DateTime) odt.Value;
398 throw new NotImplementedException (); // ?
400 bytes = PackDate (dt);
401 bindType = OciDataType.Date;
402 bindSize = bytes.Length;
404 else if (oracleType == OracleType.Blob) {
406 bindType = OciDataType.LongRaw;
407 bindSize = bytes.Length;
409 else if (oracleType == OracleType.Clob) {
410 string sv = (string) v;
413 // Get size of buffer
414 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, sv, out rsize);
417 bytes = new byte[rsize];
418 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, sv, out rsize);
420 bindType = OciDataType.Long;
421 bindSize = bytes.Length;
423 else if (oracleType == OracleType.Raw) {
424 byte[] val = v as byte[];
425 bindValue = Marshal.AllocHGlobal (val.Length);
426 Marshal.Copy (val, 0, bindValue, val.Length);
427 bindSize = val.Length;
430 string svalue = v.ToString ();
433 // Get size of buffer
434 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
437 bytes = new byte[rsize];
438 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
440 bindType = OciDataType.VarChar2;
441 bindSize = v.ToString ().Length;
445 if (dateTimeDesc != null) {
446 bindValue = dateTimeDesc.Handle;
447 status = OciCalls.OCIBindByNameRef (statement,
449 connection.ErrorHandle,
451 ParameterName.Length,
462 else if (bytes != null) {
463 status = OciCalls.OCIBindByNameBytes (statement,
465 connection.ErrorHandle,
467 ParameterName.Length,
479 status = OciCalls.OCIBindByName (statement,
481 connection.ErrorHandle,
483 ParameterName.Length,
496 OciErrorInfo info = connection.ErrorHandle.HandleError ();
497 throw new OracleException (info.ErrorCode, info.ErrorMessage);
500 bindHandle.SetHandle (tmpHandle);
503 object ICloneable.Clone ()
505 return new OracleParameter(this);
508 private void InferOracleType (object value)
510 Type type = value.GetType ();
511 string exception = String.Format ("The parameter data type of {0} is invalid.", type.Name);
512 switch (type.FullName) {
514 SetOracleType (OracleType.Number);
516 case "System.Boolean":
518 SetOracleType (OracleType.Byte);
520 case "System.String":
521 SetOracleType (OracleType.VarChar);
523 case "System.DateTime":
524 SetOracleType (OracleType.DateTime);
526 case "System.Decimal":
527 SetOracleType (OracleType.Number);
528 //scale = ((decimal) value).Scale;
530 case "System.Double":
531 SetOracleType (OracleType.Double);
533 case "System.Byte[]":
535 SetOracleType (OracleType.Raw);
538 SetOracleType (OracleType.Int32);
540 case "System.Single":
541 SetOracleType (OracleType.Float);
544 SetOracleType (OracleType.Int16);
547 throw new ArgumentException (exception);
551 private int InferSize ()
556 case OciDataType.VarChar2:
557 case OciDataType.String:
558 case OciDataType.VarChar:
559 case OciDataType.Char:
560 case OciDataType.CharZ:
561 case OciDataType.OciString:
562 case OciDataType.Long:
563 case OciDataType.LongVarChar:
564 if (value == null || value == DBNull.Value)
567 newSize = value.ToString ().Length;
569 case OciDataType.RowIdDescriptor:
572 case OciDataType.Integer:
573 case OciDataType.Number:
574 case OciDataType.Float:
577 case OciDataType.Date:
580 case OciDataType.TimeStamp:
583 case OciDataType.Blob:
584 case OciDataType.Clob:
588 if (value == null || value == DBNull.Value)
591 newSize = value.ToString ().Length;
600 private void SetDbType (DbType type)
602 string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
604 case DbType.AnsiString:
605 oracleType = OracleType.VarChar;
606 ociType = OciDataType.VarChar;
608 case DbType.AnsiStringFixedLength:
609 oracleType = OracleType.Char;
610 ociType = OciDataType.Char;
614 oracleType = OracleType.Raw;
615 ociType = OciDataType.Raw;
619 oracleType = OracleType.Byte;
620 ociType = OciDataType.Integer;
622 case DbType.Currency:
625 oracleType = OracleType.Number;
626 ociType = OciDataType.Number;
629 case DbType.DateTime:
631 oracleType = OracleType.DateTime;
632 ociType = OciDataType.Char;
635 oracleType = OracleType.Double;
636 ociType = OciDataType.Float;
639 oracleType = OracleType.Int16;
640 ociType = OciDataType.Integer;
643 oracleType = OracleType.Int32;
644 ociType = OciDataType.Integer;
647 oracleType = OracleType.Blob;
648 ociType = OciDataType.Blob;
651 oracleType = OracleType.Float;
652 ociType = OciDataType.Float;
655 oracleType = OracleType.NVarChar;
656 ociType = OciDataType.VarChar;
658 case DbType.StringFixedLength:
659 oracleType = OracleType.NChar;
660 ociType = OciDataType.Char;
663 throw new ArgumentException (exception);
669 private void SetOracleType (OracleType type)
671 string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
673 case OracleType.BFile:
674 case OracleType.Blob:
675 dbType = DbType.Binary;
676 ociType = OciDataType.Blob;
678 case OracleType.LongRaw:
680 dbType = DbType.Binary;
681 ociType = OciDataType.Raw;
683 case OracleType.Byte:
684 dbType = DbType.Byte;
685 ociType = OciDataType.Number;
687 case OracleType.Char:
688 dbType = DbType.AnsiString;
689 ociType = OciDataType.Char;
691 case OracleType.Clob:
692 dbType = DbType.AnsiString;
693 ociType = OciDataType.Clob;
695 case OracleType.LongVarChar:
696 case OracleType.RowId:
697 case OracleType.VarChar:
698 dbType = DbType.AnsiString;
699 ociType = OciDataType.VarChar;
701 case OracleType.Cursor:
702 case OracleType.IntervalDayToSecond:
703 dbType = DbType.AnsiStringFixedLength;
704 ociType = OciDataType.Char;
706 case OracleType.Timestamp:
707 case OracleType.TimestampLocal:
708 case OracleType.TimestampWithTZ:
709 dbType = DbType.DateTime;
710 ociType = OciDataType.TimeStamp;
712 case OracleType.DateTime:
713 dbType = DbType.DateTime;
714 ociType = OciDataType.Date;
716 case OracleType.Double:
717 dbType = DbType.Double;
718 ociType = OciDataType.Number;
720 case OracleType.Float:
721 dbType = DbType.Single;
722 ociType = OciDataType.Number;
724 case OracleType.Int16:
725 dbType = DbType.Int16;
726 ociType = OciDataType.Number;
728 case OracleType.Int32:
729 case OracleType.IntervalYearToMonth:
730 dbType = DbType.Int32;
731 ociType = OciDataType.Number;
733 case OracleType.NChar:
734 dbType = DbType.StringFixedLength;
735 ociType = OciDataType.Char;
737 case OracleType.NClob:
738 case OracleType.NVarChar:
739 dbType = DbType.String;
740 ociType = OciDataType.Char;
742 case OracleType.Number:
743 dbType = DbType.VarNumeric;
744 ociType = OciDataType.Number;
746 case OracleType.SByte:
747 dbType = DbType.SByte;
748 ociType = OciDataType.Number;
750 case OracleType.UInt16:
751 dbType = DbType.UInt16;
752 ociType = OciDataType.Number;
754 case OracleType.UInt32:
755 dbType = DbType.UInt32;
756 ociType = OciDataType.Number;
759 throw new ArgumentException (exception);
765 public override string ToString ()
767 return ParameterName;
770 internal void Update (OracleCommand cmd)
772 // used to update the parameter value
773 // for Output, the output of InputOutput, and Return parameters
774 value = DBNull.Value;
778 byte[] buffer = null;
782 case OciDataType.VarChar2:
783 case OciDataType.String:
784 case OciDataType.VarChar:
785 case OciDataType.Char:
786 case OciDataType.CharZ:
787 case OciDataType.OciString:
788 case OciDataType.RowIdDescriptor:
789 buffer = new byte [Size];
790 Marshal.Copy (bindOutValue, buffer, 0, Size);
792 // Get length of returned string
794 IntPtr env = cmd.Connection.Environment;
795 OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
798 StringBuilder ret = new StringBuilder(rsize);
799 OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
801 value = ret.ToString ();
803 case OciDataType.Integer:
804 case OciDataType.Number:
805 case OciDataType.Float:
806 tmp = Marshal.PtrToStringAnsi (bindOutValue, bindSize);
808 value = Decimal.Parse (String.Copy ((string) tmp));
810 case OciDataType.TimeStamp:
811 value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
813 case OciDataType.Date:
814 value = UnpackDate (bindOutValue);
816 case OciDataType.Blob:
817 case OciDataType.Clob:
818 OracleLob lob = new OracleLob (lobLocator, ociType);
819 lob.connection = connection;
823 throw new NotImplementedException ();
827 // copied from OciDefineHandle
828 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
829 internal DateTime UnpackDate (IntPtr dateValue)
831 byte century = Marshal.ReadByte (dateValue, 0);
832 byte year = Marshal.ReadByte (dateValue, 1);
833 byte month = Marshal.ReadByte (dateValue, 2);
834 byte day = Marshal.ReadByte (dateValue, 3);
835 byte hour = Marshal.ReadByte (dateValue, 4);
836 byte minute = Marshal.ReadByte (dateValue, 5);
837 byte second = Marshal.ReadByte (dateValue, 6);
839 return new DateTime ((century - 100) * 100 + (year - 100),
848 internal byte[] PackDate (DateTime dateValue)
850 byte[] buffer = new byte[7];
852 buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
853 buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
854 buffer[2] = (byte)dateValue.Month;
855 buffer[3] = (byte)dateValue.Day;
856 buffer[4] = (byte)(dateValue.Hour+1);
857 buffer[5] = (byte)(dateValue.Minute+1);
858 buffer[6] = (byte)(dateValue.Second+1);
863 #endregion // Methods
865 internal sealed class OracleParameterConverter : ExpandableObjectConverter
867 public OracleParameterConverter ()
872 public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
874 throw new NotImplementedException ();
878 public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
880 throw new NotImplementedException ();