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