Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[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 Static 
41                 public const int maxVarCharCharacters =  2147483647; // According to MS, max size is 2GB, 1 Byte Characters
42                 public const int maxNVarCharCharacters = 1073741823; // According to MS, max size is 2GB, 2 Byte Characters
43                 #endregion
44
45                 #region Fields
46
47                 TdsParameterDirection direction = TdsParameterDirection.Input;
48                 byte precision;
49                 byte scale;
50                 int size;
51                 string typeName;
52                 string name;
53                 bool isSizeSet = false;
54                 bool isNullable;
55                 object value;
56                 bool isVariableSizeType;
57                 FrameworkValueGetter frameworkValueGetter;
58                 object rawValue;
59                 bool isUpdated;
60
61                 #endregion // Fields
62
63                 public TdsMetaParameter (string name, object value)
64                         : this (name, String.Empty, value)
65                 {
66                 }
67
68                 public TdsMetaParameter (string name, FrameworkValueGetter valueGetter)
69                         : this (name, String.Empty, null)
70                 {
71                         frameworkValueGetter = valueGetter;
72                 }
73
74                 public TdsMetaParameter (string name, string typeName, object value)
75                 {
76                         ParameterName = name;
77                         Value = value;
78                         TypeName = typeName;
79                         IsNullable = false;
80                 }
81
82                 public TdsMetaParameter (string name, int size, bool isNullable, byte precision, byte scale, object value)
83                 {
84                         ParameterName = name;
85                         Size = size;
86                         IsNullable = isNullable;
87                         Precision = precision;
88                         Scale = scale;
89                         Value = value;
90                 }
91
92                 public TdsMetaParameter (string name, int size, bool isNullable, byte precision, byte scale, FrameworkValueGetter valueGetter)
93                 {
94                         ParameterName = name;
95                         Size = size;
96                         IsNullable = isNullable;
97                         Precision = precision;
98                         Scale = scale;
99                         frameworkValueGetter = valueGetter;
100                 }
101
102                 #region Properties
103
104                 public TdsParameterDirection Direction {
105                         get { return direction; }
106                         set { direction = value; }
107                 }
108
109                 public string TypeName {
110                         get { return typeName; }
111                         set { typeName = value; }
112                 }
113
114                 public string ParameterName {
115                         get { return name; }
116                         set { name = value; }
117                 }
118
119                 public bool IsNullable {
120                         get { return isNullable; }
121                         set { isNullable = value; }
122                 }
123
124                 public object Value {
125                         get {
126                                 if (frameworkValueGetter != null) {
127                                         object newValue = frameworkValueGetter (rawValue, ref isUpdated);
128                                         if (isUpdated)
129                                                 value = newValue;
130                                 }
131
132                                 if (isUpdated) {
133                                         value = ResizeValue (value);
134                                         isUpdated = false;
135                                 }
136                                 return value;
137                         }
138                         set {
139                                 rawValue = this.value = value;
140                                 isUpdated = true;
141                         }
142                 }
143
144                 public object RawValue {
145                         get { return rawValue; }
146                         set { Value = value; }
147                 }
148
149                 public byte Precision {
150                         get { return precision; }
151                         set { precision = value; }
152                 }
153
154                 public byte Scale {
155                         get { 
156                                 if (TypeName == "decimal" || TypeName == "numeric") {
157                                         if (scale == 0 && !Convert.IsDBNull(Value)) {
158                                                 int[] arr = Decimal.GetBits (
159                                                                 Convert.ToDecimal(Value));
160                                                 scale = (byte)((arr[3]>>16) & (int)0xFF);
161                                         }
162                                 }
163                                 return scale;
164                         }
165                         set { scale = value; }
166                 }
167
168                 public int Size {
169                         get { return GetSize (); }
170                         set {
171                                 size = value;
172                                 isUpdated = true;
173                                 isSizeSet = true;
174                         }
175                 }
176
177                 public bool IsVariableSizeType
178                 {
179                         get { return isVariableSizeType; }
180                         set { isVariableSizeType = value; }
181                 }
182
183                 public bool IsVarNVarCharMax
184                 {
185                         get { return (TypeName == "ntext" && size >= maxNVarCharCharacters); }
186                 }
187
188                 public bool IsVarCharMax
189                 {
190                         get { return (TypeName == "text" && size >= maxVarCharCharacters); }
191                 }
192
193                 public bool IsAnyVarCharMax
194                 {
195                         get { return IsVarNVarCharMax || IsVarCharMax; }
196                 }
197
198                 public bool IsNonUnicodeText
199                 {
200                         get {
201                                 TdsColumnType colType = GetMetaType();
202                                 return (colType == TdsColumnType.VarChar ||
203                                         colType == TdsColumnType.BigVarChar ||
204                                         colType == TdsColumnType.Text ||
205                                         colType == TdsColumnType.Char ||
206                                         colType == TdsColumnType.BigChar);
207                         }
208                 }
209
210                 public bool IsMoneyType
211                 {
212                         get {
213                                 TdsColumnType colType = GetMetaType();
214                                 return (colType == TdsColumnType.Money ||
215                                         colType == TdsColumnType.MoneyN ||
216                                         colType == TdsColumnType.Money4 ||
217                                         colType == TdsColumnType.SmallMoney);
218                         }
219                 }
220
221                 public bool IsDateTimeType
222                 {
223                         get {
224                                 TdsColumnType colType = GetMetaType();
225                                 return (colType == TdsColumnType.DateTime ||
226                                         colType == TdsColumnType.DateTime4 ||
227                                         colType == TdsColumnType.DateTimeN);
228                         }
229                 }
230
231                 public bool IsTextType
232                 {
233                         get {
234                                 TdsColumnType colType = GetMetaType();
235                                 return (colType == TdsColumnType.VarChar ||
236                                         colType == TdsColumnType.BigVarChar ||
237                                         colType == TdsColumnType.BigChar ||
238                                         colType == TdsColumnType.Char ||
239                                         colType == TdsColumnType.BigNVarChar || 
240                                         colType == TdsColumnType.NChar ||
241                                         colType == TdsColumnType.Text ||
242                                         colType == TdsColumnType.NText);
243                         }
244                 }
245
246                 public bool IsDecimalType
247                 {
248                         get {
249                                 TdsColumnType colType = GetMetaType();
250                                 return (colType == TdsColumnType.Decimal ||
251                                         colType == TdsColumnType.Numeric);
252                         }
253                 }
254
255                 #endregion // Properties
256
257                 #region Methods
258
259                 object ResizeValue (object newValue)
260                 {
261                         if (newValue == DBNull.Value || newValue == null)
262                                 return newValue;
263
264                         if (!isSizeSet || size <= 0)
265                                 return newValue;
266
267                         // if size is set, truncate the value to specified size
268                         string text = newValue as string;
269                         if (text != null) {
270                                 if (TypeName == "nvarchar" || 
271                                     TypeName == "nchar" ||
272                                     TypeName == "xml") {
273                                         if (text.Length > size)
274                                                 return text.Substring (0, size);
275                                 }
276                         } else if (newValue.GetType () == typeof (byte [])) {
277                                 byte [] buffer = (byte []) newValue;
278                                 if (buffer.Length > size) {
279                                         byte [] tmpVal = new byte [size];
280                                         Array.Copy (buffer, tmpVal, size);
281                                         return tmpVal;
282                                 }
283                         }
284                         return newValue;
285                 }
286
287                 internal string Prepare ()
288                 {
289                         string typeName = TypeName;
290                         
291                         if (typeName == "varbinary") {
292                                 int size = Size;
293                                 if (size <= 0) {
294                                         size = GetActualSize ();
295                                 }
296                                 
297                                 if (size > 8000) {
298                                         typeName = "varbinary(max)";
299                                 }
300                         }
301                         
302                         string includeAt = "@";
303                         if (ParameterName [0] == '@')
304                                 includeAt = "";
305                         StringBuilder result = new StringBuilder (String.Format ("{0}{1} {2}", includeAt, ParameterName, typeName));
306                         switch (typeName) {
307                         case "decimal":
308                         case "numeric":
309                                 // msdotnet sends a default precision of 29
310                                 result.Append (String.Format ("({0},{1})",
311                                          (Precision == (byte)0 ? (byte)38 : Precision), Scale));
312                                 break;
313                         case "varchar":
314                         case "varbinary":
315                                 //A size of 0 is not allowed in declarations.
316                                 int size = Size;
317                                 if (size <= 0) {
318                                         size = GetActualSize ();
319                                         if (size <= 0)
320                                                 size = 1;
321                                 }
322                                 result.Append (size > 8000 ? "(max)" : String.Format ("({0})", size));
323                                 break;
324                         case "nvarchar":
325                         case "xml":
326                                 int paramSize = Size < 0 ? GetActualSize () / 2 : Size;
327                                 result.Append (paramSize > 0 ? (paramSize > 4000 ? "(max)" : String.Format ("({0})", paramSize)) : "(4000)");
328                                 break;
329                         case "char":
330                         case "nchar":
331                         case "binary":
332                                 if (isSizeSet && Size > 0)
333                                         result.Append (String.Format ("({0})", Size));
334                                 break;
335                         }
336                         return result.ToString ();
337                 }
338
339                 internal int GetActualSize ()
340                 {
341                         if (Value == DBNull.Value || Value == null)
342                                 return 0;
343
344                         switch (Value.GetType ().ToString ()) {
345                         case "System.String":
346                                 int len = ((string)value).Length;
347                                 if (TypeName == "nvarchar" || TypeName == "nchar" 
348                                     || TypeName == "ntext"
349                                     || TypeName == "xml")
350                                         len *= 2;
351                                 return len ;    
352                         case "System.Byte[]":
353                                 return ((byte[]) value).Length;
354                         }
355                         return GetSize ();
356                 }
357
358                 private int GetSize ()
359                 {
360                         switch (TypeName) {
361                         case "decimal":
362                                 return 17;
363                         case "uniqueidentifier":
364                                 return 16;
365                         case "bigint":
366                         case "datetime":
367                         case "float":
368                         case "money":
369                                 return 8;
370                         case "int":
371                         case "real":
372                         case "smalldatetime":
373                         case "smallmoney":
374                                 return 4;
375                         case "smallint":
376                                 return 2;
377                         case "tinyint":
378                         case "bit":
379                                 return 1;
380                         /*
381                         case "nvarchar" :
382                         */
383                         case "nchar" :
384                         case "ntext" :
385                                 return size*2 ;
386                         }
387                         return size;
388                 }
389
390                 internal byte[] GetBytes ()
391                 {
392                         byte[] result = {};
393                         if (Value == DBNull.Value || Value == null)
394                                 return result;
395
396                         switch (TypeName)
397                         {
398                                 case "nvarchar" :
399                                 case "nchar" :
400                                 case "ntext" :
401                                 case "xml" :
402                                         return Encoding.Unicode.GetBytes ((string)Value);
403                                 case "varchar" :
404                                 case "char" :
405                                 case "text" :
406                                         return Encoding.Default.GetBytes ((string)Value);
407                                 default :
408                                         return ((byte[]) Value);
409                         }
410                 }
411
412                 internal TdsColumnType GetMetaType ()
413                 {
414                         switch (TypeName) {
415                         case "binary":
416                                 return TdsColumnType.BigBinary;
417                         case "bit":
418                                 if (IsNullable)
419                                         return TdsColumnType.BitN;
420                                 return TdsColumnType.Bit;
421                         case "bigint":
422                                 if (IsNullable)
423                                         return TdsColumnType.IntN ;
424                                 return TdsColumnType.BigInt;
425                         case "char":
426                                 return TdsColumnType.BigChar;
427                         case "money":
428                                 if (IsNullable)
429                                         return TdsColumnType.MoneyN;
430                                 return TdsColumnType.Money;
431                         case "smallmoney":
432                                 if (IsNullable)
433                                         return TdsColumnType.MoneyN ;
434                                 return TdsColumnType.SmallMoney;
435                         case "decimal":
436                                 return TdsColumnType.Decimal;
437                         case "datetime":
438                                 if (IsNullable)
439                                         return TdsColumnType.DateTimeN;
440                                 return TdsColumnType.DateTime;
441                         case "smalldatetime":
442                                 if (IsNullable)
443                                         return TdsColumnType.DateTimeN;
444                                 return TdsColumnType.DateTime4;
445                         case "float":
446                                 if (IsNullable)
447                                         return TdsColumnType.FloatN ;
448                                 return TdsColumnType.Float8;
449                         case "image":
450                                 return TdsColumnType.Image;
451                         case "int":
452                                 if (IsNullable)
453                                         return TdsColumnType.IntN;
454                                 return TdsColumnType.Int4;
455                         case "numeric":
456                                 return TdsColumnType.Numeric;
457                         case "nchar":
458                                 return TdsColumnType.NChar;
459                         case "ntext":
460                                 return TdsColumnType.NText;
461                         case "xml":
462                         case "nvarchar":
463                                 return TdsColumnType.BigNVarChar;
464                         case "real":
465                                 if (IsNullable)
466                                         return TdsColumnType.FloatN ;
467                                 return TdsColumnType.Real;
468                         case "smallint":
469                                 if (IsNullable)
470                                         return TdsColumnType.IntN;
471                                 return TdsColumnType.Int2;
472                         case "text":
473                                 return TdsColumnType.Text;
474                         case "tinyint":
475                                 if (IsNullable)
476                                         return TdsColumnType.IntN;
477                                 return TdsColumnType.Int1;
478                         case "uniqueidentifier":
479                                 return TdsColumnType.UniqueIdentifier;
480                         case "varbinary":
481                                 return TdsColumnType.BigVarBinary;
482                         case "varchar":
483                                 return TdsColumnType.BigVarChar;
484                         default:
485                                 throw new NotSupportedException ("Unknown Type : " + TypeName);
486                         }
487                 }
488
489                 public void CalculateIsVariableType()
490                 {
491                         switch (GetMetaType ()) {
492                                 case TdsColumnType.UniqueIdentifier:
493                                 case TdsColumnType.BigVarChar:
494                                 case TdsColumnType.BigVarBinary:
495                                 case TdsColumnType.IntN:
496                                 case TdsColumnType.Text:
497                                 case TdsColumnType.FloatN:
498                                 case TdsColumnType.BigNVarChar:
499                                 case TdsColumnType.NText:
500                                 case TdsColumnType.Image:
501                                 case TdsColumnType.Decimal:
502                                 case TdsColumnType.BigBinary:
503                                 case TdsColumnType.DateTimeN:
504                                 case TdsColumnType.MoneyN:
505                                 case TdsColumnType.BitN:
506                                 case TdsColumnType.Char:
507                                 case TdsColumnType.BigChar:
508                                 case TdsColumnType.NChar:
509                                         IsVariableSizeType = true;
510                                         break;
511                                 default:
512                                         IsVariableSizeType = false;
513                                 break;
514                         }
515                 }
516
517                 public void Validate (int index)
518                 {
519                         if ((this.direction == TdsParameterDirection.InputOutput || this.direction == TdsParameterDirection.Output) &&
520                                  this.isVariableSizeType && (Value == DBNull.Value || Value == null) && Size == 0
521                                 ) 
522                         {
523                                 throw new InvalidOperationException (String.Format ("{0}[{1}]: the Size property should " +
524                                                                                                 "not be of size 0",
525                                                                                                 this.typeName,
526                                                                                                 index));
527                         }
528                 }
529
530                 #endregion // Methods
531         }
532 }