Support for Partlentype type-info in TDS 7
[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" || TypeName == "nchar") {
194                                         if (text.Length > size)
195                                                 return text.Substring (0, size);
196                                 }
197                         } else if (newValue.GetType () == typeof (byte [])) {
198                                 byte [] buffer = (byte []) newValue;
199                                 if (buffer.Length > size) {
200                                         byte [] tmpVal = new byte [size];
201                                         Array.Copy (buffer, tmpVal, size);
202                                         return tmpVal;
203                                 }
204                         }
205                         return newValue;
206                 }
207
208                 internal string Prepare ()
209                 {
210                         string typeName = TypeName;
211                         
212                         if (typeName == "varbinary") {
213                                 int size = Size;
214                                 if (size <= 0) {
215                                         size = GetActualSize ();
216                                 }
217                                 
218                                 if (size > 8000) {
219                                         typeName = "image";
220                                 }
221                         }
222                         
223                         string includeAt = "@";
224                         if (ParameterName [0] == '@')
225                                 includeAt = "";
226                         StringBuilder result = new StringBuilder (String.Format ("{0}{1} {2}", includeAt, ParameterName, typeName));
227                         switch (typeName) {
228                         case "decimal":
229                         case "numeric":
230                                 // msdotnet sends a default precision of 28
231                                 result.Append (String.Format ("({0},{1})",
232                                          (Precision == (byte)0 ? (byte)28 : Precision), Scale));
233                                 break;
234                         case "varchar":
235                         case "varbinary":
236                                 //A size of 0 is not allowed in declarations.
237                                 int size = Size;
238                                 if (size <= 0) {
239                                         size = GetActualSize ();
240                                         if (size <= 0)
241                                                 size = 1;
242                                 }
243                                 result.Append (size > 8000 ? "(max)" : String.Format ("({0})", size));
244                                 break;
245                         case "nvarchar":
246                                 result.Append (Size > 0 ? (Size > 8000 ? "(max)" : String.Format ("({0})", Size)) : "(4000)");
247                                 break;
248                         case "char":
249                         case "nchar":
250                         case "binary":
251                                 if (isSizeSet && Size > 0)
252                                         result.Append (String.Format ("({0})", Size));
253                                 break;
254                         }
255                         return result.ToString ();
256                 }
257
258                 internal int GetActualSize ()
259                 {
260                         if (Value == DBNull.Value || Value == null)
261                                 return 0;
262
263                         switch (Value.GetType ().ToString ()) {
264                         case "System.String":
265                                 int len = ((string)value).Length;
266                                 if (TypeName == "nvarchar" || TypeName == "nchar" || TypeName == "ntext")
267                                         len *= 2;
268                                 return len ;    
269                         case "System.Byte[]":
270                                 return ((byte[]) value).Length;
271                         }
272                         return GetSize ();
273                 }
274
275                 private int GetSize ()
276                 {
277                         switch (TypeName) {
278                         case "decimal":
279                                 return 17;
280                         case "uniqueidentifier":
281                                 return 16;
282                         case "bigint":
283                         case "datetime":
284                         case "float":
285                         case "money":
286                                 return 8;
287                         case "int":
288                         case "real":
289                         case "smalldatetime":
290                         case "smallmoney":
291                                 return 4;
292                         case "smallint":
293                                 return 2;
294                         case "tinyint":
295                         case "bit":
296                                 return 1;
297                         /*
298                         case "nvarchar" :
299                         */
300                         case "nchar" :
301                         case "ntext" :
302                                 return size*2 ;
303                         }
304                         return size;
305                 }
306
307                 internal byte[] GetBytes ()
308                 {
309                         byte[] result = {};
310                         if (Value == DBNull.Value || Value == null)
311                                 return result;
312
313                         switch (TypeName)
314                         {
315                                 case "nvarchar" :
316                                 case "nchar" :
317                                 case "ntext" :
318                                         return Encoding.Unicode.GetBytes ((string)Value);
319                                 case "varchar" :
320                                 case "char" :
321                                 case "text" :
322                                         return Encoding.Default.GetBytes ((string)Value);
323                                 default :
324                                         return ((byte[]) Value);
325                         }
326                 }
327
328                 internal TdsColumnType GetMetaType ()
329                 {
330                         switch (TypeName) {
331                         case "binary":
332                                 return TdsColumnType.BigBinary;
333                         case "bit":
334                                 if (IsNullable)
335                                         return TdsColumnType.BitN;
336                                 return TdsColumnType.Bit;
337                         case "bigint":
338                                 return TdsColumnType.IntN;
339                         case "char":
340                                 return TdsColumnType.Char;
341                         case "money":
342                                 if (IsNullable)
343                                         return TdsColumnType.MoneyN;
344                                 return TdsColumnType.Money;
345                         case "smallmoney":
346                                 if (IsNullable)
347                                         return TdsColumnType.MoneyN ;
348                                 return TdsColumnType.Money4;
349                         case "decimal":
350                                 return TdsColumnType.Decimal;
351                         case "datetime":
352                                 if (IsNullable)
353                                         return TdsColumnType.DateTimeN;
354                                 return TdsColumnType.DateTime;
355                         case "smalldatetime":
356                                 if (IsNullable)
357                                         return TdsColumnType.DateTimeN;
358                                 return TdsColumnType.DateTime4;
359                         case "float":
360                                 if (IsNullable)
361                                         return TdsColumnType.FloatN ;
362                                 return TdsColumnType.Float8;
363                         case "image":
364                                 return TdsColumnType.Image;
365                         case "int":
366                                 if (IsNullable)
367                                         return TdsColumnType.IntN;
368                                 return TdsColumnType.Int4;
369                         case "numeric":
370                                 return TdsColumnType.Numeric;
371                         case "nchar":
372                                 return TdsColumnType.NChar;
373                         case "ntext":
374                                 return TdsColumnType.NText;
375                         case "nvarchar":
376                                 return TdsColumnType.BigNVarChar;
377                         case "real":
378                                 if (IsNullable)
379                                         return TdsColumnType.FloatN ;
380                                 return TdsColumnType.Real;
381                         case "smallint":
382                                 if (IsNullable)
383                                         return TdsColumnType.IntN;
384                                 return TdsColumnType.Int2;
385                         case "text":
386                                 return TdsColumnType.Text;
387                         case "tinyint":
388                                 if (IsNullable)
389                                         return TdsColumnType.IntN;
390                                 return TdsColumnType.Int1;
391                         case "uniqueidentifier":
392                                 return TdsColumnType.UniqueIdentifier;
393                         case "varbinary":
394                                 return TdsColumnType.BigVarBinary;
395                         case "varchar":
396                                 return TdsColumnType.BigVarChar;
397                         default:
398                                 throw new NotSupportedException ("Unknown Type : " + TypeName);
399                         }
400                 }
401
402                 public void Validate (int index)
403                 {
404                         if ((this.direction == TdsParameterDirection.InputOutput || this.direction == TdsParameterDirection.Output) &&
405                                  this.isVariableSizeType && (Value == DBNull.Value || Value == null) && Size == 0
406                                 ) 
407                         {
408                                 throw new InvalidOperationException (String.Format ("{0}[{1}]: the Size property should " +
409                                                                                                 "not be of size 0",
410                                                                                                 this.typeName,
411                                                                                                 index));
412                         }
413                 }
414
415                 #endregion // Methods
416         }
417 }