f2e9406be404c2196adb9e6c0991bf233a04e688
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcParameter.cs
1 //
2 // System.Data.Odbc.OdbcParameter
3 //
4 // Authors:
5 //   Brian Ritchie (brianlritchie@hotmail.com)
6 //   Sureshkumar T <tsureshkumar@novell.com>  2004.
7 //
8 // Copyright (C) Brian Ritchie, 2002
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Text;
36 using System.Data;
37 using System.Data.Common;
38 using System.Runtime.InteropServices;
39 using System.Globalization;
40 using System.ComponentModel;
41
42 namespace System.Data.Odbc
43 {
44         [TypeConverterAttribute (typeof (OdbcParameterConverter))]
45 #if NET_2_0
46         public sealed class OdbcParameter : DbParameter, ICloneable
47 #else
48         public sealed class OdbcParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
49 #endif // NET_2_0
50         {
51                 #region Fields
52
53                 string name;
54                 ParameterDirection direction;
55                 bool isNullable;
56                 int size;
57                 DataRowVersion sourceVersion;
58                 string sourceColumn;
59                 byte _precision;
60                 byte _scale;
61                 object _value;
62
63                 private OdbcTypeMap _typeMap;
64                 private NativeBuffer _nativeBuffer = new NativeBuffer ();
65                 private NativeBuffer _cbLengthInd;
66                 private OdbcParameterCollection container = null;       
67                 
68                 #endregion
69
70                 #region Constructors
71                 
72                 public OdbcParameter ()
73                 {
74                         _cbLengthInd = new NativeBuffer ();
75                         ParameterName = String.Empty;
76                         IsNullable = true;
77                         SourceColumn = String.Empty;
78                         Direction = ParameterDirection.Input;
79                         _typeMap = OdbcTypeConverter.GetTypeMap (OdbcType.VarChar);
80                 }
81
82                 public OdbcParameter (string name, object value) 
83                         : this ()
84                 {
85                         this.ParameterName = name;
86                         Value = value;
87                         _typeMap = OdbcTypeConverter.InferFromValue (value);
88                         if (value != null && !value.GetType ().IsValueType) {
89                                 Type type = value.GetType ();
90                                 if (type.IsArray)
91                                         Size = type.GetElementType () == typeof (byte) ?
92                                                 ((Array) value).Length : 0;
93                                 else
94                                         Size = value.ToString ().Length;
95                         }
96                 }
97
98                 public OdbcParameter (string name, OdbcType odbcType) 
99                         : this ()
100                 {
101                         this.ParameterName = name;
102                         _typeMap = (OdbcTypeMap) OdbcTypeConverter.GetTypeMap (odbcType);
103                 }
104
105                 public OdbcParameter (string name, OdbcType odbcType, int size)
106                         : this (name, odbcType)
107                 {
108                         this.Size = size;
109                 }
110
111                 public OdbcParameter (string name, OdbcType odbcType, int size, string srcColumn)
112                         : this (name, odbcType, size)
113                 {
114                         this.SourceColumn = srcColumn;
115                 }
116
117                 [EditorBrowsable (EditorBrowsableState.Advanced)]
118                 public OdbcParameter(string name, OdbcType odbcType, int size, 
119                                      ParameterDirection direction, bool isNullable, 
120                                      byte precision, byte scale, string srcColumn, 
121                                      DataRowVersion srcVersion, object value)
122                         : this (name, odbcType, size, srcColumn)
123                 {
124                         this.Direction = direction;
125                         this.IsNullable = isNullable;
126                         this.SourceVersion = srcVersion;
127                 }
128
129                 #endregion
130
131                 #region Properties
132
133                 // Used to ensure that only one collection can contain this
134                 // parameter
135                 internal OdbcParameterCollection Container {
136                         get { return container; }
137                         set { container = value; }
138                 }
139                 
140                 [BrowsableAttribute (false)]
141                 [OdbcDescriptionAttribute ("The parameter generic type")]
142                 [RefreshPropertiesAttribute (RefreshProperties.All)]
143                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
144                 [OdbcCategory ("Data")]
145                 public 
146 #if NET_2_0
147                 override 
148 #endif
149                 DbType DbType {
150                         get { return _typeMap.DbType; }
151                         set { 
152                                 if (value == _typeMap.DbType)
153                                         return;
154                                 
155                                 _typeMap = OdbcTypeConverter.GetTypeMap (value);
156                         }
157                 }
158                 
159                 [OdbcCategory ("Data")]
160                 [OdbcDescriptionAttribute ("Input, output, or bidirectional parameter")]  
161                 [DefaultValue (ParameterDirection.Input)]
162                 public 
163 #if NET_2_0
164                 override 
165 #endif
166                 ParameterDirection Direction {
167                         get { return direction; }
168                         set { direction = value; }
169                 }
170                 
171                 [BrowsableAttribute (false)]
172                 [OdbcDescriptionAttribute ("A design-time property used for strongly typed code generation")]
173                 [DesignOnlyAttribute (true)]
174                 [EditorBrowsableAttribute (EditorBrowsableState.Advanced)]
175                 [DefaultValue (false)]
176                 public 
177 #if NET_2_0
178                 override 
179 #endif
180                 bool IsNullable {
181                         get { return isNullable; }
182                         set { isNullable = value; }
183                 }
184
185
186                 [DefaultValue (OdbcType.NChar)]
187                 [OdbcDescriptionAttribute ("The parameter native type")]
188                 [RefreshPropertiesAttribute (RefreshProperties.All)]
189                 [OdbcCategory ("Data")]
190                 public OdbcType OdbcType {
191                         get { return _typeMap.OdbcType; }
192                         set {
193                                 if (value == OdbcType)
194                                         return;
195
196                                 _typeMap = OdbcTypeConverter.GetTypeMap (value);
197                         }
198                 }
199                 
200                 [OdbcDescription ("DataParameter_ParameterName")]
201                 [DefaultValue ("")]     
202                 public 
203 #if NET_2_0
204                 override 
205 #endif
206                 string ParameterName {
207                         get { return name; }
208                         set { name = value; }
209                 }
210
211                 [OdbcDescription ("DbDataParameter_Precision")]
212                 [OdbcCategory ("DataCategory_Data")]
213                 [DefaultValue (0)]
214                 public byte Precision {
215                         get { return _precision; }
216                         set { _precision = value; }
217                 }
218                 
219                 [OdbcDescription ("DbDataParameter_Scale")]
220                 [OdbcCategory ("DataCategory_Data")]
221                 [DefaultValue (0)]
222                 public byte Scale {
223                         get { return _scale; }
224                         set { _scale = value; }
225                 }
226                 
227                 [OdbcDescription ("DbDataParameter_Size")]
228                 [OdbcCategory ("DataCategory_Data")]
229                 [DefaultValue (0)]
230                 public 
231 #if NET_2_0
232                 override 
233 #endif
234                 int Size {
235                         get { return size; }
236                         set { size = value; }
237                 }
238
239                 [OdbcDescription ("DataParameter_SourceColumn")]
240                 [OdbcCategory ("DataCategory_Data")]
241                 [DefaultValue ("")]
242                 public 
243 #if NET_2_0
244                 override 
245 #endif
246                 string SourceColumn {
247                         get { return sourceColumn; }
248                         set { sourceColumn = value; }
249                 }
250                 
251                 [OdbcDescription ("DataParameter_SourceVersion")]
252                 [OdbcCategory ("DataCategory_Data")]
253                 [DefaultValue ("Current")]                      
254                 public 
255 #if NET_2_0
256                 override 
257 #endif
258                 DataRowVersion SourceVersion {
259                         get { return sourceVersion; }
260                         set { sourceVersion = value; }
261                 }
262
263                 [TypeConverter (typeof(StringConverter))]
264                 [OdbcDescription ("DataParameter_Value")]
265                 [OdbcCategory ("DataCategory_Data")]
266                 [DefaultValue (null)]           
267                 public 
268 #if NET_2_0
269                 override 
270 #endif
271                 object Value {
272                         get { 
273                                 return _value;
274                         }
275                         set { 
276                                 _value = value;
277                         }
278                 }
279
280                 #endregion // Properties
281
282                 #region Methods
283
284                 internal void Bind(IntPtr hstmt, int ParamNum) {
285                         OdbcReturn ret;
286                         
287                         // Convert System.Data.ParameterDirection into odbc enum
288                         OdbcInputOutputDirection paramdir = libodbc.ConvertParameterDirection(this.Direction);
289
290                         _cbLengthInd.EnsureAlloc (Marshal.SizeOf (typeof (int)));
291                         Marshal.WriteInt32 (_cbLengthInd, GetNativeSize ());
292                         AllocateBuffer ();
293                         ret = libodbc.SQLBindParameter(hstmt, (ushort) ParamNum, (short) paramdir,
294                                                        _typeMap.NativeType, _typeMap.SqlType, Convert.ToUInt32(Size),
295                                                        0, (IntPtr) _nativeBuffer, 0, _cbLengthInd);
296
297                         // Check for error condition
298                         if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
299                                 throw new OdbcException(new OdbcError("SQLBindParam", OdbcHandleType.Stmt, hstmt));
300                 }
301
302                 [MonoTODO]
303                 object ICloneable.Clone ()
304                 {
305                         throw new NotImplementedException ();
306                 }
307
308                 public override string ToString ()
309                 {
310                         return ParameterName;
311                 }
312                 
313                 private int GetNativeSize ()
314                 {
315                         TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
316                         Encoding enc = Encoding.GetEncoding (ti.ANSICodePage);
317
318                         switch (_typeMap.OdbcType) {
319                         case OdbcType.Binary:
320                                 if (Value.GetType ().IsArray &&
321                                     Value.GetType ().GetElementType () == typeof (byte))
322                                         return ( (Array) Value).Length;
323                                 else
324                                         return Value.ToString ().Length;
325                         case OdbcType.Bit:
326                                 return Marshal.SizeOf (typeof  (byte));
327                         case OdbcType.Double:
328                                 return Marshal.SizeOf (typeof (double));
329                         case OdbcType.Real:
330                                 return Marshal.SizeOf (typeof (float));
331                         case OdbcType.Int:
332                                 return Marshal.SizeOf (typeof (int));
333                         case OdbcType.BigInt:
334                                 return Marshal.SizeOf (typeof  (long));
335                         case OdbcType.Decimal:
336                         case OdbcType.Numeric:
337                                 return 19;
338                         case OdbcType.SmallInt:
339                                 return Marshal.SizeOf (typeof  (Int16));
340                         case OdbcType.TinyInt:
341                                 return Marshal.SizeOf (typeof  (byte));
342                         case OdbcType.Char:
343                         case OdbcType.Text:
344                         case OdbcType.VarChar:
345                                 return enc.GetByteCount (Convert.ToString (Value)) + 1;
346                         case OdbcType.NChar:
347                         case OdbcType.NText:
348                         case OdbcType.NVarChar:
349                                 // FIXME: Change to unicode
350                                 return enc.GetByteCount (Convert.ToString (Value)) + 1;
351                         case OdbcType.VarBinary:
352                         case OdbcType.Image:
353                                 if (Value.GetType ().IsArray &&
354                                     Value.GetType ().GetElementType () == typeof (byte))
355                                         return ( (Array) Value).Length;
356                                 throw new ArgumentException ("Unsupported Native Type!");
357                         case OdbcType.Date:
358                         case OdbcType.DateTime:
359                         case OdbcType.SmallDateTime:
360                         case OdbcType.Time:
361                         case OdbcType.Timestamp:
362                                 return 18;
363                         case OdbcType.UniqueIdentifier:
364                                 return Marshal.SizeOf (typeof (Guid));
365                         }
366
367                         if (Value.GetType ().IsArray &&
368                             Value.GetType ().GetElementType () == typeof (byte))
369                                 return ( (Array) Value).Length;
370                         
371                         return Value.ToString ().Length;
372                 }
373
374                 private void AllocateBuffer ()
375                 {
376                         int size = GetNativeSize ();
377
378                         if (_nativeBuffer.Size == size)
379                                 return;
380
381                         _nativeBuffer.AllocBuffer (size);
382                 }
383
384                 internal void CopyValue ()
385                 {
386                         if (_nativeBuffer.Handle == IntPtr.Zero)
387                                 return;
388
389                         DateTime dt;
390                         TextInfo ti = CultureInfo.InvariantCulture.TextInfo;
391                         Encoding enc = Encoding.GetEncoding (ti.ANSICodePage);
392                         byte [] nativeBytes, buffer;
393
394                         switch (_typeMap.OdbcType) {
395                         case OdbcType.Binary:
396                                 throw new NotImplementedException ();
397                         case OdbcType.Bit:
398                                 Marshal.WriteByte (_nativeBuffer, Convert.ToByte (Value));
399                                 return;
400                         case OdbcType.Double:
401                                 Marshal.StructureToPtr (Convert.ToDouble (Value), _nativeBuffer, false);
402                                 return;
403                         case OdbcType.Real:
404                                 Marshal.StructureToPtr (Convert.ToSingle (Value), _nativeBuffer, false);
405                                 return;
406                         case OdbcType.Int:
407                                 Marshal.WriteInt32 (_nativeBuffer, Convert.ToInt32 (Value));
408                                 return;
409                         case OdbcType.BigInt:
410                                 Marshal.WriteInt64 (_nativeBuffer, Convert.ToInt64 (Value));
411                                 return;
412                         case OdbcType.Decimal:
413                         case OdbcType.Numeric:
414                                 // for numeric, the buffer is a packed decimal struct.
415                                 // ref http://www.it-faq.pl/mskb/181/254.HTM
416                                 int [] bits = Decimal.GetBits (Convert.ToDecimal (Value));
417                                 buffer = new byte [19]; // ref sqltypes.h
418                                 buffer [0] = Precision;
419                                 buffer [1] = (byte) ((bits [3] & 0x00FF0000) >> 16); // scale
420                                 buffer [2] = (byte) ((bits [3] & 0x80000000) > 0 ? 2 : 1); //sign
421                                 Buffer.BlockCopy (bits, 0, buffer, 3, 12); // copy data
422                                 for (int j = 16; j < 19; j++) // pad with 0
423                                         buffer [j] = 0;
424                                 Marshal.Copy (buffer, 0, _nativeBuffer, 19); 
425                                 return; 
426                         case OdbcType.SmallInt:
427                                 Marshal.WriteInt16 (_nativeBuffer, Convert.ToInt16 (Value));
428                                 return;
429                         case OdbcType.TinyInt:
430                                 Marshal.WriteByte (_nativeBuffer, Convert.ToByte (Value));
431                                 return;
432                         case OdbcType.Char:
433                         case OdbcType.Text:
434                         case OdbcType.VarChar:
435                                 buffer = new byte [GetNativeSize ()];
436                                 nativeBytes = enc.GetBytes (Convert.ToString (Value));
437                                 Array.Copy (nativeBytes, 0, buffer, 0, nativeBytes.Length);
438                                 buffer [buffer.Length-1] = (byte) 0;
439                                 Marshal.Copy (buffer, 0, _nativeBuffer, buffer.Length);
440                                 Marshal.WriteInt32 (_cbLengthInd, -3);
441                                 return;
442                         case OdbcType.NChar:
443                         case OdbcType.NText:
444                         case OdbcType.NVarChar:
445                                 // FIXME : change to unicode
446                                 buffer = new byte [GetNativeSize ()];
447                                 nativeBytes = enc.GetBytes (Convert.ToString (Value));
448                                 Array.Copy (nativeBytes, 0, buffer, 0, nativeBytes.Length);
449                                 buffer [buffer.Length-1] = (byte) 0;
450                                 Marshal.Copy (buffer, 0, _nativeBuffer, buffer.Length);
451                                 Marshal.WriteInt32 (_cbLengthInd, -3);
452                                 return;
453                         case OdbcType.VarBinary:
454                         case OdbcType.Image:
455                                 if (Value.GetType ().IsArray &&
456                                     Value.GetType ().GetElementType () == typeof (byte)) {
457                                         Marshal.Copy ( (byte []) Value, 0, _nativeBuffer, ((byte []) Value).Length);
458                                 }else
459                                         throw new ArgumentException ("Unsupported Native Type!");
460                                 return;
461                         case OdbcType.Date:
462                                 dt = (DateTime) Value;
463                                 Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Year);
464                                 Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Month);
465                                 Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Day);
466                                 return;
467                         case OdbcType.Time:
468                                 dt = (DateTime) Value;
469                                 Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Hour);
470                                 Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Minute);
471                                 Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Second);
472                                 return;
473                         case OdbcType.SmallDateTime:
474                         case OdbcType.Timestamp:
475                         case OdbcType.DateTime:
476                                 dt = (DateTime) Value;
477                                 Marshal.WriteInt16 (_nativeBuffer, 0, (short) dt.Year);
478                                 Marshal.WriteInt16 (_nativeBuffer, 2, (short) dt.Month);
479                                 Marshal.WriteInt16 (_nativeBuffer, 4, (short) dt.Day);
480                                 Marshal.WriteInt16 (_nativeBuffer, 6, (short) dt.Hour);
481                                 Marshal.WriteInt16 (_nativeBuffer, 8, (short) dt.Minute);
482                                 Marshal.WriteInt16 (_nativeBuffer, 10, (short) dt.Second);
483                                 Marshal.WriteInt32 (_nativeBuffer, 12, (int) (dt.Ticks % 10000000) * 100);
484                                 return;
485                         case OdbcType.UniqueIdentifier:
486                                 throw new NotImplementedException ();
487                         }
488
489                         if (Value.GetType ().IsArray &&
490                             Value.GetType ().GetElementType () == typeof (byte)) {
491                                 Marshal.Copy ( (byte []) Value, 0, _nativeBuffer, ((byte []) Value).Length);
492                         }else
493                                 throw new ArgumentException ("Unsupported Native Type!");
494                 }
495
496 #if NET_2_0
497                 public override bool SourceColumnNullMapping {
498                         get {return false;}
499                         set {}
500                 }
501
502                 public override void ResetDbType () 
503                 {
504                         throw new NotImplementedException ();
505                 }
506 #endif
507
508                 #endregion
509         }
510 }