New tests.
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds70.cs
index 1d068e48781b3a27ce2c64f44acbde1e49b3ae8c..57fb0914645e385fc2638e012dba459829845478 100644 (file)
@@ -6,12 +6,13 @@
 //   Diego Caravana (diego@toth.it)
 //   Sebastien Pouliot (sebastien@ximian.com)
 //   Daniel Morgan (danielmorgan@verizon.net)
+//   Gert Driesen (drieseng@users.sourceforge.net)
+//   Veerapuram Varadhan  (vvaradhan@novell.com)
 //
 // Copyright (C) 2002 Tim Coleman
 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
 // Portions (C) 2003 Daniel Morgan
 //
-
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using Mono.Security.Protocol.Ntlm;
 using System;
+using System.Globalization;
 using System.Text;
 
-namespace Mono.Data.Tds.Protocol {
-        public class Tds70 : Tds
+using Mono.Security.Protocol.Ntlm;
+
+namespace Mono.Data.Tds.Protocol
+{
+       public class Tds70 : Tds
        {
                #region Fields
 
-               public readonly static TdsVersion Version = TdsVersion.tds70;
+               //public readonly static TdsVersion Version = TdsVersion.tds70;
+               static readonly decimal SMALLMONEY_MIN = -214748.3648m;
+               static readonly decimal SMALLMONEY_MAX = 214748.3647m;
 
                #endregion // Fields
 
@@ -54,12 +60,24 @@ namespace Mono.Data.Tds.Protocol {
                }
 
                public Tds70 (string server, int port, int packetSize, int timeout)
-                       : base (server, port, packetSize, timeout, Version)
+                       : base (server, port, packetSize, timeout, TdsVersion.tds70)
                {
                }
 
+               public Tds70 (string server, int port, int packetSize, int timeout, TdsVersion version)
+                       : base (server, port, packetSize, timeout, version)
+               {
+               }
+               
                #endregion // Constructors
 
+               #region Properties
+               
+               protected virtual byte[] ClientVersion {
+                       get { return new byte[] {0x00, 0x0, 0x0, 0x70};}
+               }
+               #endregion // Properties
+               
                #region Methods
 
                private string BuildExec (string sql)
@@ -78,14 +96,18 @@ namespace Mono.Data.Tds.Protocol {
 
                        StringBuilder result = new StringBuilder ();
                        foreach (TdsMetaParameter p in Parameters) {
+                               string parameterName = p.ParameterName;
+                               if (parameterName [0] == '@') {
+                                       parameterName = parameterName.Substring (1);
+                               }
                                if (p.Direction != TdsParameterDirection.ReturnValue) {
-                               if (result.Length > 0)
-                                       result.Append (", ");
+                                       if (result.Length > 0)
+                                               result.Append (", ");
                                        if (p.Direction == TdsParameterDirection.InputOutput)
-                                               result.Append (String.Format("{0}={0} output", p.ParameterName));
+                                               result.AppendFormat ("@{0}={0} output", parameterName);
                                        else
-                               result.Append (FormatParameter (p));
-                       }
+                                               result.Append (FormatParameter (p));
+                               }
                        }
                        return result.ToString ();
                }
@@ -119,35 +141,38 @@ namespace Mono.Data.Tds.Protocol {
                        int count = 0;
                        if (Parameters != null) {
                                foreach (TdsMetaParameter p in Parameters) {
-                                       if (p.Direction != TdsParameterDirection.Input) {
+                                       string parameterName = p.ParameterName;
+                                       if (parameterName [0] == '@') {
+                                               parameterName = parameterName.Substring (1);
+                                       }
 
+                                       if (p.Direction != TdsParameterDirection.Input) {
                                                if (count == 0)
                                                        select.Append ("select ");
                                                else
                                                        select.Append (", ");
-                                               select.Append (p.ParameterName);
+                                               select.Append ("@" + parameterName);
                                                        
                                                declare.Append (String.Format ("declare {0}\n", p.Prepare ()));
 
                                                if (p.Direction != TdsParameterDirection.ReturnValue) {
-                                                       if( p.Direction == TdsParameterDirection.InputOutput )
+                                                       if (p.Direction == TdsParameterDirection.InputOutput)
                                                                set.Append (String.Format ("set {0}\n", FormatParameter(p)));
                                                        else
-                                               set.Append (String.Format ("set {0}=NULL\n", p.ParameterName));
+                                                               set.Append (String.Format ("set @{0}=NULL\n", parameterName));
                                                }
                                        
-                                               count += 1;
-                                       }
-                                       
-                                       if (p.Direction == TdsParameterDirection.ReturnValue) {
-                                               exec = p.ParameterName + "=";
+                                               count++;
                                        }
+                                       if (p.Direction == TdsParameterDirection.ReturnValue)
+                                               exec = "@" + parameterName + "=";
                                }
                        }
-                        exec = "exec " + exec;
-                        
-                        string sql = String.Format ("{0}{1}{2}{3} {4}\n{5}", declare.ToString (), set.ToString (), exec, procedure, BuildParameters (), select.ToString ());
-                       return sql;
+                       exec = "exec " + exec;
+
+                       return String.Format ("{0}{1}{2}{3} {4}\n{5}",
+                               declare.ToString (), set.ToString (), exec,
+                               procedure, BuildParameters (), select.ToString ());
                }
 
                public override bool Connect (TdsConnectionParameters connectionParameters)
@@ -164,50 +189,50 @@ namespace Mono.Data.Tds.Protocol {
                        short authLen = 0;
                        byte pad = (byte) 0;
                        
-                       byte[] domainMagic =  { 6, 0x7d, 0x0f, 0xfd, 0xff, 0x0, 0x0, 0x0,
+                       byte[] domainMagic = { 6, 0x7d, 0x0f, 0xfd, 0xff, 0x0, 0x0, 0x0,
                                                                        0x0, 0xe0, 0x83, 0x0, 0x0,
                                                                        0x68, 0x01, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00 };
-                       byte[] sqlserverMagic = { 6, 0x83, 0xf2, 0xf8,
-                                                                               0xff, 0x0, 0x0, 0x0,
+                       byte[] sqlserverMagic = { 6, 0x0, 0x0, 0x0,
+                                                                               0x0, 0x0, 0x0, 0x0,
                                                                                0x0, 0xe0, 0x03, 0x0,
-                                                                               0x0, 0x88, 0xff, 0xff, 0xff, 0x36
-                                                                               0x04, 0x00, 0x00 };
+                                                                               0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+                                                                               0x0, 0x0, 0x0 };
                        byte[] magic = null;
                        
-                       if (connectionParameters.DomainLogin == true)
+                       if (connectionParameters.DomainLogin)
                                magic = domainMagic;
                        else
                                magic = sqlserverMagic;
                        
                        string username = connectionParameters.User;
+                       string domain = null;
 
-                       string domain = Environment.UserDomainName;
-                       domain = connectionParameters.DefaultDomain = Environment.UserDomainName;
-
-                       int idx = 0;
-                       if ((idx = username.IndexOf ("\\")) > -1) {
+                       int idx = username.IndexOf ("\\");
+                       if (idx != -1) {
                                domain = username.Substring (0, idx);
                                username = username.Substring (idx + 1);
 
                                connectionParameters.DefaultDomain = domain;
                                connectionParameters.User = username;
+                       } else {
+                               domain = Environment.UserDomainName;
+                               connectionParameters.DefaultDomain = domain;
                        }
-                                                       
+
                        short partialPacketSize = (short) (86 + (
-                               connectionParameters.Hostname.Length +          
-                               connectionParameters.ApplicationName.Length + 
+                               connectionParameters.Hostname.Length +
+                               connectionParameters.ApplicationName.Length +
                                DataSource.Length +
                                connectionParameters.LibraryName.Length +
                                Language.Length +
                                connectionParameters.Database.Length +
-                               connectionParameters.AttachDBFileName.Length) * 2); 
-                       
-                       if(connectionParameters.DomainLogin == true) {
+                               connectionParameters.AttachDBFileName.Length) * 2);
+
+                       if (connectionParameters.DomainLogin) {
                                authLen = ((short) (32 + (connectionParameters.Hostname.Length +
                                        domain.Length)));
                                partialPacketSize += authLen;
-                       }
-                       else 
+                       } else
                                partialPacketSize += ((short) ((username.Length + connectionParameters.Password.Length) * 2));
                        
                        int totalPacketSize = partialPacketSize;
@@ -216,28 +241,27 @@ namespace Mono.Data.Tds.Protocol {
                        
                        Comm.Append (totalPacketSize);
 
-                       Comm.Append (empty, 3, pad);
-                       Comm.Append ((byte) 0x70); // TDS Version 7
+                       //Comm.Append (empty, 3, pad);
+                       //byte[] version = {0x00, 0x0, 0x0, 0x71};
+                       //Console.WriteLine ("Version: {0}", ClientVersion[3]);
+                       Comm.Append (ClientVersion); // TDS Version 7
                        Comm.Append ((int)this.PacketSize); // Set the Block Size
                        Comm.Append (empty, 3, pad);
                        Comm.Append (magic);
 
                        short curPos = 86;
 
-                       // Hostname 
+                       // Hostname
                        Comm.Append (curPos);
                        Comm.Append ((short) connectionParameters.Hostname.Length);
                        curPos += (short) (connectionParameters.Hostname.Length * 2);
 
-                       if(connectionParameters.DomainLogin.Equals(true))
-                       {
+                       if (connectionParameters.DomainLogin) {
                                Comm.Append((short)0);
                                Comm.Append((short)0);
                                Comm.Append((short)0);
                                Comm.Append((short)0);
-                       }
-                       else 
-                       {
+                       } else {
                                // Username
                                Comm.Append (curPos);
                                Comm.Append ((short) username.Length);
@@ -249,7 +273,7 @@ namespace Mono.Data.Tds.Protocol {
                                curPos += (short) (connectionParameters.Password.Length * 2);
                        }
 
-                       // AppName                      
+                       // AppName
                        Comm.Append (curPos);
                        Comm.Append ((short) connectionParameters.ApplicationName.Length);
                        curPos += (short) (connectionParameters.ApplicationName.Length * 2);
@@ -260,7 +284,7 @@ namespace Mono.Data.Tds.Protocol {
                        curPos += (short) (DataSource.Length * 2);
 
                        // Unknown
-                       Comm.Append ((short) 0);
+                       Comm.Append ((short) curPos);
                        Comm.Append ((short) 0);
 
                        // Library Name
@@ -288,12 +312,10 @@ namespace Mono.Data.Tds.Protocol {
 
                        // Authentication Stuff
                        Comm.Append ((short) curPos);
-                       if (connectionParameters.DomainLogin == true) 
-                       {
+                       if (connectionParameters.DomainLogin) {
                                Comm.Append ((short) authLen);
                                curPos += (short) authLen;
-                       }
-                       else
+                       } else
                                Comm.Append ((short) 0);
                        
                        // Unknown
@@ -303,8 +325,7 @@ namespace Mono.Data.Tds.Protocol {
                        
                        // Connection Parameters
                        Comm.Append (connectionParameters.Hostname);
-                       if (connectionParameters.DomainLogin == false) 
-                       {
+                       if (!connectionParameters.DomainLogin) {
                                // SQL Server Authentication
                                Comm.Append (connectionParameters.User);
                                string scrambledPwd = EncryptPassword (connectionParameters.Password);
@@ -316,16 +337,15 @@ namespace Mono.Data.Tds.Protocol {
                        Comm.Append (Language);
                        Comm.Append (connectionParameters.Database);
 
-                       if (connectionParameters.DomainLogin) 
-                       {
+                       if (connectionParameters.DomainLogin) {
                                // the rest of the packet is NTLMSSP authentication
                                Type1Message msg = new Type1Message ();
                                msg.Domain = domain;
                                msg.Host = connectionParameters.Hostname;
-                               msg.Flags = NtlmFlags.NegotiateUnicode | 
-                                       NtlmFlags.NegotiateNtlm | 
-                                       NtlmFlags.NegotiateDomainSupplied | 
-                                       NtlmFlags.NegotiateWorkstationSupplied | 
+                               msg.Flags = NtlmFlags.NegotiateUnicode |
+                                       NtlmFlags.NegotiateNtlm |
+                                       NtlmFlags.NegotiateDomainSupplied |
+                                       NtlmFlags.NegotiateWorkstationSupplied |
                                        NtlmFlags.NegotiateAlwaysSign; // 0xb201
                                Comm.Append (msg.GetBytes ());
                        }
@@ -356,94 +376,334 @@ namespace Mono.Data.Tds.Protocol {
 
                public override bool Reset ()
                {
-                       try {
-                               ExecProc ("sp_reset_connection");
-                               base.Reset (); 
-                       } catch (Exception e) {
-                               System.Reflection.PropertyInfo pinfo = e.GetType ().GetProperty ("Class");
-                               if (pinfo != null && pinfo.PropertyType == typeof (byte)) {
-                                       byte klass = (byte) pinfo.GetValue (e, null);
-                                       // 11 to 16 indicates error that can be fixed by the user such as 'Invalid object name'
-                                       if (klass < 11 || klass > 16)
-                                               return false;
-                               }
-                       }
+                       // Check validity of the connection - a false removes
+                       // the connection from the pool
+                       // NOTE: MS implementation will throw a connection-reset error as it will
+                       // try to use the same connection
+                       if (!Comm.IsConnected ())
+                               return false;
 
+                       // Set "reset-connection" bit for the next message packet
+                       Comm.ResetConnection = true;
+                       base.Reset ();
                        return true;
                }
 
                public override void ExecPrepared (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
                {
                        Parameters = parameters;
-                       ExecuteQuery (BuildPreparedQuery (commandText), timeout, wantResults);
+                       if (Parameters != null && Parameters.Count > 0)
+                               ExecRPC (TdsRpcProcId.ExecuteSql, commandText, parameters, timeout, wantResults);
+                       else
+                               ExecuteQuery (BuildPreparedQuery (commandText), timeout, wantResults);
                }
                        
                public override void ExecProc (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
                {
-                       if (parameters != null && parameters.Count > 0) {
-                               Parameters = parameters;
-                               ExecuteQuery (BuildProcedureCall (commandText), timeout, wantResults);
-                       } else {
-                               ExecRPC (commandText, parameters, timeout, wantResults);
+                       Parameters = parameters;
+                       ExecRPC (commandText, parameters, timeout, wantResults);
+               }
+
+               private void WriteRpcParameterInfo (TdsMetaParameterCollection parameters)
+               {
+                       if (parameters != null) {
+                               foreach (TdsMetaParameter param in parameters) {
+                                       if (param.Direction == TdsParameterDirection.ReturnValue) 
+                                               continue;
+                                       string pname = param.ParameterName;
+                                       if (pname != null && pname.Length > 0 && pname [0] == '@') {
+                                               Comm.Append ( (byte) pname.Length);
+                                               Comm.Append (pname);
+                                       } else {
+                                               Comm.Append ( (byte) (pname.Length + 1));
+                                               Comm.Append ("@" + pname);
+                                       }
+                                       short status = 0; // unused
+                                       if (param.Direction != TdsParameterDirection.Input)
+                                               status |= 0x01; // output
+                                       Comm.Append ( (byte) status);
+                                       WriteParameterInfo (param);
+                               }
                        }
                }
+               
+               private void WritePreparedParameterInfo (TdsMetaParameterCollection parameters)
+               {
+                       if (parameters == null)
+                               return;
+                       
+                       string param = BuildPreparedParameters ();
+                       Comm.Append ((byte) 0x00); // no param meta data name
+                       Comm.Append ((byte) 0x00); // no status flags
+                       
+                       // Type_info - parameter info
+                       WriteParameterInfo (new TdsMetaParameter ("prep_params", 
+                                                                 param.Length > 4000 ? "ntext" : "nvarchar", 
+                                                                 param));
+               }
+               
+               private void ExecRPC (TdsRpcProcId rpcId, string sql, 
+                                     TdsMetaParameterCollection parameters, 
+                                     int timeout, bool wantResults)
+               {
+                       // clean up
+                       InitExec ();
+                       Comm.StartPacket (TdsPacketType.RPC);
+                       
+                       Comm.Append ((ushort) 0xFFFF);
+                       Comm.Append ((ushort) rpcId);
+                       Comm.Append ((short) 0x02); // no meta data
+                       
+                       Comm.Append ((byte) 0x00); // no param meta data name
+                       Comm.Append ((byte) 0x00); // no status flags
+                       
+                       // Write sql as a parameter value - UCS2
+                       TdsMetaParameter param = new TdsMetaParameter ("sql", 
+                                                                      sql.Length > 4000 ? "ntext":"nvarchar",
+                                                                      sql);            
+                       WriteParameterInfo (param);
+                       
+                       // Write Parameter infos - name and type
+                       WritePreparedParameterInfo (parameters);
 
+                       // Write parameter/value info
+                       WriteRpcParameterInfo (parameters);
+                       Comm.SendPacket ();
+                       CheckForData (timeout);
+                       if (!wantResults)
+                               SkipToEnd ();
+               }
+               
                protected override void ExecRPC (string rpcName, TdsMetaParameterCollection parameters, 
                                                 int timeout, bool wantResults)
                {
-                       // clean up 
+                       // clean up
                        InitExec ();
-                       
                        Comm.StartPacket (TdsPacketType.RPC);
 
                        Comm.Append ( (short) rpcName.Length);
                        Comm.Append (rpcName);
                        Comm.Append ( (short) 0); //no meta data
-
-                       // FIXME : support parameters here
-
+                       WriteRpcParameterInfo (parameters);
                        Comm.SendPacket ();
                        CheckForData (timeout);
-                       if (!wantResults) 
+                       if (!wantResults)
                                SkipToEnd ();
+               }
+
+               private void WriteParameterInfo (TdsMetaParameter param)
+               {
+                       /*
+                       Ms.net send non-nullable datatypes as nullable and allows setting null values
+                       to int/float etc.. So, using Nullable form of type for all data
+                       */
+                       param.IsNullable = true;
+                       TdsColumnType colType = param.GetMetaType ();
+                       param.IsNullable = false;
+
+                       bool partLenType = false;
+                       int size = param.Size;
+                       if (size < 1) {
+                               if (size < 0)
+                                       partLenType = true;
+                               size = param.GetActualSize ();
+                       }
+
+                       // Change colType according to the following table
+                       /* 
+                        * Original Type        Maxlen          New Type 
+                        * 
+                        * NVarChar             4000 UCS2       NText
+                        * BigVarChar           8000 ASCII      Text
+                        * BigVarBinary         8000 bytes      Image
+                        * 
+                        */
+                       TdsColumnType origColType = colType;
+                       if (colType == TdsColumnType.BigNVarChar) {
+                               // param.GetActualSize() returns len*2
+                               if (size == param.Size)
+                                       size <<= 1;
+                               if ((size >> 1) > 4000)
+                                       colType = TdsColumnType.NText;
+                       } else if (colType == TdsColumnType.BigVarChar) {
+                               if (size > 8000)
+                                       colType = TdsColumnType.Text;   
+                       } else if (colType == TdsColumnType.BigVarBinary) {
+                               if (size > 8000)
+                                       colType = TdsColumnType.Image;
+                       }
+                       // Calculation of TypeInfo field
+                       /* 
+                        * orig size value              TypeInfo field
+                        * 
+                        * >= 0 <= Maxlen               origColType + content len
+                        * > Maxlen             NewType as per above table + content len
+                        * -1           origColType + USHORTMAXLEN (0xFFFF) + content len (TDS 9)
+                        * 
+                        */
+                       // Write updated colType, iff partLenType == false
+                       if (TdsVersion > TdsVersion.tds81 && partLenType) {
+                               Comm.Append ((byte)origColType);
+                               Comm.Append ((short)-1);
+                       } else if (ServerTdsVersion > TdsVersion.tds70 
+                                  && origColType == TdsColumnType.Decimal) {
+                               Comm.Append ((byte)TdsColumnType.Numeric);
+                       } else {
+                               Comm.Append ((byte)colType);
+                       }
+
+                       if (IsLargeType (colType))
+                               Comm.Append ((short)size); // Parameter size passed in SqlParameter
+                       else if (IsBlobType (colType))
+                               Comm.Append (size); // Parameter size passed in SqlParameter
+                       else
+                               Comm.Append ((byte)size);
+
+                       // Precision and Scale are non-zero for only decimal/numeric
+                       if ( param.TypeName == "decimal" || param.TypeName == "numeric") {
+                               Comm.Append ((param.Precision !=0 ) ? param.Precision : (byte) 29);
+                               Comm.Append (param.Scale);
+                               // Convert the decimal value according to Scale
+                               if (param.Value != null && param.Value != DBNull.Value &&
+                                   ((decimal)param.Value) != Decimal.MaxValue && 
+                                   ((decimal)param.Value) != Decimal.MinValue) {
+                                       decimal expo = new Decimal (System.Math.Pow (10, (double)param.Scale));
+                                       int pVal = (int)(((decimal)param.Value) * expo);
+                                       param.Value = (decimal)pVal;                            
+                               }
+                       }
 
+                       
+                       /* VARADHAN: TDS 8 Debugging */
+                       /*
+                       if (Collation != null) {
+                               Console.WriteLine ("Collation is not null");
+                               Console.WriteLine ("Column Type: {0}", colType);
+                               Console.WriteLine ("Collation bytes: {0} {1} {2} {3} {4}", Collation[0], Collation[1], Collation[2],
+                                                  Collation[3], Collation[4]);
+                       } else {
+                               Console.WriteLine ("Collation is null");
+                       }
+                       */
+                       
+                       // Tds > 7.0 uses collation
+                       if (Collation != null && 
+                           (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
+                            colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
+                            colType == TdsColumnType.NVarChar || colType == TdsColumnType.Text ||
+                            colType == TdsColumnType.NText))
+                               Comm.Append (Collation);
+
+                       // LAMESPEC: size should be 0xFFFF for any bigvarchar, bignvarchar and bigvarbinary 
+                       // types if param value is NULL
+                       if ((colType == TdsColumnType.BigVarChar || 
+                            colType == TdsColumnType.BigNVarChar ||
+                            colType == TdsColumnType.BigVarBinary) && 
+                           (param.Value == null || param.Value == DBNull.Value))
+                               size = -1;
+                       else
+                               size = param.GetActualSize ();
+
+                       if (IsLargeType (colType))
+                               Comm.Append ((short)size); 
+                       else if (IsBlobType (colType))
+                               Comm.Append (size); 
+                       else
+                               Comm.Append ((byte)size);
+                       
+                       if (size > 0) {
+                               switch (param.TypeName) {
+                               case "money" : {
+                                       Decimal val = (decimal) param.Value;
+                                       int[] arr = Decimal.GetBits (val);
+
+                                       if (val >= 0) {
+                                               Comm.Append (arr[1]);
+                                               Comm.Append (arr[0]);
+                                       } else {
+                                               Comm.Append (~arr[1]);
+                                               Comm.Append (~arr[0] + 1);
+                                       }
+                                       break;
+                               }
+                               case "smallmoney": {
+                                       Decimal val = (decimal) param.Value;
+                                       if (val < SMALLMONEY_MIN || val > SMALLMONEY_MAX)
+                                               throw new OverflowException (string.Format (
+                                                       CultureInfo.InvariantCulture,
+                                                       "Value '{0}' is not valid for SmallMoney."
+                                                       + "  Must be between {1:N4} and {2:N4}.",
+#if NET_2_0
+                                                       val,
+#else
+                                                       val.ToString (CultureInfo.CurrentCulture),
+#endif
+                                                       SMALLMONEY_MIN, SMALLMONEY_MAX));
+
+                                       int[] arr = Decimal.GetBits (val);
+                                       int sign = (val>0 ? 1: -1);
+                                       Comm.Append (sign * arr[0]);
+                                       break;
+                               }
+                               case "datetime":
+                                       Comm.Append ((DateTime)param.Value, 8);
+                                       break;
+                               case "smalldatetime":
+                                       Comm.Append ((DateTime)param.Value, 4);
+                                       break;
+                               case "varchar" :
+                               case "nvarchar" :
+                               case "char" :
+                               case "nchar" :
+                               case "text" :
+                               case "ntext" :
+                                       byte [] tmp = param.GetBytes ();
+                                       Comm.Append (tmp);
+                                       break;
+                               case "uniqueidentifier" :
+                                       Comm.Append (((Guid)param.Value).ToByteArray());
+                                       break;
+                               default :
+                                       Comm.Append (param.Value);
+                                       break;
+                               }
+                       }
+                       return;
                }
 
                public override void Execute (string commandText, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
                {
                        Parameters = parameters;
                        string sql = commandText;
-                       if (wantResults || (Parameters != null && Parameters.Count > 0))
-                               sql = BuildExec (commandText);
-                       ExecuteQuery (sql, timeout, wantResults);
-               }
-
-                private bool IsBlobType (TdsColumnType columnType)
-               {
-                       return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
-               }
 
-                private bool IsLargeType (TdsColumnType columnType)
-               {
-                       return (columnType == TdsColumnType.NChar || (byte) columnType > 128);
+                       if (Parameters != null && Parameters.Count > 0) {
+                               ExecRPC (TdsRpcProcId.ExecuteSql, commandText, parameters, timeout, wantResults);
+                       } else {
+                               if (wantResults)
+                                       sql = BuildExec (commandText);
+                               ExecuteQuery (sql, timeout, wantResults);
+                       }
                }
 
                private string FormatParameter (TdsMetaParameter parameter)
                {
+                       string parameterName = parameter.ParameterName;
+                       if (parameterName [0] == '@') {
+                               parameterName = parameterName.Substring (1);
+                       }
                        if (parameter.Direction == TdsParameterDirection.Output)
-                               return String.Format ("{0}={0} output", parameter.ParameterName);
-
+                               return String.Format ("@{0}=@{0} output", parameterName);
                        if (parameter.Value == null || parameter.Value == DBNull.Value)
-                               return parameter.ParameterName + "=NULL";
+                               return String.Format ("@{0}=NULL", parameterName);
 
                        string value = null;
                        switch (parameter.TypeName) {
-                        case "smalldatetime":
+                       case "smalldatetime":
                        case "datetime":
                                DateTime d = Convert.ToDateTime (parameter.Value);
-                               value = String.Format(System.Globalization.CultureInfo.InvariantCulture, 
-                                                      "'{0:MMM dd yyyy hh:mm:ss tt}'", d );
-                                break;
+                               value = String.Format (base.Locale,
+                                       "'{0:MMM dd yyyy hh:mm:ss.fff tt}'", d);
+                               break;
                        case "bigint":
                        case "decimal":
                        case "float":
@@ -465,14 +725,13 @@ namespace Mono.Data.Tds.Protocol {
                                value = String.Format ("N'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
                                break;
                        case "uniqueidentifier":
-                               value = String.Format ("'{0}'", ((Guid) parameter.Value).ToString (""));
+                               value = String.Format ("'{0}'", ((Guid) parameter.Value).ToString (string.Empty));
                                break;
                        case "bit":
                                if (parameter.Value.GetType () == typeof (bool))
                                        value = (((bool) parameter.Value) ? "0x1" : "0x0");
                                else
                                        value = parameter.Value.ToString ();
-
                                break;
                        case "image":
                        case "binary":
@@ -483,14 +742,14 @@ namespace Mono.Data.Tds.Protocol {
                                if (byteArray.Length == 0)
                                        value = "0x";
                                else
-                                       value = String.Format ("0x{0}", BitConverter.ToString (byteArray).Replace ("-", "").ToLower ());
+                                       value = String.Format ("0x{0}", BitConverter.ToString (byteArray).Replace ("-", string.Empty).ToLower ());
                                break;
                        default:
                                value = String.Format ("'{0}'", parameter.Value.ToString ().Replace ("'", "''"));
                                break;
                        }
 
-                       return parameter.ParameterName + "=" + value;
+                       return "@" + parameterName + "=" + value;
                }
 
                public override string Prepare (string commandText, TdsMetaParameterCollection parameters)
@@ -498,40 +757,38 @@ namespace Mono.Data.Tds.Protocol {
                        Parameters = parameters;
 
                        TdsMetaParameterCollection parms = new TdsMetaParameterCollection ();
-                       TdsMetaParameter parm = new TdsMetaParameter ("@P1", "int", null);
+                       TdsMetaParameter parm = new TdsMetaParameter ("@Handle", "int", null);
                        parm.Direction = TdsParameterDirection.Output;
                        parms.Add (parm);
 
-                       parms.Add (new TdsMetaParameter ("@P2", "nvarchar", BuildPreparedParameters ()));
-                       parms.Add (new TdsMetaParameter ("@P3", "nvarchar", commandText));
+                       parms.Add (new TdsMetaParameter ("@VarDecl", "nvarchar", BuildPreparedParameters ()));
+                       parms.Add (new TdsMetaParameter ("@Query", "nvarchar", commandText));
 
                        ExecProc ("sp_prepare", parms, 0, true);
-                       SkipToEnd ();   
-                       if (ColumnValues [0] == null || ColumnValues [0] == DBNull.Value)
-                               throw new TdsInternalException ();
-                       return ColumnValues [0].ToString ();
+                       SkipToEnd ();
+                       return OutputParameters[0].ToString () ;
+                       //if (ColumnValues == null || ColumnValues [0] == null || ColumnValues [0] == DBNull.Value)
+                       //      throw new TdsInternalException ();
+                       //return string.Empty;
+                       //return ColumnValues [0].ToString ();
                }
 
-               protected override TdsDataColumnCollection ProcessColumnInfo ()
+               protected override void ProcessColumnInfo ()
                {
-                       TdsDataColumnCollection result = new TdsDataColumnCollection ();
                        int numColumns = Comm.GetTdsShort ();
-
                        for (int i = 0; i < numColumns; i += 1) {
                                byte[] flagData = new byte[4];
                                for (int j = 0; j < 4; j += 1) 
                                        flagData[j] = Comm.GetByte ();
 
                                bool nullable = (flagData[2] & 0x01) > 0;
-                               bool caseSensitive = (flagData[2] & 0x02) > 0;
+                               //bool caseSensitive = (flagData[2] & 0x02) > 0;
                                bool writable = (flagData[2] & 0x0c) > 0;
                                bool autoIncrement = (flagData[2] & 0x10) > 0;
                                bool isIdentity = (flagData[2] & 0x10) > 0;
 
-                               TdsColumnType columnType = (TdsColumnType) (Comm.GetByte () & 0xff);
-                               if ((byte) columnType == 0xef)
-                                       columnType = TdsColumnType.NChar;
-                       
+                               TdsColumnType columnType = (TdsColumnType) ((Comm.GetByte () & 0xff));
+
                                byte xColumnType = 0;
                                if (IsLargeType (columnType)) {
                                        xColumnType = (byte) columnType;
@@ -545,47 +802,58 @@ namespace Mono.Data.Tds.Protocol {
                                if (IsBlobType (columnType)) {
                                        columnSize = Comm.GetTdsInt ();
                                        tableName = Comm.GetString (Comm.GetTdsShort ());
-                               }
-
-                               else if (IsFixedSizeColumn (columnType))
+                               } else if (IsFixedSizeColumn (columnType)) {
                                        columnSize = LookupBufferSize (columnType);
-                               else if (IsLargeType ((TdsColumnType) xColumnType))
+                               } else if (IsLargeType ((TdsColumnType) xColumnType)) {
                                        columnSize = Comm.GetTdsShort ();
-                               else
+                               } else {
                                        columnSize = Comm.GetByte () & 0xff;
+                               }
+
+                               if (IsWideType ((TdsColumnType) columnType))
+                                       columnSize /= 2;
 
                                byte precision = 0;
                                byte scale = 0;
 
-                               switch (columnType) {
-                               case TdsColumnType.NText:
-                               case TdsColumnType.NChar:
-                               case TdsColumnType.NVarChar:
-                                       columnSize /= 2;
-                                       break;
-                               case TdsColumnType.Decimal:
-                               case TdsColumnType.Numeric:
+                               if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
                                        precision = Comm.GetByte ();
                                        scale = Comm.GetByte ();
-                                       break;
+                               } else {
+                                       precision = GetPrecision (columnType, columnSize);
+                                       scale = GetScale (columnType, columnSize);
                                }
 
                                string columnName = Comm.GetString (Comm.GetByte ());
 
-                               int index = result.Add (new TdsDataColumn ());
-                               result[index]["AllowDBNull"] = nullable;
-                               result[index]["ColumnName"] = columnName;
-                               result[index]["ColumnSize"] = columnSize;
-                               result[index]["ColumnType"] = columnType;
-                               result[index]["IsAutoIncrement"] = autoIncrement;
-                               result[index]["IsIdentity"] = isIdentity;
-                               result[index]["IsReadOnly"] = !writable;
-                               result[index]["NumericPrecision"] = precision;
-                               result[index]["NumericScale"] = scale;
-                               result[index]["BaseTableName"] = tableName;
+                               TdsDataColumn col = new TdsDataColumn ();
+                               Columns.Add (col);
+#if NET_2_0
+                               col.ColumnType = columnType;
+                               col.ColumnName = columnName;
+                               col.IsAutoIncrement = autoIncrement;
+                               col.IsIdentity = isIdentity;
+                               col.ColumnSize = columnSize;
+                               col.NumericPrecision = precision;
+                               col.NumericScale = scale;
+                               col.IsReadOnly = !writable;
+                               col.AllowDBNull = nullable;
+                               col.BaseTableName = tableName;
+                               col.DataTypeName = Enum.GetName (typeof (TdsColumnType), xColumnType);
+#else
+                               col ["ColumnType"] = columnType;
+                               col ["ColumnName"] = columnName;
+                               col ["IsAutoIncrement"] = autoIncrement;
+                               col ["IsIdentity"] = isIdentity;
+                               col ["ColumnSize"] = columnSize;
+                               col ["NumericPrecision"] = precision;
+                               col ["NumericScale"] = scale;
+                               col ["IsReadOnly"] = !writable;
+                               col ["AllowDBNull"] = nullable;
+                               col ["BaseTableName"] = tableName;
+                               col ["DataTypeName"] = Enum.GetName (typeof (TdsColumnType), xColumnType);
+#endif
                        }
-
-                       return result;
                }
 
                public override void Unprepare (string statementId)
@@ -602,76 +870,214 @@ namespace Mono.Data.Tds.Protocol {
                        return true; 
                }
 
+               protected override void ProcessReturnStatus ()
+               {
+                       int result = Comm.GetTdsInt ();
+                       if (Parameters != null) {
+                               foreach (TdsMetaParameter param in Parameters) {
+                                       if (param.Direction == TdsParameterDirection.ReturnValue) {
+                                               param.Value = result;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               byte GetScale (TdsColumnType type, int columnSize)
+               {
+                       switch (type) {
+                       case TdsColumnType.DateTime:
+                               return 0x03;
+                       case TdsColumnType.DateTime4:
+                               return 0x00;
+                       case TdsColumnType.DateTimeN:
+                               switch (columnSize) {
+                               case 4:
+                                       return 0x00;
+                               case 8:
+                                       return 0x03;
+                               }
+                               break;
+                       default:
+                               return 0xff;
+                       }
+
+                       throw new NotSupportedException (string.Format (
+                               CultureInfo.InvariantCulture,
+                               "Fixed scale not defined for column " +
+                               "type '{0}' with size {1}.", type, columnSize));
+               }
+
+               byte GetPrecision (TdsColumnType type, int columnSize)
+               {
+                       switch (type) {
+                       case TdsColumnType.Binary:
+                               return 0xff;
+                       case TdsColumnType.Bit:
+                               return 0xff;
+                       case TdsColumnType.Char:
+                               return 0xff;
+                       case TdsColumnType.DateTime:
+                               return 0x17;
+                       case TdsColumnType.DateTime4:
+                               return 0x10;
+                       case TdsColumnType.DateTimeN:
+                               switch (columnSize) {
+                               case 4:
+                                       return 0x10;
+                               case 8:
+                                       return 0x17;
+                               }
+                               break;
+                       case TdsColumnType.Real:
+                               return 0x07;
+                       case TdsColumnType.Float8:
+                               return 0x0f;
+                       case TdsColumnType.FloatN:
+                               switch (columnSize) {
+                               case 4:
+                                       return 0x07;
+                               case 8:
+                                       return 0x0f;
+                               }
+                               break;
+                       case TdsColumnType.Image:
+                               return 0xff;
+                       case TdsColumnType.Int1:
+                               return 0x03;
+                       case TdsColumnType.Int2:
+                               return 0x05;
+                       case TdsColumnType.Int4:
+                               return 0x0a;
+                       case TdsColumnType.IntN:
+                               switch (columnSize) {
+                               case 1:
+                                       return 0x03;
+                               case 2:
+                                       return 0x05;
+                               case 4:
+                                       return 0x0a;
+                               }
+                               break;
+                       case TdsColumnType.Void:
+                               return 0x01;
+                       case TdsColumnType.Text:
+                               return 0xff;
+                       case TdsColumnType.UniqueIdentifier:
+                               return 0xff;
+                       case TdsColumnType.VarBinary:
+                               return 0xff;
+                       case TdsColumnType.VarChar:
+                               return 0xff;
+                       case TdsColumnType.Money:
+                               return 19;
+                       case TdsColumnType.NText:
+                               return 0xff;
+                       case TdsColumnType.NVarChar:
+                               return 0xff;
+                       case TdsColumnType.BitN:
+                               return 0xff;
+                       case TdsColumnType.MoneyN:
+                               switch (columnSize) {
+                               case 4:
+                                       return 0x0a;
+                               case 8:
+                                       return 0x13;
+                               }
+                               break;
+                       case TdsColumnType.Money4:
+                               return 0x0a;
+                       case TdsColumnType.NChar:
+                               return 0xff;
+                       case TdsColumnType.BigBinary:
+                               return 0xff;
+                       case TdsColumnType.BigVarBinary:
+                               return 0xff;
+                       case TdsColumnType.BigVarChar:
+                               return 0xff;
+                       case TdsColumnType.BigNVarChar:
+                               return 0xff;
+                       case TdsColumnType.BigChar:
+                               return 0xff;
+                       case TdsColumnType.SmallMoney:
+                               return 0x0a;
+                       case TdsColumnType.Variant:
+                               return 0xff;
+                       case TdsColumnType.BigInt:
+                               return 0xff;
+                       }
+
+                       throw new NotSupportedException (string.Format (
+                               CultureInfo.InvariantCulture,
+                               "Fixed precision not defined for column " +
+                               "type '{0}' with size {1}.", type, columnSize));
+               }
+
                #endregion // Methods
 
 #if NET_2_0
-                #region Asynchronous Methods
-                public override IAsyncResult BeginExecuteNonQuery (string cmdText,
+               #region Asynchronous Methods
+
+               public override IAsyncResult BeginExecuteNonQuery (string cmdText,
                                                           TdsMetaParameterCollection parameters,
                                                           AsyncCallback callback,
                                                           object state)
-                {
-                        Parameters = parameters;
-                        string sql = cmdText;
+               {
+                       Parameters = parameters;
+                       string sql = cmdText;
                        if (Parameters != null && Parameters.Count > 0)
                                sql = BuildExec (cmdText);
 
-                        IAsyncResult ar = BeginExecuteQueryInternal (sql, false, callback, state);
-                        return ar;
-                }
+                       IAsyncResult ar = BeginExecuteQueryInternal (sql, false, callback, state);
+                       return ar;
+               }
 
-                public override void EndExecuteNonQuery (IAsyncResult ar)
-                {
-                        EndExecuteQueryInternal (ar);
-                }
+               public override void EndExecuteNonQuery (IAsyncResult ar)
+               {
+                       EndExecuteQueryInternal (ar);
+               }
 
-                public override IAsyncResult BeginExecuteQuery (string cmdText,
+               public override IAsyncResult BeginExecuteQuery (string cmdText,
                                                                 TdsMetaParameterCollection parameters,
                                                                 AsyncCallback callback,
                                                                 object state)
-                {
-                        Parameters = parameters;
-                        string sql = cmdText;
+               {
+                       Parameters = parameters;
+                       string sql = cmdText;
                        if (Parameters != null && Parameters.Count > 0)
                                sql = BuildExec (cmdText);
 
-                        IAsyncResult ar = BeginExecuteQueryInternal (sql, true, callback, state);
-                        return ar;
-                }
-
-                public override void EndExecuteQuery (IAsyncResult ar)
-                {
-                        EndExecuteQueryInternal (ar);
-                }
+                       IAsyncResult ar = BeginExecuteQueryInternal (sql, true, callback, state);
+                       return ar;
+               }
 
+               public override void EndExecuteQuery (IAsyncResult ar)
+               {
+                       EndExecuteQueryInternal (ar);
+               }
 
-                public override IAsyncResult BeginExecuteProcedure (string prolog,
+               public override IAsyncResult BeginExecuteProcedure (string prolog,
                                                                     string epilog,
                                                                     string cmdText,
                                                                     bool IsNonQuery,
                                                                     TdsMetaParameterCollection parameters,
                                                                     AsyncCallback callback,
                                                                     object state)
-                {
-
-                        
-                        Parameters = parameters;
+               {
+                       Parameters = parameters;
                        string pcall = BuildProcedureCall (cmdText);
-                        string sql = String.Format ("{0};{1};{2};", prolog, pcall, epilog);
-
-                        IAsyncResult ar = BeginExecuteQueryInternal (sql, !IsNonQuery, callback, state);
-                        return ar;
-                }
-
-                public override void EndExecuteProcedure (IAsyncResult ar)
-                {
-                        EndExecuteQueryInternal (ar);
-                }
+                       string sql = String.Format ("{0};{1};{2};", prolog, pcall, epilog);
 
+                       IAsyncResult ar = BeginExecuteQueryInternal (sql, !IsNonQuery, callback, state);
+                       return ar;
+               }
 
+               public override void EndExecuteProcedure (IAsyncResult ar)
+               {
+                       EndExecuteQueryInternal (ar);
+               }
 
-                #endregion // Asynchronous Methods
+               #endregion // Asynchronous Methods
 #endif // NET_2_0
-
        }
 }