Merge pull request #214 from QuickJack/cd2c570c5543963d987f51080218715407c5d4b9
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds / TdsMetaParameter.cs
index 2e5a38ddd4f6d1ba1ca02cf0b2b83796923dd076..aa5422df5e3bf0708cccaf425567513b8e822f3e 100644 (file)
@@ -33,6 +33,8 @@ using System;
 using System.Text;
 
 namespace Mono.Data.Tds {
+       public delegate object FrameworkValueGetter (object rawValue, ref bool updated);
+
        public class TdsMetaParameter
        {
                #region Fields
@@ -46,6 +48,10 @@ namespace Mono.Data.Tds {
                bool isSizeSet = false;
                bool isNullable;
                object value;
+               bool isVariableSizeType;
+               FrameworkValueGetter frameworkValueGetter;
+               object rawValue;
+               bool isUpdated;
 
                #endregion // Fields
 
@@ -54,6 +60,12 @@ namespace Mono.Data.Tds {
                {
                }
 
+               public TdsMetaParameter (string name, FrameworkValueGetter valueGetter)
+                       : this (name, String.Empty, null)
+               {
+                       frameworkValueGetter = valueGetter;
+               }
+
                public TdsMetaParameter (string name, string typeName, object value)
                {
                        ParameterName = name;
@@ -72,6 +84,16 @@ namespace Mono.Data.Tds {
                        Value = value;
                }
 
+               public TdsMetaParameter (string name, int size, bool isNullable, byte precision, byte scale, FrameworkValueGetter valueGetter)
+               {
+                       ParameterName = name;
+                       Size = size;
+                       IsNullable = isNullable;
+                       Precision = precision;
+                       Scale = scale;
+                       frameworkValueGetter = valueGetter;
+               }
+
                #region Properties
 
                public TdsParameterDirection Direction {
@@ -95,8 +117,28 @@ namespace Mono.Data.Tds {
                }
 
                public object Value {
-                       get { return value; }
-                       set { this.value = value; }
+                       get {
+                               if (frameworkValueGetter != null) {
+                                       object newValue = frameworkValueGetter (rawValue, ref isUpdated);
+                                       if (isUpdated)
+                                               value = newValue;
+                               }
+
+                               if (isUpdated) {
+                                       value = ResizeValue (value);
+                                       isUpdated = false;
+                               }
+                               return value;
+                       }
+                       set {
+                               rawValue = this.value = value;
+                               isUpdated = true;
+                       }
+               }
+
+               public object RawValue {
+                       get { return rawValue; }
+                       set { Value = value; }
                }
 
                public byte Precision {
@@ -107,7 +149,7 @@ namespace Mono.Data.Tds {
                public byte Scale {
                        get { 
                                if (TypeName == "decimal" || TypeName == "numeric") {
-                                       if (scale == 0) { 
+                                       if (scale == 0 && !Convert.IsDBNull(Value)) {
                                                int[] arr = Decimal.GetBits (
                                                                Convert.ToDecimal(Value));
                                                scale = (byte)((arr[3]>>16) & (int)0xFF);
@@ -121,15 +163,50 @@ namespace Mono.Data.Tds {
                public int Size {
                        get { return GetSize (); }
                        set {
-                               size = value; 
+                               size = value;
+                               isUpdated = true;
                                isSizeSet = true;
                        }
                }
 
+               public bool IsVariableSizeType
+               {
+                       get { return isVariableSizeType; }
+                       set { isVariableSizeType = value; }
+               }
+
                #endregion // Properties
 
                #region Methods
 
+               object ResizeValue (object newValue)
+               {
+                       if (newValue == DBNull.Value || newValue == null)
+                               return newValue;
+
+                       if (!isSizeSet || size <= 0)
+                               return newValue;
+
+                       // if size is set, truncate the value to specified size
+                       string text = newValue as string;
+                       if (text != null) {
+                               if (TypeName == "nvarchar" || 
+                                   TypeName == "nchar" ||
+                                   TypeName == "xml") {
+                                       if (text.Length > size)
+                                               return text.Substring (0, size);
+                               }
+                       } else if (newValue.GetType () == typeof (byte [])) {
+                               byte [] buffer = (byte []) newValue;
+                               if (buffer.Length > size) {
+                                       byte [] tmpVal = new byte [size];
+                                       Array.Copy (buffer, tmpVal, size);
+                                       return tmpVal;
+                               }
+                       }
+                       return newValue;
+               }
+
                internal string Prepare ()
                {
                        string typeName = TypeName;
@@ -141,17 +218,20 @@ namespace Mono.Data.Tds {
                                }
                                
                                if (size > 8000) {
-                                       typeName = "image";
+                                       typeName = "varbinary(max)";
                                }
                        }
                        
-                       StringBuilder result = new StringBuilder (String.Format ("{0} {1}", ParameterName, typeName));
+                       string includeAt = "@";
+                       if (ParameterName [0] == '@')
+                               includeAt = "";
+                       StringBuilder result = new StringBuilder (String.Format ("{0}{1} {2}", includeAt, ParameterName, typeName));
                        switch (typeName) {
                        case "decimal":
                        case "numeric":
-                               // msdotnet sends a default precision of 28
+                               // msdotnet sends a default precision of 29
                                result.Append (String.Format ("({0},{1})",
-                                        (Precision == (byte)0 ? (byte)28 : Precision), Scale));
+                                        (Precision == (byte)0 ? (byte)38 : Precision), Scale));
                                break;
                        case "varchar":
                        case "varbinary":
@@ -162,10 +242,12 @@ namespace Mono.Data.Tds {
                                        if (size <= 0)
                                                size = 1;
                                }
-                               result.Append (String.Format ("({0})", size));
+                               result.Append (size > 8000 ? "(max)" : String.Format ("({0})", size));
                                break;
                        case "nvarchar":
-                               result.Append (String.Format ("({0})", Size > 0 ? Size : 4000));
+                       case "xml":
+                               int paramSize = Size < 0 ? GetActualSize () / 2 : Size;
+                               result.Append (paramSize > 0 ? (paramSize > 4000 ? "(max)" : String.Format ("({0})", paramSize)) : "(4000)");
                                break;
                        case "char":
                        case "nchar":
@@ -184,7 +266,12 @@ namespace Mono.Data.Tds {
 
                        switch (Value.GetType ().ToString ()) {
                        case "System.String":
-                               return ((string) value).Length;
+                               int len = ((string)value).Length;
+                               if (TypeName == "nvarchar" || TypeName == "nchar" 
+                                   || TypeName == "ntext"
+                                   || TypeName == "xml")
+                                       len *= 2;
+                               return len ;    
                        case "System.Byte[]":
                                return ((byte[]) value).Length;
                        }
@@ -193,45 +280,94 @@ namespace Mono.Data.Tds {
 
                private int GetSize ()
                {
-                       if (IsNullable) {
-                               switch (TypeName) {
-                               case "bigint":
-                                       return 8;
-                               case "datetime":
-                                       return 8;
-                               case "float":
-                                       return 8;
-                               case "int":
-                                       return 4;
-                               case "real":
-                                       return 4;
-                               case "smalldatetime":
-                                       return 4;
-                               case "smallint":
-                                       return 2;
-                               case "tinyint":
-                                       return 1;
-                               }
+                       switch (TypeName) {
+                       case "decimal":
+                               return 17;
+                       case "uniqueidentifier":
+                               return 16;
+                       case "bigint":
+                       case "datetime":
+                       case "float":
+                       case "money":
+                               return 8;
+                       case "int":
+                       case "real":
+                       case "smalldatetime":
+                       case "smallmoney":
+                               return 4;
+                       case "smallint":
+                               return 2;
+                       case "tinyint":
+                       case "bit":
+                               return 1;
+                       /*
+                       case "nvarchar" :
+                       */
+                       case "nchar" :
+                       case "ntext" :
+                               return size*2 ;
                        }
                        return size;
                }
 
+               internal byte[] GetBytes ()
+               {
+                       byte[] result = {};
+                       if (Value == DBNull.Value || Value == null)
+                               return result;
+
+                       switch (TypeName)
+                       {
+                               case "nvarchar" :
+                               case "nchar" :
+                               case "ntext" :
+                               case "xml" :
+                                       return Encoding.Unicode.GetBytes ((string)Value);
+                               case "varchar" :
+                               case "char" :
+                               case "text" :
+                                       return Encoding.Default.GetBytes ((string)Value);
+                               default :
+                                       return ((byte[]) Value);
+                       }
+               }
+
                internal TdsColumnType GetMetaType ()
                {
                        switch (TypeName) {
                        case "binary":
-                               return TdsColumnType.Binary;
+                               return TdsColumnType.BigBinary;
                        case "bit":
+                               if (IsNullable)
+                                       return TdsColumnType.BitN;
                                return TdsColumnType.Bit;
+                       case "bigint":
+                               if (IsNullable)
+                                       return TdsColumnType.IntN ;
+                               return TdsColumnType.BigInt;
                        case "char":
                                return TdsColumnType.Char;
+                       case "money":
+                               if (IsNullable)
+                                       return TdsColumnType.MoneyN;
+                               return TdsColumnType.Money;
+                       case "smallmoney":
+                               if (IsNullable)
+                                       return TdsColumnType.MoneyN ;
+                               return TdsColumnType.Money4;
                        case "decimal":
                                return TdsColumnType.Decimal;
                        case "datetime":
                                if (IsNullable)
                                        return TdsColumnType.DateTimeN;
                                return TdsColumnType.DateTime;
+                       case "smalldatetime":
+                               if (IsNullable)
+                                       return TdsColumnType.DateTimeN;
+                               return TdsColumnType.DateTime4;
                        case "float":
+                               if (IsNullable)
+                                       return TdsColumnType.FloatN ;
                                return TdsColumnType.Float8;
                        case "image":
                                return TdsColumnType.Image;
@@ -245,9 +381,12 @@ namespace Mono.Data.Tds {
                                return TdsColumnType.NChar;
                        case "ntext":
                                return TdsColumnType.NText;
+                       case "xml":
                        case "nvarchar":
-                               return TdsColumnType.NVarChar;
+                               return TdsColumnType.BigNVarChar;
                        case "real":
+                               if (IsNullable)
+                                       return TdsColumnType.FloatN ;
                                return TdsColumnType.Real;
                        case "smallint":
                                if (IsNullable)
@@ -262,11 +401,24 @@ namespace Mono.Data.Tds {
                        case "uniqueidentifier":
                                return TdsColumnType.UniqueIdentifier;
                        case "varbinary":
-                               return TdsColumnType.VarBinary;
+                               return TdsColumnType.BigVarBinary;
                        case "varchar":
-                               return TdsColumnType.VarChar;
+                               return TdsColumnType.BigVarChar;
                        default:
-                               throw new NotSupportedException ();
+                               throw new NotSupportedException ("Unknown Type : " + TypeName);
+                       }
+               }
+
+               public void Validate (int index)
+               {
+                       if ((this.direction == TdsParameterDirection.InputOutput || this.direction == TdsParameterDirection.Output) &&
+                                this.isVariableSizeType && (Value == DBNull.Value || Value == null) && Size == 0
+                               ) 
+                       {
+                               throw new InvalidOperationException (String.Format ("{0}[{1}]: the Size property should " +
+                                                                                               "not be of size 0",
+                                                                                               this.typeName,
+                                                                                               index));
                        }
                }