2010-06-25 Alan McGovern <amcgovern@novell.com>
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds / TdsMetaParameter.cs
1 //
2 // Mono.Data.Tds.TdsMetaParameter.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2002
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using Mono.Data.Tds.Protocol;
32 using System;
33 using System.Text;
34
35 namespace Mono.Data.Tds {
36         public delegate object FrameworkValueGetter (object rawValue, ref bool updated);
37
38         public class TdsMetaParameter
39         {
40                 #region Fields
41
42                 TdsParameterDirection direction = TdsParameterDirection.Input;
43                 byte precision;
44                 byte scale;
45                 int size;
46                 string typeName;
47                 string name;
48                 bool isSizeSet = false;
49                 bool isNullable;
50                 object value;
51                 bool isVariableSizeType;
52                 FrameworkValueGetter frameworkValueGetter;
53                 object rawValue;
54                 bool isUpdated;
55
56                 #endregion // Fields
57
58                 public TdsMetaParameter (string name, object value)
59                         : this (name, String.Empty, value)
60                 {
61                 }
62
63                 public TdsMetaParameter (string name, FrameworkValueGetter valueGetter)
64                         : this (name, String.Empty, null)
65                 {
66                         frameworkValueGetter = valueGetter;
67                 }
68
69                 public TdsMetaParameter (string name, string typeName, object value)
70                 {
71                         ParameterName = name;
72                         Value = value;
73                         TypeName = typeName;
74                         IsNullable = false;
75                 }
76
77                 public TdsMetaParameter (string name, int size, bool isNullable, byte precision, byte scale, object value)
78                 {
79                         ParameterName = name;
80                         Size = size;
81                         IsNullable = isNullable;
82                         Precision = precision;
83                         Scale = scale;
84                         Value = value;
85                 }
86
87                 public TdsMetaParameter (string name, int size, bool isNullable, byte precision, byte scale, FrameworkValueGetter valueGetter)
88                 {
89                         ParameterName = name;
90                         Size = size;
91                         IsNullable = isNullable;
92                         Precision = precision;
93                         Scale = scale;
94                         frameworkValueGetter = valueGetter;
95                 }
96
97                 #region Properties
98
99                 public TdsParameterDirection Direction {
100                         get { return direction; }
101                         set { direction = value; }
102                 }
103
104                 public string TypeName {
105                         get { return typeName; }
106                         set { typeName = value; }
107                 }
108
109                 public string ParameterName {
110                         get { return name; }
111                         set { name = value; }
112                 }
113
114                 public bool IsNullable {
115                         get { return isNullable; }
116                         set { isNullable = value; }
117                 }
118
119                 public object Value {
120                         get {
121                                 if (frameworkValueGetter != null) {
122                                         object newValue = frameworkValueGetter (rawValue, ref isUpdated);
123                                         if (isUpdated)
124                                                 value = newValue;
125                                 }
126
127                                 if (isUpdated) {
128                                         value = ResizeValue (value);
129                                         isUpdated = false;
130                                 }
131                                 return value;
132                         }
133                         set {
134                                 rawValue = this.value = value;
135                                 isUpdated = true;
136                         }
137                 }
138
139                 public object RawValue {
140                         get { return rawValue; }
141                         set { Value = value; }
142                 }
143
144                 public byte Precision {
145                         get { return precision; }
146                         set { precision = value; }
147                 }
148
149                 public byte Scale {
150                         get { 
151                                 if (TypeName == "decimal" || TypeName == "numeric") {
152                                         if (scale == 0 && !Convert.IsDBNull(Value)) {
153                                                 int[] arr = Decimal.GetBits (
154                                                                 Convert.ToDecimal(Value));
155                                                 scale = (byte)((arr[3]>>16) & (int)0xFF);
156                                         }
157                                 }
158                                 return scale;
159                         }
160                         set { scale = value; }
161                 }
162
163                 public int Size {
164                         get { return GetSize (); }
165                         set {
166                                 size = value;
167                                 isUpdated = true;
168                                 isSizeSet = true;
169                         }
170                 }
171
172                 public bool IsVariableSizeType
173                 {
174                         get { return isVariableSizeType; }
175                         set { isVariableSizeType = value; }
176                 }
177
178                 #endregion // Properties
179
180                 #region Methods
181
182                 object ResizeValue (object newValue)
183                 {
184                         if (newValue == DBNull.Value || newValue == null)
185                                 return newValue;
186
187                         if (!isSizeSet || size <= 0)
188                                 return newValue;
189
190                         // if size is set, truncate the value to specified size
191                         string text = newValue as string;
192                         if (text != null) {
193                                 if (TypeName == "nvarchar" || 
194                                     TypeName == "nchar" ||
195                                     TypeName == "xml") {
196                                         if (text.Length > size)
197                                                 return text.Substring (0, size);
198                                 }
199                         } else if (newValue.GetType () == typeof (byte [])) {
200                                 byte [] buffer = (byte []) newValue;
201                                 if (buffer.Length > size) {
202                                         byte [] tmpVal = new byte [size];
203                                         Array.Copy (buffer, tmpVal, size);
204                                         return tmpVal;
205                                 }
206                         }
207                         return newValue;
208                 }
209
210                 internal string Prepare ()
211                 {
212                         string typeName = TypeName;
213                         
214                         if (typeName == "varbinary") {
215                                 int size = Size;
216                                 if (size <= 0) {
217                                         size = GetActualSize ();
218                                 }
219                                 
220                                 if (size > 8000) {
221                                         typeName = "image";
222                                 }
223                         }
224                         
225                         string includeAt = "@";
226                         if (ParameterName [0] == '@')
227                                 includeAt = "";
228                         StringBuilder result = new StringBuilder (String.Format ("{0}{1} {2}", includeAt, ParameterName, typeName));
229                         switch (typeName) {
230                         case "decimal":
231                         case "numeric":
232                                 // msdotnet sends a default precision of 29
233                                 result.Append (String.Format ("({0},{1})",
234                                          (Precision == (byte)0 ? (byte)29 : Precision), Scale));
235                                 break;
236                         case "varchar":
237                         case "varbinary":
238                                 //A size of 0 is not allowed in declarations.
239                                 int size = Size;
240                                 if (size <= 0) {
241                                         size = GetActualSize ();
242                                         if (size <= 0)
243                                                 size = 1;
244                                 }
245                                 result.Append (size > 8000 ? "(max)" : String.Format ("({0})", size));
246                                 break;
247                         case "nvarchar":
248                         case "xml":
249                                 result.Append (Size > 0 ? (Size > 8000 ? "(max)" : String.Format ("({0})", Size)) : "(4000)");
250                                 break;
251                         case "char":
252                         case "nchar":
253                         case "binary":
254                                 if (isSizeSet && Size > 0)
255                                         result.Append (String.Format ("({0})", Size));
256                                 break;
257                         }
258                         return result.ToString ();
259                 }
260
261                 internal int GetActualSize ()
262                 {
263                         if (Value == DBNull.Value || Value == null)
264                                 return 0;
265
266                         switch (Value.GetType ().ToString ()) {
267                         case "System.String":
268                                 int len = ((string)value).Length;
269                                 if (TypeName == "nvarchar" || TypeName == "nchar" 
270                                     || TypeName == "ntext"
271                                     || TypeName == "xml")
272                                         len *= 2;
273                                 return len ;    
274                         case "System.Byte[]":
275                                 return ((byte[]) value).Length;
276                         }
277                         return GetSize ();
278                 }
279
280                 private int GetSize ()
281                 {
282                         switch (TypeName) {
283                         case "decimal":
284                                 return 17;
285                         case "uniqueidentifier":
286                                 return 16;
287                         case "bigint":
288                         case "datetime":
289                         case "float":
290                         case "money":
291                                 return 8;
292                         case "int":
293                         case "real":
294                         case "smalldatetime":
295                         case "smallmoney":
296                                 return 4;
297                         case "smallint":
298                                 return 2;
299                         case "tinyint":
300                         case "bit":
301                                 return 1;
302                         /*
303                         case "nvarchar" :
304                         */
305                         case "nchar" :
306                         case "ntext" :
307                                 return size*2 ;
308                         }
309                         return size;
310                 }
311
312                 internal byte[] GetBytes ()
313                 {
314                         byte[] result = {};
315                         if (Value == DBNull.Value || Value == null)
316                                 return result;
317
318                         switch (TypeName)
319                         {
320                                 case "nvarchar" :
321                                 case "nchar" :
322                                 case "ntext" :
323                                 case "xml" :
324                                         return Encoding.Unicode.GetBytes ((string)Value);
325                                 case "varchar" :
326                                 case "char" :
327                                 case "text" :
328                                         return Encoding.Default.GetBytes ((string)Value);
329                                 default :
330                                         return ((byte[]) Value);
331                         }
332                 }
333
334                 internal TdsColumnType GetMetaType ()
335                 {
336                         switch (TypeName) {
337                         case "binary":
338                                 return TdsColumnType.BigBinary;
339                         case "bit":
340                                 if (IsNullable)
341                                         return TdsColumnType.BitN;
342                                 return TdsColumnType.Bit;
343                         case "bigint":
344                                 if (IsNullable)
345                                         return TdsColumnType.IntN ;
346                                 return TdsColumnType.BigInt;
347                         case "char":
348                                 return TdsColumnType.Char;
349                         case "money":
350                                 if (IsNullable)
351                                         return TdsColumnType.MoneyN;
352                                 return TdsColumnType.Money;
353                         case "smallmoney":
354                                 if (IsNullable)
355                                         return TdsColumnType.MoneyN ;
356                                 return TdsColumnType.Money4;
357                         case "decimal":
358                                 return TdsColumnType.Decimal;
359                         case "datetime":
360                                 if (IsNullable)
361                                         return TdsColumnType.DateTimeN;
362                                 return TdsColumnType.DateTime;
363                         case "smalldatetime":
364                                 if (IsNullable)
365                                         return TdsColumnType.DateTimeN;
366                                 return TdsColumnType.DateTime4;
367                         case "float":
368                                 if (IsNullable)
369                                         return TdsColumnType.FloatN ;
370                                 return TdsColumnType.Float8;
371                         case "image":
372                                 return TdsColumnType.Image;
373                         case "int":
374                                 if (IsNullable)
375                                         return TdsColumnType.IntN;
376                                 return TdsColumnType.Int4;
377                         case "numeric":
378                                 return TdsColumnType.Numeric;
379                         case "nchar":
380                                 return TdsColumnType.NChar;
381                         case "ntext":
382                                 return TdsColumnType.NText;
383                         case "xml":
384                         case "nvarchar":
385                                 return TdsColumnType.BigNVarChar;
386                         case "real":
387                                 if (IsNullable)
388                                         return TdsColumnType.FloatN ;
389                                 return TdsColumnType.Real;
390                         case "smallint":
391                                 if (IsNullable)
392                                         return TdsColumnType.IntN;
393                                 return TdsColumnType.Int2;
394                         case "text":
395                                 return TdsColumnType.Text;
396                         case "tinyint":
397                                 if (IsNullable)
398                                         return TdsColumnType.IntN;
399                                 return TdsColumnType.Int1;
400                         case "uniqueidentifier":
401                                 return TdsColumnType.UniqueIdentifier;
402                         case "varbinary":
403                                 return TdsColumnType.BigVarBinary;
404                         case "varchar":
405                                 return TdsColumnType.BigVarChar;
406                         default:
407                                 throw new NotSupportedException ("Unknown Type : " + TypeName);
408                         }
409                 }
410
411                 public void Validate (int index)
412                 {
413                         if ((this.direction == TdsParameterDirection.InputOutput || this.direction == TdsParameterDirection.Output) &&
414                                  this.isVariableSizeType && (Value == DBNull.Value || Value == null) && Size == 0
415                                 ) 
416                         {
417                                 throw new InvalidOperationException (String.Format ("{0}[{1}]: the Size property should " +
418                                                                                                 "not be of size 0",
419                                                                                                 this.typeName,
420                                                                                                 index));
421                         }
422                 }
423
424                 #endregion // Methods
425         }
426 }