Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds / TdsMetaParameter.cs
index 8a59211cff8e2c76207d98505d0288ad33b1fe88..8b704455cb3cb1cd1c77b378c82586f079ebd7a3 100644 (file)
@@ -33,8 +33,15 @@ using System;
 using System.Text;
 
 namespace Mono.Data.Tds {
+       public delegate object FrameworkValueGetter (object rawValue, ref bool updated);
+
        public class TdsMetaParameter
        {
+               #region Static 
+               public const int maxVarCharCharacters =  2147483647; // According to MS, max size is 2GB, 1 Byte Characters
+               public const int maxNVarCharCharacters = 1073741823; // According to MS, max size is 2GB, 2 Byte Characters
+               #endregion
+
                #region Fields
 
                TdsParameterDirection direction = TdsParameterDirection.Input;
@@ -46,6 +53,10 @@ namespace Mono.Data.Tds {
                bool isSizeSet = false;
                bool isNullable;
                object value;
+               bool isVariableSizeType;
+               FrameworkValueGetter frameworkValueGetter;
+               object rawValue;
+               bool isUpdated;
 
                #endregion // Fields
 
@@ -54,6 +65,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 +89,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 +122,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 {
@@ -121,15 +168,122 @@ 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; }
+               }
+
+               public bool IsVarNVarCharMax
+               {
+                       get { return (TypeName == "ntext" && size >= maxNVarCharCharacters); }
+               }
+
+               public bool IsVarCharMax
+               {
+                       get { return (TypeName == "text" && size >= maxVarCharCharacters); }
+               }
+
+               public bool IsAnyVarCharMax
+               {
+                       get { return IsVarNVarCharMax || IsVarCharMax; }
+               }
+
+               public bool IsNonUnicodeText
+               {
+                       get {
+                               TdsColumnType colType = GetMetaType();
+                               return (colType == TdsColumnType.VarChar ||
+                                       colType == TdsColumnType.BigVarChar ||
+                                       colType == TdsColumnType.Text ||
+                                       colType == TdsColumnType.Char ||
+                                       colType == TdsColumnType.BigChar);
+                       }
+               }
+
+               public bool IsMoneyType
+               {
+                       get {
+                               TdsColumnType colType = GetMetaType();
+                               return (colType == TdsColumnType.Money ||
+                                       colType == TdsColumnType.MoneyN ||
+                                       colType == TdsColumnType.Money4 ||
+                                       colType == TdsColumnType.SmallMoney);
+                       }
+               }
+
+               public bool IsDateTimeType
+               {
+                       get {
+                               TdsColumnType colType = GetMetaType();
+                               return (colType == TdsColumnType.DateTime ||
+                                       colType == TdsColumnType.DateTime4 ||
+                                       colType == TdsColumnType.DateTimeN);
+                       }
+               }
+
+               public bool IsTextType
+               {
+                       get {
+                               TdsColumnType colType = GetMetaType();
+                               return (colType == TdsColumnType.VarChar ||
+                                       colType == TdsColumnType.BigVarChar ||
+                                       colType == TdsColumnType.BigChar ||
+                                       colType == TdsColumnType.Char ||
+                                       colType == TdsColumnType.BigNVarChar || 
+                                       colType == TdsColumnType.NChar ||
+                                       colType == TdsColumnType.Text ||
+                                       colType == TdsColumnType.NText);
+                       }
+               }
+
+               public bool IsDecimalType
+               {
+                       get {
+                               TdsColumnType colType = GetMetaType();
+                               return (colType == TdsColumnType.Decimal ||
+                                       colType == TdsColumnType.Numeric);
+                       }
+               }
+
                #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 +295,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 +319,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 +343,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 +357,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;
+                               return TdsColumnType.BigChar;
+                       case "money":
+                               if (IsNullable)
+                                       return TdsColumnType.MoneyN;
+                               return TdsColumnType.Money;
+                       case "smallmoney":
+                               if (IsNullable)
+                                       return TdsColumnType.MoneyN ;
+                               return TdsColumnType.SmallMoney;
                        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 +458,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 +478,52 @@ 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 CalculateIsVariableType()
+               {
+                       switch (GetMetaType ()) {
+                               case TdsColumnType.UniqueIdentifier:
+                               case TdsColumnType.BigVarChar:
+                               case TdsColumnType.BigVarBinary:
+                               case TdsColumnType.IntN:
+                               case TdsColumnType.Text:
+                               case TdsColumnType.FloatN:
+                               case TdsColumnType.BigNVarChar:
+                               case TdsColumnType.NText:
+                               case TdsColumnType.Image:
+                               case TdsColumnType.Decimal:
+                               case TdsColumnType.BigBinary:
+                               case TdsColumnType.DateTimeN:
+                               case TdsColumnType.MoneyN:
+                               case TdsColumnType.BitN:
+                               case TdsColumnType.Char:
+                               case TdsColumnType.BigChar:
+                               case TdsColumnType.NChar:
+                                       IsVariableSizeType = true;
+                                       break;
+                               default:
+                                       IsVariableSizeType = false;
+                               break;
+                       }
+               }
+
+               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));
                        }
                }