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