Bug 15572. Lookup KnownTypeCollection element types in MSSimpleNamespace
[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 = "varbinary(max)";
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)38 : 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                                 int paramSize = Size < 0 ? GetActualSize () / 2 : Size;
250                                 result.Append (paramSize > 0 ? (paramSize > 4000 ? "(max)" : String.Format ("({0})", paramSize)) : "(4000)");
251                                 break;
252                         case "char":
253                         case "nchar":
254                         case "binary":
255                                 if (isSizeSet && Size > 0)
256                                         result.Append (String.Format ("({0})", Size));
257                                 break;
258                         }
259                         return result.ToString ();
260                 }
261
262                 internal int GetActualSize ()
263                 {
264                         if (Value == DBNull.Value || Value == null)
265                                 return 0;
266
267                         switch (Value.GetType ().ToString ()) {
268                         case "System.String":
269                                 int len = ((string)value).Length;
270                                 if (TypeName == "nvarchar" || TypeName == "nchar" 
271                                     || TypeName == "ntext"
272                                     || TypeName == "xml")
273                                         len *= 2;
274                                 return len ;    
275                         case "System.Byte[]":
276                                 return ((byte[]) value).Length;
277                         }
278                         return GetSize ();
279                 }
280
281                 private int GetSize ()
282                 {
283                         switch (TypeName) {
284                         case "decimal":
285                                 return 17;
286                         case "uniqueidentifier":
287                                 return 16;
288                         case "bigint":
289                         case "datetime":
290                         case "float":
291                         case "money":
292                                 return 8;
293                         case "int":
294                         case "real":
295                         case "smalldatetime":
296                         case "smallmoney":
297                                 return 4;
298                         case "smallint":
299                                 return 2;
300                         case "tinyint":
301                         case "bit":
302                                 return 1;
303                         /*
304                         case "nvarchar" :
305                         */
306                         case "nchar" :
307                         case "ntext" :
308                                 return size*2 ;
309                         }
310                         return size;
311                 }
312
313                 internal byte[] GetBytes ()
314                 {
315                         byte[] result = {};
316                         if (Value == DBNull.Value || Value == null)
317                                 return result;
318
319                         switch (TypeName)
320                         {
321                                 case "nvarchar" :
322                                 case "nchar" :
323                                 case "ntext" :
324                                 case "xml" :
325                                         return Encoding.Unicode.GetBytes ((string)Value);
326                                 case "varchar" :
327                                 case "char" :
328                                 case "text" :
329                                         return Encoding.Default.GetBytes ((string)Value);
330                                 default :
331                                         return ((byte[]) Value);
332                         }
333                 }
334
335                 internal TdsColumnType GetMetaType ()
336                 {
337                         switch (TypeName) {
338                         case "binary":
339                                 return TdsColumnType.BigBinary;
340                         case "bit":
341                                 if (IsNullable)
342                                         return TdsColumnType.BitN;
343                                 return TdsColumnType.Bit;
344                         case "bigint":
345                                 if (IsNullable)
346                                         return TdsColumnType.IntN ;
347                                 return TdsColumnType.BigInt;
348                         case "char":
349                                 return TdsColumnType.Char;
350                         case "money":
351                                 if (IsNullable)
352                                         return TdsColumnType.MoneyN;
353                                 return TdsColumnType.Money;
354                         case "smallmoney":
355                                 if (IsNullable)
356                                         return TdsColumnType.MoneyN ;
357                                 return TdsColumnType.Money4;
358                         case "decimal":
359                                 return TdsColumnType.Decimal;
360                         case "datetime":
361                                 if (IsNullable)
362                                         return TdsColumnType.DateTimeN;
363                                 return TdsColumnType.DateTime;
364                         case "smalldatetime":
365                                 if (IsNullable)
366                                         return TdsColumnType.DateTimeN;
367                                 return TdsColumnType.DateTime4;
368                         case "float":
369                                 if (IsNullable)
370                                         return TdsColumnType.FloatN ;
371                                 return TdsColumnType.Float8;
372                         case "image":
373                                 return TdsColumnType.Image;
374                         case "int":
375                                 if (IsNullable)
376                                         return TdsColumnType.IntN;
377                                 return TdsColumnType.Int4;
378                         case "numeric":
379                                 return TdsColumnType.Numeric;
380                         case "nchar":
381                                 return TdsColumnType.NChar;
382                         case "ntext":
383                                 return TdsColumnType.NText;
384                         case "xml":
385                         case "nvarchar":
386                                 return TdsColumnType.BigNVarChar;
387                         case "real":
388                                 if (IsNullable)
389                                         return TdsColumnType.FloatN ;
390                                 return TdsColumnType.Real;
391                         case "smallint":
392                                 if (IsNullable)
393                                         return TdsColumnType.IntN;
394                                 return TdsColumnType.Int2;
395                         case "text":
396                                 return TdsColumnType.Text;
397                         case "tinyint":
398                                 if (IsNullable)
399                                         return TdsColumnType.IntN;
400                                 return TdsColumnType.Int1;
401                         case "uniqueidentifier":
402                                 return TdsColumnType.UniqueIdentifier;
403                         case "varbinary":
404                                 return TdsColumnType.BigVarBinary;
405                         case "varchar":
406                                 return TdsColumnType.BigVarChar;
407                         default:
408                                 throw new NotSupportedException ("Unknown Type : " + TypeName);
409                         }
410                 }
411
412                 public void Validate (int index)
413                 {
414                         if ((this.direction == TdsParameterDirection.InputOutput || this.direction == TdsParameterDirection.Output) &&
415                                  this.isVariableSizeType && (Value == DBNull.Value || Value == null) && Size == 0
416                                 ) 
417                         {
418                                 throw new InvalidOperationException (String.Format ("{0}[{1}]: the Size property should " +
419                                                                                                 "not be of size 0",
420                                                                                                 this.typeName,
421                                                                                                 index));
422                         }
423                 }
424
425                 #endregion // Methods
426         }
427 }