2007-06-06 Amit Biswas <amit@amitbiswas.com>
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcParameter.cs
index 77c2d7e083c755df0d3a086518be63844f22239b..0cc330ebcd340e3a1cc77dcbcab491b41917fee6 100644 (file)
@@ -3,6 +3,7 @@
 //
 // Authors:
 //   Brian Ritchie (brianlritchie@hotmail.com)
+//   Sureshkumar T <tsureshkumar@novell.com>  2004.
 //
 // Copyright (C) Brian Ritchie, 2002
 //
 //
 
 using System;
+using System.Text;
 using System.Data;
 using System.Data.Common;
+using System.Runtime.InteropServices;
+using System.Globalization;
 using System.ComponentModel;
 
 namespace System.Data.Odbc
 {
-       [TypeConverterAttribute (typeof (OdbcParameterConverter))]      
+       [TypeConverterAttribute (typeof (OdbcParameterConverter))]
+#if NET_2_0
+        public sealed class OdbcParameter : DbParameter, ICloneable
+#else
        public sealed class OdbcParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
+#endif // NET_2_0
        {
                #region Fields
 
                string name;
-               object ParamValue;
-               int size;
+               ParameterDirection direction;
                bool isNullable;
-               byte precision;
-               byte scale;
+               int size;
                DataRowVersion sourceVersion;
                string sourceColumn;
-               ParameterDirection direction;
-               OdbcType odbcType;
-               DbType dbType;
-               OdbcParameterCollection container = null;       
-               
-               // Buffers for parameter value based on type. Currently I've only optimized 
-               // for int parameters and everything else is just converted to a string.
-               private bool bufferIsSet;
-               int intbuf;
-               byte[] buffer;
+               byte _precision;
+               byte _scale;
+               object _value;
 
+               private OdbcTypeMap _typeMap;
+               private NativeBuffer _nativeBuffer = new NativeBuffer ();
+               private NativeBuffer _cbLengthInd;
+               private OdbcParameterCollection container = null;       
+               
                #endregion
 
                #region Constructors
                
                public OdbcParameter ()
                {
-                       name = String.Empty;
-                       ParamValue = null;
-                       size = 0;
-                       isNullable = true;
-                       precision = 0;
-                       scale = 0;
-                       sourceColumn = String.Empty;
+                       _cbLengthInd = new NativeBuffer ();
+                       ParameterName = String.Empty;
+                       IsNullable = false;
+                       SourceColumn = String.Empty;
+                       Direction = ParameterDirection.Input;
+                       _typeMap = OdbcTypeConverter.GetTypeMap (OdbcType.NVarChar);
                }
 
                public OdbcParameter (string name, object value) 
                        : this ()
                {
-                       this.name = name;
-                       this.ParamValue = value;
-                        
-                        if (value != null && !value.GetType ().IsValueType) {
-                                Type type = value.GetType ();
-                                if (type.IsArray)
-                                        size = type.GetElementType () == typeof (byte) ? 
+                       this.ParameterName = name;
+                       Value = value;
+                       //FIXME: MS.net does not infer OdbcType from value unless a type is provided
+                       _typeMap = OdbcTypeConverter.InferFromValue (value);
+                       if (value != null && !value.GetType ().IsValueType) {
+                               Type type = value.GetType ();
+                               if (type.IsArray)
+                                       Size = type.GetElementType () == typeof (byte) ?
                                                 ((Array) value).Length : 0;
-                                else
-                                        size = value.ToString ().Length;
+                               else
+                                       Size = value.ToString ().Length;
                         }
-
-
                }
 
-               public OdbcParameter (string name, OdbcType dataType) 
+               public OdbcParameter (string name, OdbcType odbcType) 
                        : this ()
                {
-                       this.name = name;
-                       OdbcType = dataType;
+                       this.ParameterName = name;
+                       _typeMap = (OdbcTypeMap) OdbcTypeConverter.GetTypeMap (odbcType);
                }
 
-               public OdbcParameter (string name, OdbcType dataType, int size)
-                       : this (name, dataType)
+               public OdbcParameter (string name, OdbcType odbcType, int size)
+                       : this (name, odbcType)
                {
-                       this.size = size;
+                       this.Size = size;
                }
 
-               public OdbcParameter (string name, OdbcType dataType, int size, string srcColumn)
-                       : this (name, dataType, size)
+               public OdbcParameter (string name, OdbcType odbcType, int size, string srcColumn)
+                       : this (name, odbcType, size)
                {
-                       this.sourceColumn = srcColumn;
+                       this.SourceColumn = srcColumn;
                }
 
                [EditorBrowsable (EditorBrowsableState.Advanced)]
-               public OdbcParameter(string name, OdbcType dataType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
-                       : this (name, dataType, size, srcColumn)
+               public OdbcParameter(string name, OdbcType odbcType, int size, 
+                                    ParameterDirection direction, bool isNullable, 
+                                    byte precision, byte scale, string srcColumn, 
+                                    DataRowVersion srcVersion, object value)
+                       : this (name, odbcType, size, srcColumn)
                {
-                       this.direction = direction;
-                       this.isNullable = isNullable;
-                       this.precision = precision;
-                       this.scale = scale;
-                       this.sourceVersion = srcVersion;
-                       this.ParamValue = value;
+                       this.Direction = direction;
+                       this.IsNullable = isNullable;
+                       this.SourceVersion = srcVersion;
                }
 
                #endregion
@@ -141,17 +143,28 @@ namespace System.Data.Odbc
                 [RefreshPropertiesAttribute (RefreshProperties.All)]
                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
                [OdbcCategory ("Data")]
-               public DbType DbType {
-                       get { return dbType; }
+               public 
+#if NET_2_0
+                override 
+#endif
+                DbType DbType {
+                       get { return _typeMap.DbType; }
                        set { 
-                               dbType = value;
+                               if (value == _typeMap.DbType)
+                                       return;
+                               
+                               _typeMap = OdbcTypeConverter.GetTypeMap (value);
                        }
                }
                
                [OdbcCategory ("Data")]
                [OdbcDescriptionAttribute ("Input, output, or bidirectional parameter")]  
                [DefaultValue (ParameterDirection.Input)]
-               public ParameterDirection Direction {
+               public 
+#if NET_2_0
+                override 
+#endif
+               ParameterDirection Direction {
                        get { return direction; }
                        set { direction = value; }
                }
@@ -161,25 +174,37 @@ namespace System.Data.Odbc
                 [DesignOnlyAttribute (true)]
                 [EditorBrowsableAttribute (EditorBrowsableState.Advanced)]
                 [DefaultValue (false)]
-               public bool IsNullable {
-                       get { return isNullable; }\r
-                       set { isNullable = value; }\r
+               public 
+#if NET_2_0
+                override 
+#endif
+               bool IsNullable {
+                       get { return isNullable; }
+                       set { isNullable = value; }
                }
 
+
                [DefaultValue (OdbcType.NChar)]
                 [OdbcDescriptionAttribute ("The parameter native type")]
                 [RefreshPropertiesAttribute (RefreshProperties.All)]
                [OdbcCategory ("Data")]
                public OdbcType OdbcType {
-                       get { return odbcType; }
+                       get { return _typeMap.OdbcType; }
                        set {
-                               odbcType = value;
+                               if (value == OdbcType)
+                                       return;
+
+                               _typeMap = OdbcTypeConverter.GetTypeMap (value);
                        }
                }
                
                [OdbcDescription ("DataParameter_ParameterName")]
                 [DefaultValue ("")]    
-               public string ParameterName {
+               public 
+#if NET_2_0
+                override 
+#endif
+               string ParameterName {
                        get { return name; }
                        set { name = value; }
                }
@@ -188,22 +213,26 @@ namespace System.Data.Odbc
                 [OdbcCategory ("DataCategory_Data")]
                 [DefaultValue (0)]
                public byte Precision {
-                       get { return precision; }
-                       set { precision = value; }
+                       get { return _precision; }
+                       set { _precision = value; }
                }
                
                 [OdbcDescription ("DbDataParameter_Scale")]
                 [OdbcCategory ("DataCategory_Data")]
                 [DefaultValue (0)]
                public byte Scale {
-                       get { return scale; }
-                       set { scale = value; }
+                       get { return _scale; }
+                       set { _scale = value; }
                }
                
                [OdbcDescription ("DbDataParameter_Size")]
                 [OdbcCategory ("DataCategory_Data")]
                 [DefaultValue (0)]
-               public int Size {
+               public 
+#if NET_2_0
+                override 
+#endif
+               int Size {
                        get { return size; }
                        set { size = value; }
                }
@@ -211,15 +240,23 @@ namespace System.Data.Odbc
                [OdbcDescription ("DataParameter_SourceColumn")]
                 [OdbcCategory ("DataCategory_Data")]
                 [DefaultValue ("")]
-               public string SourceColumn {
+               public 
+#if NET_2_0
+                override 
+#endif
+               string SourceColumn {
                        get { return sourceColumn; }
                        set { sourceColumn = value; }
                }
                
                 [OdbcDescription ("DataParameter_SourceVersion")]
                 [OdbcCategory ("DataCategory_Data")]
-                [DefaultValue (512)]                   
-               public DataRowVersion SourceVersion {
+                [DefaultValue ("Current")]                     
+               public 
+#if NET_2_0
+                override 
+#endif
+               DataRowVersion SourceVersion {
                        get { return sourceVersion; }
                        set { sourceVersion = value; }
                }
@@ -228,67 +265,41 @@ namespace System.Data.Odbc
                 [OdbcDescription ("DataParameter_Value")]
                 [OdbcCategory ("DataCategory_Data")]
                 [DefaultValue (null)]          
-               public object Value {
+               public 
+#if NET_2_0
+                override 
+#endif
+               object Value {
                        get { 
-                               return ParamValue;
+                               return _value;
                        }
                        set { 
-                               this.ParamValue = value;
-                               bufferIsSet = false;
+                               _value = value;
                        }
                }
 
                #endregion // Properties
 
-               #region Methods\r
-\r
-               internal void Bind(IntPtr hstmt, int ParamNum) {\r
-                       OdbcReturn ret;\r
-                       // Set up the buffer if we haven't done so yet\r
-                       if (!bufferIsSet)\r
-                               setBuffer();\r
-\r
-                       // Convert System.Data.ParameterDirection into odbc enum\r
-                       OdbcInputOutputDirection paramdir = libodbc.ConvertParameterDirection(this.direction);\r
-                       // Bind parameter based on type\r
-                       if (odbcType == OdbcType.Int)\r
-                               ret = libodbc.SQLBindParameter(hstmt, (ushort)ParamNum, (short)paramdir,\r
-                                       (short)odbcType, (short)odbcType, Convert.ToUInt32(size),\r
-                                       0, ref intbuf, 0, 0);\r
-                       else\r
-                               ret = libodbc.SQLBindParameter(hstmt, (ushort)ParamNum, (short)paramdir,\r
-                                       (short)OdbcType.Char, (short)odbcType, Convert.ToUInt32(size),\r
-                                       0, buffer, 0, 0);\r
-                       // Check for error condition\r
-                       if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))\r
-                               throw new OdbcException(new OdbcError("SQLBindParam", OdbcHandleType.Stmt, hstmt));\r
-               }\r
-\r
-               private void setBuffer() {\r
-                       // Load buffer with new value
-                       if (odbcType == OdbcType.Int)
-                                intbuf = ParamValue == null ? new int () : (int) ParamValue;
-                        else {\r
-                               string paramValueString = ParamValue.ToString();\r
-                               // Treat everything else as a string\r
-                               // Init string buffer\r
-                                if (ParamValue is String)
-                                        paramValueString = "\'"+paramValueString+"\'";
-
-                                 int minSize = size;
-                                 minSize = size > 20 ? size : 20;
-                                if (buffer == null || buffer.Length < minSize)
-                                         buffer = new byte[minSize];
-                                 else
-                                         buffer.Initialize();
-                                 
-                                 // Convert value into string and store into buffer
-                                 minSize = paramValueString.Length < minSize ? paramValueString.Length : minSize;
-                                 System.Text.Encoding.ASCII.GetBytes(paramValueString, 0, minSize, buffer, 0);\r
-                       }\r
-                       bufferIsSet = true;\r
-               }\r
-\r
+               #region Methods
+
+               internal void Bind(IntPtr hstmt, int ParamNum) {
+                       OdbcReturn ret;
+                       
+                       // Convert System.Data.ParameterDirection into odbc enum
+                       OdbcInputOutputDirection paramdir = libodbc.ConvertParameterDirection(this.Direction);
+
+                       _cbLengthInd.EnsureAlloc (Marshal.SizeOf (typeof (int)));
+                       Marshal.WriteInt32 (_cbLengthInd, GetNativeSize ());
+                       AllocateBuffer ();
+                       ret = libodbc.SQLBindParameter(hstmt, (ushort) ParamNum, (short) paramdir,
+                                                      _typeMap.NativeType, _typeMap.SqlType, Convert.ToUInt32(Size),
+                                                      0, (IntPtr) _nativeBuffer, 0, _cbLengthInd);
+
+                       // Check for error condition
+                       if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
+                               throw new OdbcException(new OdbcError("SQLBindParam", OdbcHandleType.Stmt, hstmt));
+               }
+
                [MonoTODO]
                object ICloneable.Clone ()
                {
@@ -299,6 +310,202 @@ namespace System.Data.Odbc
                {
                        return ParameterName;
                }
+               
+               private int GetNativeSize ()
+               {
+                       TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
+                       Encoding enc = Encoding.GetEncoding (ti.ANSICodePage);
+
+                       switch (_typeMap.OdbcType) {
+                       case OdbcType.Binary:
+                               if (Value.GetType ().IsArray &&
+                                   Value.GetType ().GetElementType () == typeof (byte))
+                                       return ( (Array) Value).Length;
+                               else
+                                       return Value.ToString ().Length;
+                       case OdbcType.Bit:
+                               return Marshal.SizeOf (typeof  (byte));
+                       case OdbcType.Double:
+                               return Marshal.SizeOf (typeof (double));
+                       case OdbcType.Real:
+                               return Marshal.SizeOf (typeof (float));
+                       case OdbcType.Int:
+                               return Marshal.SizeOf (typeof (int));
+                       case OdbcType.BigInt:
+                               return Marshal.SizeOf (typeof  (long));
+                       case OdbcType.Decimal:
+                       case OdbcType.Numeric:
+                               return 19;
+                       case OdbcType.SmallInt:
+                               return Marshal.SizeOf (typeof  (Int16));
+                       case OdbcType.TinyInt:
+                               return Marshal.SizeOf (typeof  (byte));
+                       case OdbcType.Char:
+                       case OdbcType.Text:
+                       case OdbcType.VarChar:
+                               return enc.GetByteCount (Convert.ToString (Value)) + 1;
+                       case OdbcType.NChar:
+                       case OdbcType.NText:
+                       case OdbcType.NVarChar:
+                               // FIXME: Change to unicode
+                               return enc.GetByteCount (Convert.ToString (Value)) + 1;
+                       case OdbcType.VarBinary:
+                       case OdbcType.Image:
+                               if (Value.GetType ().IsArray &&
+                                   Value.GetType ().GetElementType () == typeof (byte))
+                                       return ( (Array) Value).Length;
+                               throw new ArgumentException ("Unsupported Native Type!");
+                       case OdbcType.Date:
+                       case OdbcType.DateTime:
+                       case OdbcType.SmallDateTime:
+                       case OdbcType.Time:
+                       case OdbcType.Timestamp:
+                               return 18;
+                       case OdbcType.UniqueIdentifier:
+                               return Marshal.SizeOf (typeof (Guid));
+                       }
+
+                       if (Value.GetType ().IsArray &&
+                           Value.GetType ().GetElementType () == typeof (byte))
+                               return ( (Array) Value).Length;
+                       
+                       return Value.ToString ().Length;
+               }
+
+               private void AllocateBuffer ()
+               {
+                       int size = GetNativeSize ();
+
+                       if (_nativeBuffer.Size == size)
+                               return;
+
+                       _nativeBuffer.AllocBuffer (size);
+               }
+
+               internal void CopyValue ()
+               {
+                       if (_nativeBuffer.Handle == IntPtr.Zero)
+                               return;
+
+                       DateTime dt;
+                       TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
+                       Encoding enc = Encoding.GetEncoding (ti.ANSICodePage);
+                       byte [] nativeBytes, buffer;
+
+                       switch (_typeMap.OdbcType) {
+                       case OdbcType.Binary:
+                               throw new NotImplementedException ();
+                       case OdbcType.Bit:
+                               Marshal.WriteByte (_nativeBuffer, Convert.ToByte (Value));
+                               return;
+                       case OdbcType.Double:
+                               Marshal.StructureToPtr (Convert.ToDouble (Value), _nativeBuffer, false);
+                               return;
+                       case OdbcType.Real:
+                               Marshal.StructureToPtr (Convert.ToSingle (Value), _nativeBuffer, false);
+                               return;
+                       case OdbcType.Int:
+                               Marshal.WriteInt32 (_nativeBuffer, Convert.ToInt32 (Value));
+                               return;
+                       case OdbcType.BigInt:
+                               Marshal.WriteInt64 (_nativeBuffer, Convert.ToInt64 (Value));
+                               return;
+                       case OdbcType.Decimal:
+                       case OdbcType.Numeric:
+                               // for numeric, the buffer is a packed decimal struct.
+                               // ref http://www.it-faq.pl/mskb/181/254.HTM
+                               int [] bits = Decimal.GetBits (Convert.ToDecimal (Value));
+                               buffer = new byte [19]; // ref sqltypes.h
+                               buffer [0] = Precision;
+                               buffer [1] = (byte) ((bits [3] & 0x00FF0000) >> 16); // scale
+                               buffer [2] = (byte) ((bits [3] & 0x80000000) > 0 ? 2 : 1); //sign
+                               Buffer.BlockCopy (bits, 0, buffer, 3, 12); // copy data
+                               for (int j = 16; j < 19; j++) // pad with 0
+                                       buffer [j] = 0;
+                               Marshal.Copy (buffer, 0, _nativeBuffer, 19); 
+                               return; 
+                       case OdbcType.SmallInt:
+                               Marshal.WriteInt16 (_nativeBuffer, Convert.ToInt16 (Value));
+                               return;
+                       case OdbcType.TinyInt:
+                               Marshal.WriteByte (_nativeBuffer, Convert.ToByte (Value));
+                               return;
+                       case OdbcType.Char:
+                       case OdbcType.Text:
+                       case OdbcType.VarChar:
+                               buffer = new byte [GetNativeSize ()];
+                               nativeBytes = enc.GetBytes (Convert.ToString (Value));
+                               Array.Copy (nativeBytes, 0, buffer, 0, nativeBytes.Length);
+                               buffer [buffer.Length-1] = (byte) 0;
+                               Marshal.Copy (buffer, 0, _nativeBuffer, buffer.Length);
+                               Marshal.WriteInt32 (_cbLengthInd, -3);
+                               return;
+                       case OdbcType.NChar:
+                       case OdbcType.NText:
+                       case OdbcType.NVarChar:
+                               // FIXME : change to unicode
+                               buffer = new byte [GetNativeSize ()];
+                               nativeBytes = enc.GetBytes (Convert.ToString (Value));
+                               Array.Copy (nativeBytes, 0, buffer, 0, nativeBytes.Length);
+                               buffer [buffer.Length-1] = (byte) 0;
+                               Marshal.Copy (buffer, 0, _nativeBuffer, buffer.Length);
+                               Marshal.WriteInt32 (_cbLengthInd, -3);
+                               return;
+                       case OdbcType.VarBinary:
+                       case OdbcType.Image:
+                               if (Value.GetType ().IsArray &&
+                                   Value.GetType ().GetElementType () == typeof (byte)) {
+                                       Marshal.Copy ( (byte []) Value, 0, _nativeBuffer, ((byte []) Value).Length);
+                               }else
+                                       throw new ArgumentException ("Unsupported Native Type!");
+                               return;
+                       case OdbcType.Date:
+                               dt = (DateTime) Value;
+                               Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Year);
+                               Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Month);
+                               Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Day);
+                               return;
+                       case OdbcType.Time:
+                               dt = (DateTime) Value;
+                               Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Hour);
+                               Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Minute);
+                               Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Second);
+                               return;
+                       case OdbcType.SmallDateTime:
+                       case OdbcType.Timestamp:
+                       case OdbcType.DateTime:
+                               dt = (DateTime) Value;
+                               Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Year);
+                               Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Month);
+                               Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Day);
+                               Marshal.WriteInt16 (_nativeBuffer, 6, (short) dt.Hour);
+                               Marshal.WriteInt16 (_nativeBuffer, 8, (short) dt.Minute);
+                               Marshal.WriteInt16 (_nativeBuffer, 10, (short) dt.Second);
+                               Marshal.WriteInt32 (_nativeBuffer, 12, (int) (dt.Ticks % 10000000) * 100);
+                               return;
+                       case OdbcType.UniqueIdentifier:
+                               throw new NotImplementedException ();
+                       }
+
+                       if (Value.GetType ().IsArray &&
+                           Value.GetType ().GetElementType () == typeof (byte)) {
+                               Marshal.Copy ( (byte []) Value, 0, _nativeBuffer, ((byte []) Value).Length);
+                       }else
+                               throw new ArgumentException ("Unsupported Native Type!");
+               }
+
+#if NET_2_0
+               public override bool SourceColumnNullMapping {
+                       get {return false;}
+                       set {}
+               }
+
+               public override void ResetDbType () 
+                {
+                        throw new NotImplementedException ();
+                }
+#endif
+
                #endregion
        }
 }