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.SqlTypes;
27 using System.Data.OracleClient.Oci;
28 using System.Globalization;
29 using System.Runtime.InteropServices;
32 namespace System.Data.OracleClient {
33 [TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
34 public sealed class OracleParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
39 OracleType oracleType = OracleType.VarChar;
42 ParameterDirection direction = ParameterDirection.Input;
47 DataRowVersion srcVersion;
48 DbType dbType = DbType.AnsiString;
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;
57 OracleParameterCollection container = null;
58 OciBindHandle bindHandle;
59 OciErrorHandle errorHandle;
60 OracleConnection connection;
62 IntPtr bindValue = IntPtr.Zero;
66 short indicator = 0; // TODO: handle indicator to indicate NULL value for OUT parameters
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;
93 public OracleParameter ()
94 : this (String.Empty, OracleType.VarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
98 public OracleParameter (string name, object value)
102 SourceVersion = DataRowVersion.Current;
103 InferOracleType (value);
106 public OracleParameter (string name, OracleType dataType)
107 : this (name, dataType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
111 public OracleParameter (string name, OracleType dataType, int size)
112 : this (name, dataType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
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)
121 public OracleParameter (string name, OracleType dataType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
127 OracleType = dataType;
128 Direction = direction;
129 SourceColumn = srcColumn;
130 SourceVersion = srcVersion;
133 #endregion // Constructors
137 internal OracleParameterCollection Container {
138 get { return container; }
139 set { container = value; }
143 [RefreshProperties (RefreshProperties.All)]
144 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
145 public DbType DbType {
146 get { return dbType; }
147 set { SetDbType (value); }
150 [DefaultValue (ParameterDirection.Input)]
151 [RefreshProperties (RefreshProperties.All)]
152 public ParameterDirection Direction {
153 get { return direction; }
154 set { direction = value; }
159 [DefaultValue (false)]
160 [EditorBrowsable (EditorBrowsableState.Never)]
161 public bool IsNullable {
162 get { return isNullable; }
163 set { isNullable = value; }
169 get { return offset; }
170 set { offset = value; }
173 [DefaultValue (OracleType.VarChar)]
174 [RefreshProperties (RefreshProperties.All)]
175 public OracleType OracleType {
176 get { return oracleType; }
177 set { SetOracleType (value); }
181 public string ParameterName {
183 set { name = value; }
187 public byte Precision {
188 get { return precision; }
189 set { /* NO EFFECT*/ }
194 get { return scale; }
195 set { /* NO EFFECT*/ }
208 public string SourceColumn {
209 get { return srcColumn; }
210 set { srcColumn = value; }
213 [DefaultValue ("Current")]
214 public DataRowVersion SourceVersion {
215 get { return srcVersion; }
216 set { srcVersion = value; }
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; }
227 #endregion // Properties
231 private void AssertSizeIsSet ()
234 case OciDataType.VarChar2:
235 case OciDataType.String:
236 case OciDataType.VarChar:
237 case OciDataType.Char:
238 case OciDataType.CharZ:
239 case OciDataType.OciString:
241 throw new Exception ("Size must be set.");
248 internal void Bind (OciStatementHandle statement, OracleConnection con, uint pos)
252 errorHandle = connection.ErrorHandle;
254 if (bindHandle == null)
255 bindHandle = new OciBindHandle ((OciHandle) statement);
257 IntPtr tmpHandle = bindHandle.Handle;
261 if (Direction != ParameterDirection.Input)
272 bool isnull = false;
\r
273 if (direction == ParameterDirection.Input || direction == ParameterDirection.InputOutput) {
\r
275 Type nullable = v.GetType ().GetInterface ("System.Data.SqlTypes.INullable", false);
\r
276 if (nullable != null) {
\r
277 INullable mynullable = (INullable)v;
\r
278 isnull = mynullable.IsNull;
\r
282 System.IO.TextWriter.Null.WriteLine(e.Message);
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
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;
300 bindOutValue = Marshal.AllocHGlobal (bindSize);
301 bindValue = bindOutValue;
303 case OciDataType.RowIdDescriptor:
305 bindType = OciDataType.Char;
307 bindOutValue = Marshal.AllocHGlobal (bindSize);
308 bindValue = bindOutValue;
310 case OciDataType.Date:
312 bindType = OciDataType.Date;
313 bindOutValue = Marshal.AllocHGlobal (bindSize);
314 bindValue = bindOutValue;
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);
322 dateTimeDesc.ErrorHandle = connection.ErrorHandle;
324 bindType = OciDataType.TimeStamp;
325 bindOutValue = dateTimeDesc.Handle;
326 bindValue = dateTimeDesc.Handle;
329 case OciDataType.Number:
331 bindType = OciDataType.Char;
332 bindOutValue = Marshal.AllocHGlobal (bindSize);
333 bindValue = bindOutValue;
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;
345 case OciDataType.Blob:
346 case OciDataType.Clob:
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);
353 bindOutValue = lobLocator.Handle;
354 bindValue = lobLocator.Handle;
355 lobLocator.ErrorHandle = connection.ErrorHandle;
356 lobLocator.Service = statement.Service;
359 case OciDataType.RSet: // REF CURSOR
360 cursor = IntPtr.Zero;
361 OciCalls.OCIHandleAlloc (connection.Environment,
363 OciHandleType.Statement,
368 bindType = OciDataType.RSet;
371 // define other types
372 throw new NotImplementedException ();
374 bindValue = bindOutValue;
376 else if ((v == DBNull.Value || v == null || isnull == true) && direction == ParameterDirection.Input) {
378 bindType = OciDataType.VarChar2;
382 // TODO: do other data types and oracle data types
383 // should I be using IConvertible to convert?
385 DateTime dt = DateTime.MinValue;
386 if (oracleType == OracleType.Timestamp){
387 bindType = OciDataType.TimeStamp;
389 dt = DateTime.MinValue;
393 dt = DateTime.Parse (sDate);
395 else if (v is DateTime)
397 else if (v is OracleString){
399 dt = DateTime.Parse (sDate);
401 else if (v is OracleDateTime) {
402 OracleDateTime odt = (OracleDateTime) v;
403 dt = (DateTime) odt.Value;
406 throw new NotImplementedException (); // ?
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);
421 dateTimeDesc.ErrorHandle = connection.ErrorHandle;
422 dateTimeDesc.SetDateTime (connection.Session,
423 connection.ErrorHandle,
424 year, month, day, hour, min, sec, fsec,
428 else if (oracleType == OracleType.DateTime) {
430 dt = DateTime.MinValue;
433 dt = DateTime.Parse (sDate);
435 else if (v is DateTime)
437 else if (v is OracleString) {
439 dt = DateTime.Parse (sDate);
441 else if (v is OracleDateTime) {
442 OracleDateTime odt = (OracleDateTime) v;
443 dt = (DateTime) odt.Value;
446 throw new NotImplementedException (); // ?
448 bytes = PackDate (dt);
449 bindType = OciDataType.Date;
450 bindSize = bytes.Length;
452 else if (oracleType == OracleType.Blob) {
454 bindType = OciDataType.LongRaw;
455 bindSize = bytes.Length;
457 else if (oracleType == OracleType.Clob) {
458 string sv = v.ToString();
461 // Get size of buffer
462 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, sv, out rsize);
465 bytes = new byte[rsize];
466 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, sv, out rsize);
468 bindType = OciDataType.Long;
469 bindSize = bytes.Length;
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;
477 else if (oracleType == OracleType.Number) {
478 // TODO: move number type to a non-locale specific way
479 string svalue = v.ToString ();
482 // Get size of buffer
483 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
486 bytes = new byte[rsize];
487 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
489 bindType = OciDataType.VarChar2;
490 bindSize = v.ToString ().Length;
493 string svalue = v.ToString ();
496 // Get size of buffer
497 OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
500 bytes = new byte[rsize];
501 OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
503 bindType = OciDataType.VarChar2;
504 bindSize = v.ToString ().Length;
508 // Now, call the appropriate OCI Bind function
510 if (useRef == true) {
511 if (bindType == OciDataType.TimeStamp) {
512 bindValue = dateTimeDesc.Handle;
513 status = OciCalls.OCIBindByNameRef (statement,
515 connection.ErrorHandle,
517 ParameterName.Length,
529 status = OciCalls.OCIBindByNameRef (statement,
531 connection.ErrorHandle,
533 ParameterName.Length,
545 else if (bindType == OciDataType.RSet) {
546 status = OciCalls.OCIBindByNameRef (statement,
548 connection.ErrorHandle,
550 ParameterName.Length,
561 else if (bytes != null) {
562 status = OciCalls.OCIBindByNameBytes (statement,
564 connection.ErrorHandle,
566 ParameterName.Length,
578 status = OciCalls.OCIBindByName (statement,
580 connection.ErrorHandle,
582 ParameterName.Length,
595 OciErrorInfo info = connection.ErrorHandle.HandleError ();
596 throw new OracleException (info.ErrorCode, info.ErrorMessage);
599 bindHandle.SetHandle (tmpHandle);
602 object ICloneable.Clone ()
604 return new OracleParameter(this);
607 private void InferOracleType (object value)
609 Type type = value.GetType ();
610 string exception = String.Format ("The parameter data type of {0} is invalid.", type.Name);
611 switch (type.FullName) {
613 SetOracleType (OracleType.Number);
615 case "System.Boolean":
617 SetOracleType (OracleType.Byte);
619 case "System.String":
620 SetOracleType (OracleType.VarChar);
622 case "System.DateTime":
623 SetOracleType (OracleType.DateTime);
625 case "System.Decimal":
626 SetOracleType (OracleType.Number);
627 //scale = ((decimal) value).Scale;
629 case "System.Double":
630 SetOracleType (OracleType.Double);
632 case "System.Byte[]":
634 SetOracleType (OracleType.Raw);
637 SetOracleType (OracleType.Int32);
639 case "System.Single":
640 SetOracleType (OracleType.Float);
643 SetOracleType (OracleType.Int16);
646 throw new ArgumentException (exception);
650 private int InferSize ()
655 case OciDataType.VarChar2:
656 case OciDataType.String:
657 case OciDataType.VarChar:
658 case OciDataType.Char:
659 case OciDataType.CharZ:
660 case OciDataType.OciString:
661 case OciDataType.Long:
662 case OciDataType.LongVarChar:
663 if (value == null || value == DBNull.Value)
666 newSize = value.ToString ().Length;
668 case OciDataType.RowIdDescriptor:
671 case OciDataType.Integer:
672 case OciDataType.Number:
673 case OciDataType.Float:
676 case OciDataType.Date:
679 case OciDataType.TimeStamp:
682 case OciDataType.Blob:
683 case OciDataType.Clob:
684 case OciDataType.RSet: // REF CURSOR
688 if (value == null || value == DBNull.Value)
691 newSize = value.ToString ().Length;
700 private void SetDbType (DbType type)
702 string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
704 case DbType.AnsiString:
705 oracleType = OracleType.VarChar;
706 ociType = OciDataType.VarChar;
708 case DbType.AnsiStringFixedLength:
709 oracleType = OracleType.Char;
710 ociType = OciDataType.Char;
714 oracleType = OracleType.Raw;
715 ociType = OciDataType.Raw;
719 oracleType = OracleType.Byte;
720 ociType = OciDataType.Integer;
722 case DbType.Currency:
725 oracleType = OracleType.Number;
726 ociType = OciDataType.Number;
729 case DbType.DateTime:
731 oracleType = OracleType.DateTime;
732 ociType = OciDataType.Char;
735 oracleType = OracleType.Double;
736 ociType = OciDataType.Float;
739 oracleType = OracleType.Int16;
740 ociType = OciDataType.Integer;
743 oracleType = OracleType.Int32;
744 ociType = OciDataType.Integer;
747 oracleType = OracleType.Blob;
748 ociType = OciDataType.Blob;
751 oracleType = OracleType.Float;
752 ociType = OciDataType.Float;
755 oracleType = OracleType.NVarChar;
756 ociType = OciDataType.VarChar;
758 case DbType.StringFixedLength:
759 oracleType = OracleType.NChar;
760 ociType = OciDataType.Char;
763 throw new ArgumentException (exception);
769 private void SetOracleType (OracleType type)
771 string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
773 case OracleType.BFile:
774 case OracleType.Blob:
775 dbType = DbType.Binary;
776 ociType = OciDataType.Blob;
778 case OracleType.LongRaw:
780 dbType = DbType.Binary;
781 ociType = OciDataType.Raw;
783 case OracleType.Byte:
784 dbType = DbType.Byte;
785 ociType = OciDataType.Number;
787 case OracleType.Char:
788 dbType = DbType.AnsiString;
789 ociType = OciDataType.Char;
791 case OracleType.Clob:
792 dbType = DbType.AnsiString;
793 ociType = OciDataType.Clob;
795 case OracleType.LongVarChar:
796 case OracleType.RowId:
797 case OracleType.VarChar:
798 dbType = DbType.AnsiString;
799 ociType = OciDataType.VarChar;
801 case OracleType.Cursor: // REF CURSOR
802 ociType = OciDataType.RSet;
803 dbType = DbType.Object;
805 case OracleType.IntervalDayToSecond:
806 dbType = DbType.AnsiStringFixedLength;
807 ociType = OciDataType.Char;
809 case OracleType.Timestamp:
810 case OracleType.TimestampLocal:
811 case OracleType.TimestampWithTZ:
812 dbType = DbType.DateTime;
813 ociType = OciDataType.TimeStamp;
815 case OracleType.DateTime:
816 dbType = DbType.DateTime;
817 ociType = OciDataType.Date;
819 case OracleType.Double:
820 dbType = DbType.Double;
821 ociType = OciDataType.Number;
823 case OracleType.Float:
824 dbType = DbType.Single;
825 ociType = OciDataType.Number;
827 case OracleType.Int16:
828 dbType = DbType.Int16;
829 ociType = OciDataType.Number;
831 case OracleType.Int32:
832 case OracleType.IntervalYearToMonth:
833 dbType = DbType.Int32;
834 ociType = OciDataType.Number;
836 case OracleType.NChar:
837 dbType = DbType.StringFixedLength;
838 ociType = OciDataType.Char;
840 case OracleType.NClob:
841 case OracleType.NVarChar:
842 dbType = DbType.String;
843 ociType = OciDataType.Char;
845 case OracleType.Number:
846 dbType = DbType.VarNumeric;
847 ociType = OciDataType.Number;
849 case OracleType.SByte:
850 dbType = DbType.SByte;
851 ociType = OciDataType.Number;
853 case OracleType.UInt16:
854 dbType = DbType.UInt16;
855 ociType = OciDataType.Number;
857 case OracleType.UInt32:
858 dbType = DbType.UInt32;
859 ociType = OciDataType.Number;
862 throw new ArgumentException (exception);
868 public override string ToString ()
870 return ParameterName;
873 private void GetOutValue (OracleCommand cmd)
875 // used to update the parameter value
876 // for Output, the output of InputOutput, and Return parameters
877 value = DBNull.Value;
881 byte[] buffer = null;
885 case OciDataType.VarChar2:
886 case OciDataType.String:
887 case OciDataType.VarChar:
888 case OciDataType.Char:
889 case OciDataType.CharZ:
890 case OciDataType.OciString:
891 case OciDataType.RowIdDescriptor:
892 buffer = new byte [Size];
893 Marshal.Copy (bindOutValue, buffer, 0, Size);
895 // Get length of returned string
897 IntPtr env = cmd.Connection.Environment;
898 OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
901 StringBuilder ret = new StringBuilder(rsize);
902 OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
904 value = ret.ToString ();
906 case OciDataType.Integer:
907 case OciDataType.Number:
908 case OciDataType.Float:
909 tmp = Marshal.PtrToStringAnsi (bindOutValue, bindSize);
911 value = Decimal.Parse (String.Copy ((string) tmp));
913 case OciDataType.TimeStamp:
914 value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
916 case OciDataType.Date:
917 value = UnpackDate (bindOutValue);
919 case OciDataType.Blob:
920 case OciDataType.Clob:
921 OracleLob lob = new OracleLob (lobLocator, ociType);
922 lob.connection = connection;
925 case OciDataType.RSet: // REF CURSOR
926 OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.Environment, cursor);
927 cursorStatement.ErrorHandle = cmd.ErrorHandle;
928 cursorStatement.Command = cmd.Connection.CreateCommand ();
929 cursorStatement.SetupRefCursorResult ();
930 value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
931 cursor = IntPtr.Zero;
932 cursorStatement = null;
935 throw new NotImplementedException ();
941 internal void Update (OracleCommand cmd)
943 if (Direction != ParameterDirection.Input)
949 private void FreeHandle ()
952 case OciDataType.Clob:
953 case OciDataType.Blob:
956 case OciDataType.Raw:
957 Marshal.FreeHGlobal (bindValue);
961 bindOutValue = IntPtr.Zero;
962 bindValue = IntPtr.Zero;
969 // copied from OciDefineHandle
970 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
971 internal DateTime UnpackDate (IntPtr dateValue)
973 byte century = Marshal.ReadByte (dateValue, 0);
974 byte year = Marshal.ReadByte (dateValue, 1);
975 byte month = Marshal.ReadByte (dateValue, 2);
976 byte day = Marshal.ReadByte (dateValue, 3);
977 byte hour = Marshal.ReadByte (dateValue, 4);
978 byte minute = Marshal.ReadByte (dateValue, 5);
979 byte second = Marshal.ReadByte (dateValue, 6);
981 return new DateTime ((century - 100) * 100 + (year - 100),
990 internal byte[] PackDate (DateTime dateValue)
992 byte[] buffer = new byte[7];
994 buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
995 buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
996 buffer[2] = (byte)dateValue.Month;
997 buffer[3] = (byte)dateValue.Day;
998 buffer[4] = (byte)(dateValue.Hour+1);
999 buffer[5] = (byte)(dateValue.Minute+1);
1000 buffer[6] = (byte)(dateValue.Second+1);
1005 #endregion // Methods
1007 internal sealed class OracleParameterConverter : ExpandableObjectConverter
1009 public OracleParameterConverter ()
1014 public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
1016 throw new NotImplementedException ();
1020 public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
1022 throw new NotImplementedException ();