2002-10-22 Tim Coleman (tim@timcoleman.com)
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsClient.Internal / Tds.cs
index 00072b60131512ebe00b2a6ede1fa1b4883f229a..38bef16d1b511b990abf8c38c64591d158368000 100644 (file)
@@ -18,15 +18,41 @@ namespace Mono.Data.TdsClient.Internal {
        {
                #region Fields
 
+               TdsVersion tdsVersion;
+
+               string applicationName;
+               string database = String.Empty;
+               string connectDB;
+               string charset;
+               string hostname;
+               string server;
+               string language;
+               string libraryName;
+               int packetSize;
+               string password;
+               int port;
+               string progName;
+               string user;
+
+               string databaseProductName;
+               string databaseProductVersion;
+               int databaseMajorVersion;
+
+               DataTable table = new DataTable ();
+
+               bool moreResults;
+               bool moreResults2;
+
+               TdsMessage lastServerMessage;
+
+               Encoding encoder;
                TdsServerType serverType;
                TdsComm comm;
-               TdsVersion tdsVersion;
                TdsConnectionParameters parms;
                //TdsCommand command;
-               Encoding encoding;
                IsolationLevel isolationLevel;
                bool autoCommit;
-                Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
+                Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
                #endregion // Fields
 
@@ -38,13 +64,12 @@ namespace Mono.Data.TdsClient.Internal {
                //}
 
                public string Database {
-                       get { return parms.Database; }
-                       set { parms.Database = value; }
+                       get { return database; }
+                       set { database = value; }
                }
 
                public TdsVersion TdsVersion {
                        get { return tdsVersion; }
-                       set { tdsVersion = value; }
                }
 
                #endregion // Properties
@@ -53,69 +78,911 @@ namespace Mono.Data.TdsClient.Internal {
 
                public Tds (TdsConnectionParameters parms)
                {
-                        IPHostEntry hostEntry = Dns.GetHostByName (parms.Host);
-                        IPAddress[] addresses = hostEntry.AddressList;
-
-                        IPEndPoint endPoint;
-
-                        foreach (IPAddress address in addresses) {
-                                endPoint = new IPEndPoint (address, parms.Port);
-                                socket.Connect (endPoint);
-
-                                if (socket.Connected)
-                                        break;
-                        }
-
-                       encoding = Encoding.GetEncoding (parms.Encoding);
-                       comm = new TdsComm (socket, parms.PacketSize, tdsVersion);
+                       applicationName = parms.ApplicationName;
+                       connectDB = parms.Database;
+                       encoder = Encoding.GetEncoding (parms.Encoding);
+                       charset = parms.Encoding;
+                       hostname = parms.Hostname;
+                       server = parms.DataSource;
+                       language = parms.Language;
+                       libraryName = parms.LibraryName;
+                       packetSize = parms.PacketSize;
+                       password = parms.Password;
+                       port = parms.Port;
+                       progName = parms.ProgName;
+                       tdsVersion = parms.TdsVersion;
+                       user = parms.User;
+
+                       IPHostEntry hostEntry = Dns.Resolve (server);
+                       IPAddress[] addresses = hostEntry.AddressList;
+
+                       IPEndPoint endPoint;
+
+                       foreach (IPAddress address in addresses) {
+                               endPoint = new IPEndPoint (address, port);
+                               socket.Connect (endPoint);
+
+                               if (socket.Connected)
+                                       break;
+                       }
+       
+                       comm = new TdsComm (encoder, socket, packetSize, tdsVersion);
                }       
 
                #endregion // Constructors
 
                #region Methods
-
+               
+               public void BeginTransaction ()
+               {
+                       SubmitProcedure ("BEGIN TRANSACTION");
+               }
                public void ChangeDatabase (string databaseName)
                {
-                        string query = String.Format ("use {0}", databaseName);
-                        comm.StartPacket (TdsPacketType.Query);
-                        if (tdsVersion == TdsVersion.tds70)
-                                comm.AppendChars (query);
-                        else {
-                                byte[] queryBytes = encoding.GetBytes (query);
-                                comm.AppendBytes (queryBytes, queryBytes.Length, (byte) 0);
-                        }
-                        comm.SendPacket ();
+                       TdsPacketResult result;
+                       bool isOkay;
+
+                       string query = String.Format ("use {0}", databaseName);
+                       comm.StartPacket (TdsPacketType.Query);
+                       comm.Append (query);
+                       comm.SendPacket ();
+
+                       while (!((result = ProcessSubPacket ()) is TdsPacketEndTokenResult)) {
+                               if (result is TdsPacketErrorResult) {
+                                       isOkay = false;
+                               }
+                       }
                }
 
                public void ChangeSettings (bool autoCommit, IsolationLevel isolationLevel)
                {
-                       string query = SqlStatementForSettings (autoCommit, isolationLevel);
+                       /*string query = SqlStatementForSettings (autoCommit, isolationLevel);
                        if (query != null)
                                ChangeSettings (query);
+                               */
                }
 
                private bool ChangeSettings (string query)
                {
+                       TdsPacketResult result;
                        bool isOkay = true;
-                       ifquery.Length == 0)
+                       if (query.Length == 0)
                                return true;
 
                        comm.StartPacket (TdsPacketType.Query);
-                       if (tdsVersion == TdsVersion.tds70)
-                               comm.AppendChars (query);
-                       else {
-                               byte[] queryBytes = encoding.GetBytes (query);
-                               comm.AppendBytes (queryBytes, queryBytes.Length, (byte) 0);
-                       }
+                       comm.Append (query);
                        comm.SendPacket ();
 
+                       bool done = false;
+                       while (!done) {
+                               result = ProcessSubPacket ();
+                               done = (result is TdsPacketEndTokenResult) && !((TdsPacketEndTokenResult) result).MoreResults;
+                               if (result is TdsPacketErrorResult) {
+                                       done = true;
+                                       isOkay = false;
+                               }
+                       }
+
                        return isOkay;
                }
 
+               public void CommitTransaction ()
+               {
+                       string sql = "IF @@TRANCOUNT>0 COMMIT TRAN";
+                       SubmitProcedure (sql);
+               }
+
+               [MonoTODO ("fixme")]
+               public int ExecuteNonQuery (string sql)
+               {
+                       TdsPacketResult result;
+                       bool done = false;
+
+                       if (sql.Length > 0) {
+                               comm.StartPacket (TdsPacketType.Query);
+                               comm.Append (sql);
+                               moreResults2 = true;
+                               comm.SendPacket ();
+                       }
+
+                       do {
+                               result = ProcessSubPacket ();
+                               if (result is TdsPacketMessageResult) {
+                                       Console.WriteLine (((TdsPacketMessageResult) result).Message);
+                               }
+                               done = (result is TdsPacketEndTokenResult) && (!((TdsPacketEndTokenResult) result).MoreResults);
+                               
+                       } while (!done);
+
+                       return -1;
+               }
+
+               [MonoTODO ("fixme")]
+               public void ExecuteQuery (string sql)
+               {
+                       if (sql.Length > 0) {
+                               comm.StartPacket (TdsPacketType.Query);
+                               comm.Append (sql);
+                               moreResults2 = true;
+                               comm.SendPacket ();
+                       }
+               }
+
+               private object GetCharValue (bool wideChars, bool outputParam)
+               {
+                       object result = null;
+                       bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam);
+                       int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
+
+                       if ((tdsVersion < TdsVersion.tds70 && len == 0) || (tdsVersion == TdsVersion.tds70 && len == 0xffff))
+                               result = null;
+                       else if (len >= 0) {
+                               if (wideChars)
+                                       result = comm.GetString (len / 2);
+                               else
+                                       result = encoder.GetString (comm.GetBytes (len, false), 0, len);
+
+                               if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
+                                       result = "";
+                       }
+                       else
+                               throw new TdsException ("");
+                       return result;
+               }
+
+               [MonoTODO]
+               private object GetDecimalValue (int scale)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               [MonoTODO]
+               private object GetDateTimeValue (TdsColumnType type)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               private object GetImageValue ()
+               {
+                       byte[] result;
+                       byte hasValue = comm.GetByte ();
+                       if (hasValue == 0)
+                               return null;
+                       
+                       comm.Skip (24);
+                       int len = comm.GetTdsInt ();
+                       if (len >= 0) 
+                               result = comm.GetBytes (len, true);
+                       else
+                               throw new TdsException ("");
+                       return result;
+               }
+
+               private object GetIntValue (TdsColumnType type)
+               {
+                       object result;
+                       int len;
+
+                       switch (type) {
+                       case TdsColumnType.IntN :
+                               len = comm.GetByte ();
+                               break;
+                       case TdsColumnType.Int4 :
+                               len = 4; 
+                               break;
+                       case TdsColumnType.Int2 :
+                               len = 2; 
+                               break;
+                       case TdsColumnType.Int1 :
+                               len = 1; 
+                               break;
+                       default:
+                               throw new TdsException ("");
+                       }
+
+                       switch (len) {
+                       case 4 :
+                               result = comm.GetTdsInt ();
+                               break;
+                       case 2 :
+                               result = comm.GetTdsShort ();
+                               break;
+                       case 1 :
+                               result = (byte) comm.GetByte ();
+                               break;
+                       case 0 :
+                               result = null;
+                               break;
+                       default:
+                               throw new TdsException ("Bad integer length");
+                       }
+
+                       return result;
+               }
+
+               [MonoTODO]
+               private object GetMoneyValue (TdsColumnType type)
+               {
+                       int len;
+                       object result;
+
+                       switch (type) {
+                       case TdsColumnType.SmallMoney :
+                       case TdsColumnType.Money4 :
+                               len = 4;
+                               break;
+                       case TdsColumnType.Money :
+                               len = 8;
+                               break;
+                       case TdsColumnType.MoneyN :
+                               len = comm.GetByte ();
+                               break;
+                       default:
+                               throw new TdsException ("not a money value");
+                       }
+
+                       if (len == 0)
+                               result = null;
+                       else {
+                               throw new NotImplementedException ();
+                       }
+
+                       return result;
+               }
+
+               private int GetSubPacketLength ()
+               {
+                       return comm.GetTdsShort ();
+               }
+
+               private object GetTextValue (bool wideChars)
+               {
+                       string result;
+                       byte hasValue = comm.GetByte ();
+
+                       if (hasValue == 0)
+                               return null;
+
+                       comm.Skip (24);
+                       int len = comm.GetTdsInt ();
+
+                       if (len >= 0) {
+                               if (wideChars)
+                                       result = comm.GetString (len / 2);
+                               else
+                                       result = encoder.GetString (comm.GetBytes (len, false), 0, len);
+
+                               if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
+                                       result = "";
+                       } 
+                       else 
+                               throw new TdsException ("");
+
+                       return result;
+               }
+
+               private bool IsFixedSizeColumn (TdsColumnType columnType)
+               {
+                       switch (columnType) {
+                               case TdsColumnType.Int1 :
+                               case TdsColumnType.Int2 :
+                               case TdsColumnType.Int4 :
+                               case TdsColumnType.Float8 :
+                               case TdsColumnType.DateTime :
+                               case TdsColumnType.Bit :
+                               case TdsColumnType.Money :
+                               case TdsColumnType.Money4 :
+                               case TdsColumnType.SmallMoney :
+                               case TdsColumnType.Real :
+                               case TdsColumnType.DateTime4 :
+                                       return true;
+                               case TdsColumnType.IntN :
+                               case TdsColumnType.MoneyN :
+                               case TdsColumnType.VarChar :
+                               case TdsColumnType.NVarChar :
+                               case TdsColumnType.DateTimeN :
+                               case TdsColumnType.FloatN :
+                               case TdsColumnType.Char :
+                               case TdsColumnType.NChar :
+                               case TdsColumnType.NText :
+                               case TdsColumnType.Image :
+                               case TdsColumnType.VarBinary :
+                               case TdsColumnType.Binary :
+                               case TdsColumnType.Decimal :
+                               case TdsColumnType.Numeric :
+                               case TdsColumnType.BitN :
+                               case TdsColumnType.UniqueIdentifier :
+                                       return false;
+                               default :
+                                       throw new TdsException ("bad type");
+                       }
+               }
+
+               [MonoTODO]
+               private TdsPacketRowResult LoadRow (TdsPacketRowResult result)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               private int LookupBufferSize (TdsColumnType columnType)
+               {
+                       switch (columnType) {
+                               case TdsColumnType.Int1 :
+                               case TdsColumnType.Bit :
+                                       return 1;
+                               case TdsColumnType.Int2 :
+                                       return 2;
+                               case TdsColumnType.Int4 :
+                               case TdsColumnType.Real :
+                               case TdsColumnType.DateTime4 :
+                               case TdsColumnType.Money4 :
+                               case TdsColumnType.SmallMoney :
+                                       return 4;
+                               case TdsColumnType.Float8 :
+                               case TdsColumnType.DateTime :
+                               case TdsColumnType.Money :
+                                       return 8;
+                               default :
+                                       throw new TdsException ("");
+                       }
+               }
+
+               private int LookupDisplaySize (TdsColumnType columnType) 
+               {
+                       switch (columnType) {
+                               case TdsColumnType.Int1 :
+                                       return 3;
+                               case TdsColumnType.Int2 :
+                                       return 6;
+                               case TdsColumnType.Int4 :
+                                       return 11;
+                               case TdsColumnType.Real :
+                                       return 14;
+                               case TdsColumnType.Float8 :
+                                       return 24;
+                               case TdsColumnType.DateTime :
+                                       return 23;
+                               case TdsColumnType.DateTime4 :
+                                       return 16;
+                               case TdsColumnType.Bit :
+                                       return 1;
+                               case TdsColumnType.Money :
+                                       return 21;
+                               case TdsColumnType.Money4 :
+                               case TdsColumnType.SmallMoney :
+                                       return 12;
+                               default:
+                                       throw new TdsException ("");
+                       }
+               }
+
+               private TdsPacketColumnNamesResult ProcessColumnNames ()
+               {
+                       int totalLength = comm.GetTdsShort ();
+                       int bytesRead = 0;
+                       int i = 0;
+
+                       bool newTable = (table.Columns.Count > 0);
+
+                       while (bytesRead < totalLength) {
+                               int columnNameLength = comm.GetByte ();
+                               string columnName = encoder.GetString (comm.GetBytes (columnNameLength, false), 0, columnNameLength);
+                               bytesRead = bytesRead + 1 + columnNameLength;
+                               if (newTable)
+                                       table.Columns.Add (columnName);
+                               else
+                                       table.Columns[i].ColumnName = columnName;
+                               i += 1;
+                       }
+
+                       return new TdsPacketColumnNamesResult (table.Columns);
+               }
+
+               private TdsPacketColumnInfoResult ProcessColumnInfo ()
+               {
+                       int precision;
+                       int scale;
+                       int totalLength = comm.GetTdsShort ();
+                       int bytesRead = 0;
+                       int numColumns = 0;
+
+                       bool newTable = (table.Columns.Count > 0);
+
+                       while (bytesRead < totalLength) {
+                               scale = -1;
+                               precision = -1;
+
+                               int bufLength = -1;
+                               int dispSize = -1;
+                               byte[] flagData = new byte[4];
+                               for (int i = 0; i < 4; i += 1) {
+                                       flagData[i] = comm.GetByte ();
+                                       bytesRead += 1;
+                               }
+                               bool nullable = (flagData[2] & 0x01) > 0;
+                               bool caseSensitive = (flagData[2] & 0x02) > 0;
+                               bool writable = (flagData[2] & 0x0c) > 0;
+                               bool autoIncrement = (flagData[2] & 0x10) > 0;
+                               string tableName = String.Empty;
+                               TdsColumnType columnType = (TdsColumnType) comm.GetByte ();
+                               bytesRead += 1;
+
+                               if (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image) {
+                                       comm.Skip (4);
+                                       bytesRead += 4;
+
+                                       int tableNameLength = comm.GetTdsShort ();
+                                       bytesRead += 2;
+                                       tableName = encoder.GetString (comm.GetBytes (tableNameLength, false), 0, tableNameLength);
+                                       bytesRead += tableNameLength;
+                                       bufLength = 2 << 31 - 1;
+                               }
+                               else if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
+                                       bufLength = comm.GetByte ();
+                                       bytesRead += 1;
+                                       precision = comm.GetByte ();
+                                       bytesRead += 1;
+                                       scale = comm.GetByte ();
+                                       bytesRead += 1;
+                               }
+                               else if (IsFixedSizeColumn (columnType))
+                                       bufLength = LookupBufferSize (columnType);
+                               else {
+                                       bufLength = (int) comm.GetByte () & 0xff;
+                                       bytesRead += 1;
+                               }
+
+                               DataColumn column;
+
+                               if (newTable) 
+                                       column = table.Columns.Add ();
+                               else
+                                       column = table.Columns[numColumns];
+
+                               numColumns += 1;
+
+                               column.AllowDBNull = nullable;
+                               column.AutoIncrement = autoIncrement;
+                               column.ReadOnly = !writable;
+                       }
+
+                       int skipLength = totalLength - bytesRead;
+                       if (skipLength != 0)
+                               throw new TdsException ("skipping");
+                       return new TdsPacketColumnInfoResult (table.Columns);
+               }
+
+               [MonoTODO]
+               private TdsPacketEndTokenResult ProcessEndToken (TdsPacketSubType type)
+               {
+                       byte status = comm.GetByte ();
+                       comm.GetByte ();
+                       byte op = comm.GetByte ();
+                       comm.GetByte ();
+                       int rowCount = comm.GetTdsInt ();
+                       if (op == (byte) 0xc1) 
+                               rowCount = 0;
+
+                       if (type == TdsPacketSubType.DoneInProc) 
+                               rowCount = -1;
+
+                       TdsPacketEndTokenResult result = new TdsPacketEndTokenResult (type, status, rowCount);
+
+                       Console.WriteLine ("Row count {0}", rowCount);
+
+                       moreResults = result.MoreResults;
+
+                       // FIXME: Finish the query
+
+                       return result;
+               }
+
+               private TdsPacketResult ProcessEnvChange ()
+               {
+                       int len = GetSubPacketLength ();
+                       TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
+                       int cLen;
+
+                       switch (type) {
+                       case TdsEnvPacketSubType.BlockSize :
+                               string blockSize;
+                               cLen = comm.GetByte () & 0xff;
+                               if (tdsVersion == TdsVersion.tds70) {
+                                       blockSize = comm.GetString (cLen);
+                                       comm.Skip (len - 2 - cLen * 2);
+                               }
+                               else {
+                                       blockSize = encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
+                                       comm.Skip (len - 2 - cLen);
+                               }
+
+                               comm.ResizeOutBuf (Int32.Parse (blockSize));
+                               break;
+                       case TdsEnvPacketSubType.CharSet :
+                               cLen = comm.GetByte () & 0xff;
+                               if (tdsVersion == TdsVersion.tds70) {
+                                       this.language = comm.GetString (cLen);
+                                       comm.Skip (len - 2 - cLen * 2);
+                               }
+                               else {
+                                       this.charset = encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
+                                       comm.Skip (len - 2 - cLen);
+                                       SetCharset (charset);
+                               }
+
+                               break;
+                       case TdsEnvPacketSubType.Database :
+                               cLen = comm.GetByte () & 0xff;
+                               string newDB = tdsVersion == TdsVersion.tds70 ? comm.GetString (cLen) : encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
+                               cLen = comm.GetByte () & 0xff;
+                               string oldDB = tdsVersion == TdsVersion.tds70 ? comm.GetString (cLen) : encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
+                               if (database != String.Empty && database != oldDB)
+                                       throw new TdsException ("Database mismatch.");
+                               database = newDB;
+                               break;
+                       default:
+                               comm.Skip (len - 1);
+                               break;
+                       }
+
+                       return new TdsPacketResult (TdsPacketSubType.EnvChange);
+               }
+
+               private TdsPacketResult ProcessLoginAck ()
+               {
+                       GetSubPacketLength ();
+
+                       if (tdsVersion == TdsVersion.tds70) {
+                               comm.Skip (5);
+                               int nameLength = comm.GetByte ();
+                               databaseProductName = comm.GetString (nameLength);
+                               databaseMajorVersion = comm.GetByte ();
+                               databaseProductVersion = String.Format ("0{0}.0{1}.0{2}", databaseMajorVersion, comm.GetByte (), ((256 * (comm.GetByte () + 1)) + comm.GetByte ()));
+                       }
+                       else {
+                               comm.Skip (5);
+                               short nameLength = comm.GetByte ();
+                               databaseProductName = comm.GetString (nameLength);
+                               comm.Skip (1);
+                               databaseMajorVersion = comm.GetByte ();
+                               databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
+                               comm.Skip (1);
+                       }
+                       Console.WriteLine ("Connected to {0} {1}", databaseProductName, databaseProductVersion);
+
+                       if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
+                               int last = databaseProductName.IndexOf ('\0');
+                               databaseProductName = databaseProductName.Substring (0, last);
+                       }
+
+                       return new TdsPacketResult (TdsPacketSubType.LoginAck);
+               }
+
+               private TdsPacketMessageResult ProcessMessage (TdsPacketSubType subType)
+               {
+                       TdsMessage message = new TdsMessage ();
+                       GetSubPacketLength ();
+                       int len;
+
+                       message.Number = comm.GetTdsInt ();
+                       message.State = comm.GetByte ();
+                       message.Severity = comm.GetByte ();
+                       
+                       len = comm.GetTdsShort ();
+
+                       message.Message = comm.GetString (len);
+
+                       len = comm.GetByte ();
+
+                       message.Server = comm.GetString (len);
+
+                       if (subType == TdsPacketSubType.Error | subType == TdsPacketSubType.Info) {
+                               len = comm.GetByte ();
+                               message.ProcName = comm.GetString (len);
+                       }
+                       else 
+                               throw new TdsException ("Invalid subtype");
+                       message.Line = comm.GetByte ();
+                       comm.GetByte ();
+
+                       Console.WriteLine (message.ToString ());
+
+                       lastServerMessage = message;
+                       if (subType == TdsPacketSubType.Error)
+                               return new TdsPacketErrorResult (subType, message);
+                       else
+                               return new TdsPacketMessageResult (subType, message);
+               }
+
+               private TdsPacketOutputParam ProcessOutputParam ()
+               {
+                       GetSubPacketLength ();
+                       comm.GetString (comm.GetByte () & 0xff);
+                       comm.Skip (5);
+                       TdsColumnType colType = (TdsColumnType) comm.GetByte ();
+                       int len;
+
+                       object element = null;
+                       switch (colType) {
+                       case TdsColumnType.IntN :
+                               comm.GetByte ();
+                               element = GetIntValue (colType);
+                               break;
+                       case TdsColumnType.Int1 :
+                       case TdsColumnType.Int2 :
+                       case TdsColumnType.Int4 :
+                               element = GetIntValue (colType);
+                               break;
+                       case TdsColumnType.Image :
+                               comm.GetByte ();
+                               element = GetImageValue ();
+                               break;
+                       case TdsColumnType.Text :
+                               comm.GetByte ();
+                               element = GetTextValue (false);
+                               break;
+                       case TdsColumnType.NText :
+                               comm.GetByte ();
+                               element = GetTextValue (true);
+                               break;
+                       case TdsColumnType.Char :
+                       case TdsColumnType.VarChar :
+                               comm.GetByte ();
+                               element = GetCharValue (false, true);
+                               break;
+                       case TdsColumnType.BigVarBinary :
+                               comm.GetTdsShort ();
+                               len = comm.GetTdsShort ();
+                               element = comm.GetBytes (len, true);
+                               break;
+                       case TdsColumnType.BigVarChar :
+                               comm.GetTdsShort ();
+                               element = GetCharValue (false, false);
+                               break;
+                       case TdsColumnType.NChar :
+                       case TdsColumnType.NVarChar :
+                               comm.GetByte ();
+                               element = GetCharValue (true, true);
+                               break;
+                       case TdsColumnType.Real :
+                               element = ReadFloatN (4);
+                               break;
+                       case TdsColumnType.Float8 :
+                               element = ReadFloatN (8);
+                               break;
+                       case TdsColumnType.FloatN :
+                               comm.GetByte ();
+                               int actualSize = comm.GetByte ();
+                               element = ReadFloatN (actualSize);
+                               break;
+                       case TdsColumnType.SmallMoney :
+                       case TdsColumnType.Money :
+                       case TdsColumnType.MoneyN :
+                               comm.GetByte ();
+                               element = GetMoneyValue (colType);
+                               break;
+                       case TdsColumnType.Numeric :
+                       case TdsColumnType.Decimal :
+                               comm.GetByte ();
+                               comm.GetByte ();
+                               int scale = comm.GetByte ();
+                               element = GetDecimalValue (scale);
+                               break;
+                       case TdsColumnType.DateTimeN :
+                               comm.GetByte ();
+                               element = GetDateTimeValue (colType);
+                               break;
+                       case TdsColumnType.DateTime4 :
+                       case TdsColumnType.DateTime :
+                               element = GetDateTimeValue (colType);
+                               break;
+                       case TdsColumnType.VarBinary :
+                       case TdsColumnType.Binary :
+                               comm.GetByte ();
+                               len = (comm.GetByte () & 0xff);
+                               element = comm.GetBytes (len, true);
+                               break;
+                       case TdsColumnType.BitN :
+                               comm.GetByte ();
+                               if (comm.GetByte () == 0)
+                                       element = null;
+                               else
+                                       element = (comm.GetByte() != 0);
+                               break;
+                       case TdsColumnType.Bit :
+                               int columnSize = comm.GetByte ();
+                               element = (columnSize != 0);
+                               break;
+                       case TdsColumnType.UniqueIdentifier :
+                               len = comm.GetByte () & 0xff;
+                               //element = (len == 0 ? null : new Guid (comm.GetBytes (len, false)));
+                               break;
+                       default :
+                               throw new TdsException ("");
+                       }
+
+                       return new TdsPacketOutputParam (element);
+               }
+
+               private TdsPacketResult ProcessProcId ()
+               {
+                       comm.Skip (8);
+                       return new TdsPacketResult (TdsPacketSubType.ProcId);
+               }
+
+               private TdsPacketRetStatResult ProcessReturnStatus ()
+               {
+                       return new TdsPacketRetStatResult (comm.GetTdsInt ());
+               }
+
+               [MonoTODO]
+               private TdsPacketResult ProcessSubPacket ()
+               {
+                       TdsPacketResult result = null;
+                       moreResults = false;
+
+                       TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
+
+                       switch (subType) {
+                       case TdsPacketSubType.EnvChange :
+Console.WriteLine ("Environment change");
+                               result = ProcessEnvChange ();
+                               break;
+                       case TdsPacketSubType.Error :
+                       case TdsPacketSubType.Info :
+                       case TdsPacketSubType.Msg50Token :
+Console.WriteLine ("Error/info/message");
+                               result = ProcessMessage (subType);
+                               break;
+                       case TdsPacketSubType.Param :
+Console.WriteLine ("Param");
+                               result = ProcessOutputParam ();
+                               break;
+                       case TdsPacketSubType.LoginAck :
+Console.WriteLine ("Login Ack");
+                               result = ProcessLoginAck ();
+                               break;
+                       case TdsPacketSubType.ReturnStatus :
+Console.WriteLine ("Return Status");
+                               result = ProcessReturnStatus ();
+                               break;
+                       case TdsPacketSubType.ProcId :
+Console.WriteLine ("Proc Id");
+                               result = ProcessProcId ();
+                               break;
+                       case TdsPacketSubType.Done :
+                       case TdsPacketSubType.DoneProc :
+                       case TdsPacketSubType.DoneInProc :
+Console.WriteLine ("Done");
+                               result = ProcessEndToken (subType);
+                               moreResults2 = ((TdsPacketEndTokenResult) result).MoreResults;
+                               break;
+                       case TdsPacketSubType.ColumnNameToken :
+Console.WriteLine ("Column Name");
+                               result = ProcessColumnNames ();
+                               break;
+                       case TdsPacketSubType.ColumnInfoToken :
+Console.WriteLine ("Column Info");
+                               result = ProcessColumnInfo ();
+                               break;
+                       case TdsPacketSubType.Unknown0xA5 :
+                       case TdsPacketSubType.Unknown0xA7 :
+                       case TdsPacketSubType.Unknown0xA8 :
+Console.WriteLine ("Unknown");
+                               comm.Skip (comm.GetTdsShort ());
+                               result = new TdsPacketUnknown (subType);
+                               break;
+                       case TdsPacketSubType.TableName :
+Console.WriteLine ("Table Name");
+                               result = ProcessTableName ();
+                               break;
+                       case TdsPacketSubType.Order :
+Console.WriteLine ("Order");
+                               comm.Skip (comm.GetTdsShort ());
+                               result = new TdsPacketColumnOrderResult ();
+                               break;
+                       case TdsPacketSubType.Control :
+Console.WriteLine ("Control");
+                               comm.Skip (comm.GetTdsShort ());
+                               result = new TdsPacketControlResult ();
+                               break;
+                       case TdsPacketSubType.Row :
+Console.WriteLine ("Row");
+                               result = LoadRow (new TdsPacketRowResult (null));
+                               break;
+                       case TdsPacketSubType.ColumnMetadata :
+Console.WriteLine ("Column Metadata");
+                               result = ProcessTds7Result ();
+                               break;
+                       default:
+                               throw new TdsException ("oops!");
+                       }
+
+                       return result;
+               }
+
+               private TdsPacketTableNameResult ProcessTableName ()
+               {
+                       int totalLength = comm.GetTdsShort ();
+                       comm.Skip (totalLength);
+                       return new TdsPacketTableNameResult ();
+               }
+
+               [MonoTODO ("complete")]
+               private TdsPacketResult ProcessTds7Result ()
+               {
+                       int numColumns = comm.GetTdsShort ();
+
+                       return null;
+
+               }
+
+               private object ReadFloatN (int len)
+               {
+                       object tmp;
+                       long l;
+                       switch (len) {
+                       case 8 :
+                               l = comm.GetTdsInt64 ();
+                               tmp = BitConverter.Int64BitsToDouble (l);
+                               break;
+                       case 4 :
+                               l = comm.GetTdsInt ();
+                               tmp = BitConverter.Int64BitsToDouble (l);
+                               break;
+                       case 0 :
+                               tmp = null;
+                               break;
+                       default:
+                               throw new TdsException ("");
+                       }
+
+                       return tmp;
+               }
+
+               [MonoTODO]
+               public void RollbackTransaction ()
+               {
+                       SubmitProcedure ("IF @@TRANCOUNT>0 ROLLBACK TRAN");
+               }
+
+               [MonoTODO]
+               public void SaveTransaction (string savePointName)
+               {
+                       string sql = String.Format ("SAVE TRAN {0}", savePointName);
+                       SubmitProcedure (sql);
+               }
+
+               private void SetCharset (string charset)
+               {
+                       if (charset == null || charset.Length > 30)
+                               charset = "iso-8859-1";
+                       if (this.charset != charset) {
+                               encoder = Encoding.GetEncoding (charset);
+                               this.charset = charset;
+                       }
+               }
+
+               public void SubmitProcedure (string sql)
+               {
+                       TdsPacketResult result;
+                       ExecuteQuery (sql);
+                       bool done;
+                       do {
+                               result = ProcessSubPacket ();
+                               if (result is TdsPacketMessageResult) {
+                                       Console.WriteLine (((TdsPacketMessageResult) result).Message);
+                               }
+                               done = (result is TdsPacketEndTokenResult) && (!((TdsPacketEndTokenResult) result).MoreResults);
+                               
+                       } while (!done);
+               }
+
                public bool Logon (TdsConnectionParameters parms)
                {
                        byte pad = (byte) 0;
                        byte[] empty = new byte[0];
+                       bool isOkay = true;
 
                        if (tdsVersion == TdsVersion.tds70) {
                                Send70Logon (parms);
@@ -123,235 +990,268 @@ namespace Mono.Data.TdsClient.Internal {
                                comm.StartPacket (TdsPacketType.Logon);
 
                                // hostname (offset 0)
-                               byte[] tmp = encoding.GetBytes (parms.Host);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               byte[] tmp = comm.Append (hostname, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // username (offset 31 0x1f)
-                               tmp = encoding.GetBytes (parms.User);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (user, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // password (offset 62 0x3e)
-                               tmp = encoding.GetBytes (parms.Password);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (password, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // hostproc (offset 93 0x5d)
-                               tmp = encoding.GetBytes ("00000116");
-                               comm.AppendBytes (tmp, 8, pad);
+                               comm.Append ("00000116", 8, pad);
 
                                // unused (offset 109 0x6d)
-                               comm.AppendBytes (empty, (30-14), pad);
+                               comm.Append (empty, (30-14), pad);
 
                                // apptype 
-                               comm.AppendByte ((byte) 0x0);
-                               comm.AppendByte ((byte) 0xa0);
-                               comm.AppendByte ((byte) 0x24);
-                               comm.AppendByte ((byte) 0xcc);
-                               comm.AppendByte ((byte) 0x50);
-                               comm.AppendByte ((byte) 0x12);
+                               comm.Append ((byte) 0x0);
+                               comm.Append ((byte) 0xa0);
+                               comm.Append ((byte) 0x24);
+                               comm.Append ((byte) 0xcc);
+                               comm.Append ((byte) 0x50);
+                               comm.Append ((byte) 0x12);
 
                                // hostproc length 
-                               comm.AppendByte ((byte) 8);
+                               comm.Append ((byte) 8);
 
                                // type of int2
-                               comm.AppendByte ((byte) 3);
+                               comm.Append ((byte) 3);
 
                                // type of int4
-                               comm.AppendByte ((byte) 1);
+                               comm.Append ((byte) 1);
 
                                // type of char
-                               comm.AppendByte ((byte) 6);
+                               comm.Append ((byte) 6);
 
                                // type of flt
-                               comm.AppendByte ((byte) 10);
+                               comm.Append ((byte) 10);
 
                                // type of date
-                               comm.AppendByte ((byte) 9);
+                               comm.Append ((byte) 9);
                                
                                // notify of use db
-                               comm.AppendByte ((byte) 1);
+                               comm.Append ((byte) 1);
 
                                // disallow dump/load and bulk insert
-                               comm.AppendByte ((byte) 1);
+                               comm.Append ((byte) 1);
 
                                // sql interface type
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) 0);
 
                                // type of network connection
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) 0);
 
-                               // spare [7]
-                               comm.AppendBytes (empty, 7, pad);
 
+                               // spare [7]
+                               comm.Append (empty, 7, pad);
                                // appname
-                               tmp = encoding.GetBytes (parms.ApplicationName);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (applicationName, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // server name
-                               tmp = encoding.GetBytes (parms.Host);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (server, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // remote passwords
-                               comm.AppendBytes (empty, 2, pad);
-                               tmp = encoding.GetBytes (parms.Password);
-                               comm.AppendBytes (tmp, 253, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2));
+                               comm.Append (empty, 2, pad);
+                               tmp = comm.Append (password, 253, pad);
+                               comm.Append ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2));
 
                                // tds version
-                               comm.AppendByte ((byte) (((byte) parms.TdsVersion) / 10));
-                               comm.AppendByte ((byte) (((byte) parms.TdsVersion) % 10));
-                               comm.AppendByte ((byte) 0);
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) (((byte) tdsVersion) / 10));
+                               comm.Append ((byte) (((byte) tdsVersion) % 10));
+                               comm.Append ((byte) 0);
+                               comm.Append ((byte) 0);
 
                                // prog name
-                               tmp = encoding.GetBytes (parms.ProgName);
-                               comm.AppendBytes (tmp, 10, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (progName, 10, pad);
+                               comm.Append ((byte) (tmp.Length < 10 ? tmp.Length : 10));
 
                                // prog version
-                               comm.AppendByte ((byte) 6);
+                               comm.Append ((byte) 6);
 
                                // Tell the server we can handle SQLServer version 6
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) 0);
 
                                // Send zero to tell the server we can't handle any other version
-                               comm.AppendByte ((byte) 0);
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) 0);
+                               comm.Append ((byte) 0);
 
                                // auto convert short
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) 0);
 
                                // type of flt4
-                               comm.AppendByte ((byte) 0x0d);
+                               comm.Append ((byte) 0x0d);
 
                                // type of date4
-                               comm.AppendByte ((byte) 0x11);
+                               comm.Append ((byte) 0x11);
 
                                // language
-                               tmp = encoding.GetBytes (parms.Language);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (language, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // notify on lang change
-                               comm.AppendByte ((byte) 1);
+                               comm.Append ((byte) 1);
 
                                // security label hierarchy
-                               comm.AppendShort ((short) 0);
+                               comm.Append ((short) 0);
 
                                // security components
-                               comm.AppendBytes (empty, 8, pad);
+                               comm.Append (empty, 8, pad);
 
                                // security spare
-                               comm.AppendShort ((short) 0);
+                               comm.Append ((short) 0);
 
                                // security login role
-                               comm.AppendByte ((byte) 0);
+                               comm.Append ((byte) 0);
 
                                // charset
-                               tmp = encoding.GetBytes (parms.Encoding);
-                               comm.AppendBytes (tmp, 30, pad);
-                               comm.AppendByte ((byte) (tmp.Length < 30 ? tmp.Length : 30));
+                               tmp = comm.Append (charset, 30, pad);
+                               comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
 
                                // notify on charset change
-                               comm.AppendByte ((byte) 1);
+                               comm.Append ((byte) 1);
 
                                // length of tds packets
-                               tmp = encoding.GetBytes (parms.PacketSize.ToString ());
-                               comm.AppendBytes (tmp, 6, pad);
-                               comm.AppendByte ((byte) 3);
+                               tmp = comm.Append (packetSize.ToString (), 6, pad);
+                               comm.Append ((byte) 3);
 
                                // pad out to a longword
-                               comm.AppendBytes (empty, 8, pad);
+                               comm.Append (empty, 8, pad);
                        }
 
                        comm.SendPacket ();
-                       return true;
+
+                       TdsPacketResult result;
+
+                       while (!((result = ProcessSubPacket()) is TdsPacketEndTokenResult)) {
+                               if (result is TdsPacketErrorResult) {
+                                       isOkay = false;
+                               }
+                               // XXX Should really process some more types of packets.
+                       }
+
+                       if (isOkay) {
+                               // XXX Should we move this to the Connection class?
+                               //isOkay = initSettings(_database);
+                       }
+
+                       // XXX Possible bug.  What happend if this is cancelled before the logon
+                       // takes place?  Should isOkay be false?
+                       return isOkay;
                        
                }
 
+               // This packet is documented at 
+               // http://www.freetds.org/tds.htm#login7
                public void Send70Logon (TdsConnectionParameters parms)
                {
-                       short packSize = (short) (86 + 2 * (parms.User.Length + parms.Password.Length + parms.ApplicationName.Length + parms.Host.Length + parms.LibraryName.Length + parms.Database.Length));
                        byte[] empty = new byte[0];
                        byte pad = (byte) 0;
 
+                       byte[] magic1 = {0x06, 0x83, 0xf2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x88, 0xff, 0xff, 0xff, 0x36, 0x04, 0x00, 0x00};
+                       byte[] magic2 = {0x00, 0x40, 0x33, 0x9a, 0x6b, 0x50};
+                       byte[] magic3 = {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50}; // NTLMSSP
+                       short partialPacketSize = (short) (86 + 2 * (
+                                       hostname.Length + 
+                                       user.Length + 
+                                       applicationName.Length + 
+                                       password.Length + 
+                                       server.Length +
+                                       libraryName.Length +
+                                       language.Length +
+                                       connectDB.Length)); 
+                       short totalPacketSize = (short) (partialPacketSize + 48);
                        comm.StartPacket (TdsPacketType.Logon70);
-                       comm.AppendTdsInt (packSize);
-
-                       // TDS Version
-                       comm.AppendTdsInt (0x70000000);
+                       comm.Append (totalPacketSize);
+                       comm.Append (empty, 5, pad);
 
-                       comm.AppendBytes (empty, 16, pad);
+                       if (tdsVersion == TdsVersion.tds80)
+                               comm.Append ((byte) 0x80);
+                       else
+                               comm.Append ((byte) 0x70);
 
-                       // Magic!
-                       comm.AppendByte ((byte) 0xe0);
-                       comm.AppendByte ((byte) 0x03);
-                       comm.AppendBytes (empty, 10, pad);
+                       comm.Append (empty, 7, pad);
+                       comm.Append (magic1);
 
-                       // Pack up value lengths, positions
                        short curPos = 86;
 
-                       // Hostname
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) 0);
+                       // Hostname 
+                       comm.Append (curPos);
+                       comm.Append ((short) hostname.Length);
+                       curPos += (short) (hostname.Length * 2);
 
                        // Username
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) parms.User.Length);
-                       curPos += (short) (parms.User.Length * 2);
+                       comm.Append (curPos);
+                       comm.Append ((short) user.Length);
+                       curPos += (short) (user.Length * 2);
 
                        // Password
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) parms.Password.Length);
-                       curPos += (short) (parms.Password.Length * 2);
+                       comm.Append (curPos);
+                       comm.Append ((short) password.Length);
+                       curPos += (short) (password.Length * 2);
 
                        // AppName
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) parms.ApplicationName.Length);
-                       curPos += (short) (parms.ApplicationName.Length * 2);
+                       comm.Append (curPos);
+                       comm.Append ((short) applicationName.Length);
+                       curPos += (short) (applicationName.Length * 2);
 
                        // Server Name
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) parms.Host.Length);
-                       curPos += (short) (parms.Host.Length * 2);
+                       comm.Append (curPos);
+                       comm.Append ((short) server.Length);
+                       curPos += (short) (server.Length * 2);
 
                        // Unknown
-                       comm.AppendTdsShort ((short) 0);
-                       comm.AppendTdsShort ((short) 0);
+                       comm.Append ((short) 0);
+                       comm.Append ((short) 0);
 
                        // Library Name
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) parms.LibraryName.Length);
-                       curPos += (short) (parms.LibraryName.Length * 2);
+                       comm.Append (curPos);
+                       comm.Append ((short) libraryName.Length);
+                       curPos += (short) (libraryName.Length * 2);
 
-                       // Unknown
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) 0);
+                       // Character Set
+                       comm.Append (curPos);
+                       comm.Append ((short) language.Length);
+                       curPos += (short) (language.Length * 2);
 
                        // Database
-                       comm.AppendTdsShort (curPos);
-                       comm.AppendTdsShort ((short) parms.Database.Length);
-                       curPos += (short) (parms.Database.Length * 2);
-
-                       // MAC Address
-                       comm.AppendBytes (empty, 6, pad);
-                       comm.AppendTdsShort (curPos);
-
-                       // Some sort of appended magic
-                       comm.AppendTdsShort ((short) 0);
-                       comm.AppendTdsInt (packSize);
-
-                       string scrambledPwd = Tds7CryptPass (parms.Password);
-                       comm.AppendChars (parms.User);
-                       comm.AppendChars (scrambledPwd);
-                       comm.AppendChars (parms.ApplicationName);
-                       comm.AppendChars (parms.Host);
-                       comm.AppendChars (parms.LibraryName);
-                       comm.AppendChars (parms.Database);
+                       comm.Append (curPos);
+                       comm.Append ((short) connectDB.Length);
+                       curPos += (short) (connectDB.Length * 2);
+
+                       comm.Append (magic2);
+                       comm.Append (partialPacketSize);
+                       comm.Append ((short) 48);
+                       comm.Append (totalPacketSize);
+                       comm.Append ((short) 0);
+
+                       string scrambledPwd = Tds7CryptPass (password);
+
+                       comm.Append (hostname);
+                       comm.Append (user);
+                       comm.Append (scrambledPwd);
+                       comm.Append (applicationName);
+                       comm.Append (server);
+                       comm.Append (libraryName);
+                       comm.Append (language);
+                       comm.Append (connectDB);
+                       comm.Append (magic3);
+
+                       comm.Append ((byte) 0x0);
+                       comm.Append ((byte) 0x1);
+                       comm.Append (empty, 3, pad);
+                       comm.Append ((byte) 0x6);
+                       comm.Append ((byte) 0x82);
+                       comm.Append (empty, 22, pad);
+                       comm.Append ((byte) 0x30);
+                       comm.Append (empty, 7, pad);
+                       comm.Append ((byte) 0x30);
+                       comm.Append (empty, 3, pad);
                }
 
                private string SqlStatementForSettings (bool autoCommit, IsolationLevel isolationLevel)
@@ -390,7 +1290,7 @@ namespace Mono.Data.TdsClient.Internal {
                        return result;
                }
 
-               [System.MonoTODO]
+               [MonoTODO]
                private string SqlStatementToSetIsolationLevel ()
                {
                        string result = "";
@@ -401,19 +1301,16 @@ namespace Mono.Data.TdsClient.Internal {
                {
                        int xormask = 0x5a5a;
                        int len = pass.Length;
-                       StringBuilder sb = new StringBuilder ();
-                       int i;
-                       int m1;
-                       int m2;
-
-                       foreach (char c in pass)
-                       {
-                               i = (int) (c ^ xormask);
-                               m1 = (i >> 4) & 0x0f0f;
-                               m2 = (i << 4) & 0xf0f0;
-                               sb.Append ((char) (m1 | m2));
-                       }
-                       return sb.ToString ();
+                       char[] chars = new char[len];
+
+                       for (int i = 0; i < len; ++i) {
+                               int c = ((int) (pass[i])) ^ xormask;
+                               int m1 = (c >> 4) & 0x0f0f;
+                               int m2 = (c << 4) & 0xf0f0;
+                               chars[i] = (char) (m1 | m2);
+                       }
+
+                       return new String (chars);
                }
 
                #endregion // Methods