2002-10-22 Tim Coleman (tim@timcoleman.com)
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsClient.Internal / Tds.cs
1 //
2 // Mono.Data.TdsClient.Internal.Tds.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) 2002 Tim Coleman
8 //
9
10 using System;
11 using System.Data;
12 using System.Net;
13 using System.Net.Sockets;
14 using System.Text;
15
16 namespace Mono.Data.TdsClient.Internal {
17         internal class Tds
18         {
19                 #region Fields
20
21                 TdsVersion tdsVersion;
22
23                 string applicationName;
24                 string database = String.Empty;
25                 string connectDB;
26                 string charset;
27                 string hostname;
28                 string server;
29                 string language;
30                 string libraryName;
31                 int packetSize;
32                 string password;
33                 int port;
34                 string progName;
35                 string user;
36
37                 string databaseProductName;
38                 string databaseProductVersion;
39                 int databaseMajorVersion;
40
41                 DataTable table = new DataTable ();
42
43                 bool moreResults;
44                 bool moreResults2;
45
46                 TdsMessage lastServerMessage;
47
48                 Encoding encoder;
49                 TdsServerType serverType;
50                 TdsComm comm;
51                 TdsConnectionParameters parms;
52                 //TdsCommand command;
53                 IsolationLevel isolationLevel;
54                 bool autoCommit;
55                 Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
56
57                 #endregion // Fields
58
59                 #region Properties
60
61                 //public TdsCommandInternal Command {
62                         //get { return command; }
63                         //set { command = value; }
64                 //}
65
66                 public string Database {
67                         get { return database; }
68                         set { database = value; }
69                 }
70
71                 public TdsVersion TdsVersion {
72                         get { return tdsVersion; }
73                 }
74
75                 #endregion // Properties
76
77                 #region Constructors
78
79                 public Tds (TdsConnectionParameters parms)
80                 {
81                         applicationName = parms.ApplicationName;
82                         connectDB = parms.Database;
83                         encoder = Encoding.GetEncoding (parms.Encoding);
84                         charset = parms.Encoding;
85                         hostname = parms.Hostname;
86                         server = parms.DataSource;
87                         language = parms.Language;
88                         libraryName = parms.LibraryName;
89                         packetSize = parms.PacketSize;
90                         password = parms.Password;
91                         port = parms.Port;
92                         progName = parms.ProgName;
93                         tdsVersion = parms.TdsVersion;
94                         user = parms.User;
95
96                         IPHostEntry hostEntry = Dns.Resolve (server);
97                         IPAddress[] addresses = hostEntry.AddressList;
98
99                         IPEndPoint endPoint;
100
101                         foreach (IPAddress address in addresses) {
102                                 endPoint = new IPEndPoint (address, port);
103                                 socket.Connect (endPoint);
104
105                                 if (socket.Connected)
106                                         break;
107                         }
108         
109                         comm = new TdsComm (encoder, socket, packetSize, tdsVersion);
110                 }       
111
112                 #endregion // Constructors
113
114                 #region Methods
115                 
116                 public void BeginTransaction ()
117                 {
118                         SubmitProcedure ("BEGIN TRANSACTION");
119                 }
120                 public void ChangeDatabase (string databaseName)
121                 {
122                         TdsPacketResult result;
123                         bool isOkay;
124
125                         string query = String.Format ("use {0}", databaseName);
126                         comm.StartPacket (TdsPacketType.Query);
127                         comm.Append (query);
128                         comm.SendPacket ();
129
130                         while (!((result = ProcessSubPacket ()) is TdsPacketEndTokenResult)) {
131                                 if (result is TdsPacketErrorResult) {
132                                         isOkay = false;
133                                 }
134                         }
135                 }
136
137                 public void ChangeSettings (bool autoCommit, IsolationLevel isolationLevel)
138                 {
139                         /*string query = SqlStatementForSettings (autoCommit, isolationLevel);
140                         if (query != null)
141                                 ChangeSettings (query);
142                                 */
143                 }
144
145                 private bool ChangeSettings (string query)
146                 {
147                         TdsPacketResult result;
148                         bool isOkay = true;
149                         if (query.Length == 0)
150                                 return true;
151
152                         comm.StartPacket (TdsPacketType.Query);
153                         comm.Append (query);
154                         comm.SendPacket ();
155
156                         bool done = false;
157                         while (!done) {
158                                 result = ProcessSubPacket ();
159                                 done = (result is TdsPacketEndTokenResult) && !((TdsPacketEndTokenResult) result).MoreResults;
160                                 if (result is TdsPacketErrorResult) {
161                                         done = true;
162                                         isOkay = false;
163                                 }
164                         }
165
166                         return isOkay;
167                 }
168
169                 public void CommitTransaction ()
170                 {
171                         string sql = "IF @@TRANCOUNT>0 COMMIT TRAN";
172                         SubmitProcedure (sql);
173                 }
174
175                 [MonoTODO ("fixme")]
176                 public int ExecuteNonQuery (string sql)
177                 {
178                         TdsPacketResult result;
179                         bool done = false;
180
181                         if (sql.Length > 0) {
182                                 comm.StartPacket (TdsPacketType.Query);
183                                 comm.Append (sql);
184                                 moreResults2 = true;
185                                 comm.SendPacket ();
186                         }
187
188                         do {
189                                 result = ProcessSubPacket ();
190                                 if (result is TdsPacketMessageResult) {
191                                         Console.WriteLine (((TdsPacketMessageResult) result).Message);
192                                 }
193                                 done = (result is TdsPacketEndTokenResult) && (!((TdsPacketEndTokenResult) result).MoreResults);
194                                 
195                         } while (!done);
196
197                         return -1;
198                 }
199
200                 [MonoTODO ("fixme")]
201                 public void ExecuteQuery (string sql)
202                 {
203                         if (sql.Length > 0) {
204                                 comm.StartPacket (TdsPacketType.Query);
205                                 comm.Append (sql);
206                                 moreResults2 = true;
207                                 comm.SendPacket ();
208                         }
209                 }
210
211                 private object GetCharValue (bool wideChars, bool outputParam)
212                 {
213                         object result = null;
214                         bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam);
215                         int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
216
217                         if ((tdsVersion < TdsVersion.tds70 && len == 0) || (tdsVersion == TdsVersion.tds70 && len == 0xffff))
218                                 result = null;
219                         else if (len >= 0) {
220                                 if (wideChars)
221                                         result = comm.GetString (len / 2);
222                                 else
223                                         result = encoder.GetString (comm.GetBytes (len, false), 0, len);
224
225                                 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
226                                         result = "";
227                         }
228                         else
229                                 throw new TdsException ("");
230                         return result;
231                 }
232
233                 [MonoTODO]
234                 private object GetDecimalValue (int scale)
235                 {
236                         throw new NotImplementedException ();
237                 }
238
239                 [MonoTODO]
240                 private object GetDateTimeValue (TdsColumnType type)
241                 {
242                         throw new NotImplementedException ();
243                 }
244
245                 private object GetImageValue ()
246                 {
247                         byte[] result;
248                         byte hasValue = comm.GetByte ();
249                         if (hasValue == 0)
250                                 return null;
251                         
252                         comm.Skip (24);
253                         int len = comm.GetTdsInt ();
254                         if (len >= 0) 
255                                 result = comm.GetBytes (len, true);
256                         else
257                                 throw new TdsException ("");
258                         return result;
259                 }
260
261                 private object GetIntValue (TdsColumnType type)
262                 {
263                         object result;
264                         int len;
265
266                         switch (type) {
267                         case TdsColumnType.IntN :
268                                 len = comm.GetByte ();
269                                 break;
270                         case TdsColumnType.Int4 :
271                                 len = 4; 
272                                 break;
273                         case TdsColumnType.Int2 :
274                                 len = 2; 
275                                 break;
276                         case TdsColumnType.Int1 :
277                                 len = 1; 
278                                 break;
279                         default:
280                                 throw new TdsException ("");
281                         }
282
283                         switch (len) {
284                         case 4 :
285                                 result = comm.GetTdsInt ();
286                                 break;
287                         case 2 :
288                                 result = comm.GetTdsShort ();
289                                 break;
290                         case 1 :
291                                 result = (byte) comm.GetByte ();
292                                 break;
293                         case 0 :
294                                 result = null;
295                                 break;
296                         default:
297                                 throw new TdsException ("Bad integer length");
298                         }
299
300                         return result;
301                 }
302
303                 [MonoTODO]
304                 private object GetMoneyValue (TdsColumnType type)
305                 {
306                         int len;
307                         object result;
308
309                         switch (type) {
310                         case TdsColumnType.SmallMoney :
311                         case TdsColumnType.Money4 :
312                                 len = 4;
313                                 break;
314                         case TdsColumnType.Money :
315                                 len = 8;
316                                 break;
317                         case TdsColumnType.MoneyN :
318                                 len = comm.GetByte ();
319                                 break;
320                         default:
321                                 throw new TdsException ("not a money value");
322                         }
323
324                         if (len == 0)
325                                 result = null;
326                         else {
327                                 throw new NotImplementedException ();
328                         }
329
330                         return result;
331                 }
332
333                 private int GetSubPacketLength ()
334                 {
335                         return comm.GetTdsShort ();
336                 }
337
338                 private object GetTextValue (bool wideChars)
339                 {
340                         string result;
341                         byte hasValue = comm.GetByte ();
342
343                         if (hasValue == 0)
344                                 return null;
345
346                         comm.Skip (24);
347                         int len = comm.GetTdsInt ();
348
349                         if (len >= 0) {
350                                 if (wideChars)
351                                         result = comm.GetString (len / 2);
352                                 else
353                                         result = encoder.GetString (comm.GetBytes (len, false), 0, len);
354
355                                 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
356                                         result = "";
357                         } 
358                         else 
359                                 throw new TdsException ("");
360
361                         return result;
362                 }
363
364                 private bool IsFixedSizeColumn (TdsColumnType columnType)
365                 {
366                         switch (columnType) {
367                                 case TdsColumnType.Int1 :
368                                 case TdsColumnType.Int2 :
369                                 case TdsColumnType.Int4 :
370                                 case TdsColumnType.Float8 :
371                                 case TdsColumnType.DateTime :
372                                 case TdsColumnType.Bit :
373                                 case TdsColumnType.Money :
374                                 case TdsColumnType.Money4 :
375                                 case TdsColumnType.SmallMoney :
376                                 case TdsColumnType.Real :
377                                 case TdsColumnType.DateTime4 :
378                                         return true;
379                                 case TdsColumnType.IntN :
380                                 case TdsColumnType.MoneyN :
381                                 case TdsColumnType.VarChar :
382                                 case TdsColumnType.NVarChar :
383                                 case TdsColumnType.DateTimeN :
384                                 case TdsColumnType.FloatN :
385                                 case TdsColumnType.Char :
386                                 case TdsColumnType.NChar :
387                                 case TdsColumnType.NText :
388                                 case TdsColumnType.Image :
389                                 case TdsColumnType.VarBinary :
390                                 case TdsColumnType.Binary :
391                                 case TdsColumnType.Decimal :
392                                 case TdsColumnType.Numeric :
393                                 case TdsColumnType.BitN :
394                                 case TdsColumnType.UniqueIdentifier :
395                                         return false;
396                                 default :
397                                         throw new TdsException ("bad type");
398                         }
399                 }
400
401                 [MonoTODO]
402                 private TdsPacketRowResult LoadRow (TdsPacketRowResult result)
403                 {
404                         throw new NotImplementedException ();
405                 }
406
407                 private int LookupBufferSize (TdsColumnType columnType)
408                 {
409                         switch (columnType) {
410                                 case TdsColumnType.Int1 :
411                                 case TdsColumnType.Bit :
412                                         return 1;
413                                 case TdsColumnType.Int2 :
414                                         return 2;
415                                 case TdsColumnType.Int4 :
416                                 case TdsColumnType.Real :
417                                 case TdsColumnType.DateTime4 :
418                                 case TdsColumnType.Money4 :
419                                 case TdsColumnType.SmallMoney :
420                                         return 4;
421                                 case TdsColumnType.Float8 :
422                                 case TdsColumnType.DateTime :
423                                 case TdsColumnType.Money :
424                                         return 8;
425                                 default :
426                                         throw new TdsException ("");
427                         }
428                 }
429
430                 private int LookupDisplaySize (TdsColumnType columnType) 
431                 {
432                         switch (columnType) {
433                                 case TdsColumnType.Int1 :
434                                         return 3;
435                                 case TdsColumnType.Int2 :
436                                         return 6;
437                                 case TdsColumnType.Int4 :
438                                         return 11;
439                                 case TdsColumnType.Real :
440                                         return 14;
441                                 case TdsColumnType.Float8 :
442                                         return 24;
443                                 case TdsColumnType.DateTime :
444                                         return 23;
445                                 case TdsColumnType.DateTime4 :
446                                         return 16;
447                                 case TdsColumnType.Bit :
448                                         return 1;
449                                 case TdsColumnType.Money :
450                                         return 21;
451                                 case TdsColumnType.Money4 :
452                                 case TdsColumnType.SmallMoney :
453                                         return 12;
454                                 default:
455                                         throw new TdsException ("");
456                         }
457                 }
458
459                 private TdsPacketColumnNamesResult ProcessColumnNames ()
460                 {
461                         int totalLength = comm.GetTdsShort ();
462                         int bytesRead = 0;
463                         int i = 0;
464
465                         bool newTable = (table.Columns.Count > 0);
466
467                         while (bytesRead < totalLength) {
468                                 int columnNameLength = comm.GetByte ();
469                                 string columnName = encoder.GetString (comm.GetBytes (columnNameLength, false), 0, columnNameLength);
470                                 bytesRead = bytesRead + 1 + columnNameLength;
471                                 if (newTable)
472                                         table.Columns.Add (columnName);
473                                 else
474                                         table.Columns[i].ColumnName = columnName;
475                                 i += 1;
476                         }
477
478                         return new TdsPacketColumnNamesResult (table.Columns);
479                 }
480
481                 private TdsPacketColumnInfoResult ProcessColumnInfo ()
482                 {
483                         int precision;
484                         int scale;
485                         int totalLength = comm.GetTdsShort ();
486                         int bytesRead = 0;
487                         int numColumns = 0;
488
489                         bool newTable = (table.Columns.Count > 0);
490
491                         while (bytesRead < totalLength) {
492                                 scale = -1;
493                                 precision = -1;
494
495                                 int bufLength = -1;
496                                 int dispSize = -1;
497                                 byte[] flagData = new byte[4];
498                                 for (int i = 0; i < 4; i += 1) {
499                                         flagData[i] = comm.GetByte ();
500                                         bytesRead += 1;
501                                 }
502                                 bool nullable = (flagData[2] & 0x01) > 0;
503                                 bool caseSensitive = (flagData[2] & 0x02) > 0;
504                                 bool writable = (flagData[2] & 0x0c) > 0;
505                                 bool autoIncrement = (flagData[2] & 0x10) > 0;
506                                 string tableName = String.Empty;
507                                 TdsColumnType columnType = (TdsColumnType) comm.GetByte ();
508                                 bytesRead += 1;
509
510                                 if (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image) {
511                                         comm.Skip (4);
512                                         bytesRead += 4;
513
514                                         int tableNameLength = comm.GetTdsShort ();
515                                         bytesRead += 2;
516                                         tableName = encoder.GetString (comm.GetBytes (tableNameLength, false), 0, tableNameLength);
517                                         bytesRead += tableNameLength;
518                                         bufLength = 2 << 31 - 1;
519                                 }
520                                 else if (columnType == TdsColumnType.Decimal || columnType == TdsColumnType.Numeric) {
521                                         bufLength = comm.GetByte ();
522                                         bytesRead += 1;
523                                         precision = comm.GetByte ();
524                                         bytesRead += 1;
525                                         scale = comm.GetByte ();
526                                         bytesRead += 1;
527                                 }
528                                 else if (IsFixedSizeColumn (columnType))
529                                         bufLength = LookupBufferSize (columnType);
530                                 else {
531                                         bufLength = (int) comm.GetByte () & 0xff;
532                                         bytesRead += 1;
533                                 }
534
535                                 DataColumn column;
536
537                                 if (newTable) 
538                                         column = table.Columns.Add ();
539                                 else
540                                         column = table.Columns[numColumns];
541
542                                 numColumns += 1;
543
544                                 column.AllowDBNull = nullable;
545                                 column.AutoIncrement = autoIncrement;
546                                 column.ReadOnly = !writable;
547                         }
548
549                         int skipLength = totalLength - bytesRead;
550                         if (skipLength != 0)
551                                 throw new TdsException ("skipping");
552                         return new TdsPacketColumnInfoResult (table.Columns);
553                 }
554
555                 [MonoTODO]
556                 private TdsPacketEndTokenResult ProcessEndToken (TdsPacketSubType type)
557                 {
558                         byte status = comm.GetByte ();
559                         comm.GetByte ();
560                         byte op = comm.GetByte ();
561                         comm.GetByte ();
562                         int rowCount = comm.GetTdsInt ();
563                         if (op == (byte) 0xc1) 
564                                 rowCount = 0;
565
566                         if (type == TdsPacketSubType.DoneInProc) 
567                                 rowCount = -1;
568
569                         TdsPacketEndTokenResult result = new TdsPacketEndTokenResult (type, status, rowCount);
570
571                         Console.WriteLine ("Row count {0}", rowCount);
572
573                         moreResults = result.MoreResults;
574
575                         // FIXME: Finish the query
576
577                         return result;
578                 }
579
580                 private TdsPacketResult ProcessEnvChange ()
581                 {
582                         int len = GetSubPacketLength ();
583                         TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
584                         int cLen;
585
586                         switch (type) {
587                         case TdsEnvPacketSubType.BlockSize :
588                                 string blockSize;
589                                 cLen = comm.GetByte () & 0xff;
590                                 if (tdsVersion == TdsVersion.tds70) {
591                                         blockSize = comm.GetString (cLen);
592                                         comm.Skip (len - 2 - cLen * 2);
593                                 }
594                                 else {
595                                         blockSize = encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
596                                         comm.Skip (len - 2 - cLen);
597                                 }
598
599                                 comm.ResizeOutBuf (Int32.Parse (blockSize));
600                                 break;
601                         case TdsEnvPacketSubType.CharSet :
602                                 cLen = comm.GetByte () & 0xff;
603                                 if (tdsVersion == TdsVersion.tds70) {
604                                         this.language = comm.GetString (cLen);
605                                         comm.Skip (len - 2 - cLen * 2);
606                                 }
607                                 else {
608                                         this.charset = encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
609                                         comm.Skip (len - 2 - cLen);
610                                         SetCharset (charset);
611                                 }
612
613                                 break;
614                         case TdsEnvPacketSubType.Database :
615                                 cLen = comm.GetByte () & 0xff;
616                                 string newDB = tdsVersion == TdsVersion.tds70 ? comm.GetString (cLen) : encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
617                                 cLen = comm.GetByte () & 0xff;
618                                 string oldDB = tdsVersion == TdsVersion.tds70 ? comm.GetString (cLen) : encoder.GetString (comm.GetBytes (cLen, false), 0, cLen);
619                                 if (database != String.Empty && database != oldDB)
620                                         throw new TdsException ("Database mismatch.");
621                                 database = newDB;
622                                 break;
623                         default:
624                                 comm.Skip (len - 1);
625                                 break;
626                         }
627
628                         return new TdsPacketResult (TdsPacketSubType.EnvChange);
629                 }
630
631                 private TdsPacketResult ProcessLoginAck ()
632                 {
633                         GetSubPacketLength ();
634
635                         if (tdsVersion == TdsVersion.tds70) {
636                                 comm.Skip (5);
637                                 int nameLength = comm.GetByte ();
638                                 databaseProductName = comm.GetString (nameLength);
639                                 databaseMajorVersion = comm.GetByte ();
640                                 databaseProductVersion = String.Format ("0{0}.0{1}.0{2}", databaseMajorVersion, comm.GetByte (), ((256 * (comm.GetByte () + 1)) + comm.GetByte ()));
641                         }
642                         else {
643                                 comm.Skip (5);
644                                 short nameLength = comm.GetByte ();
645                                 databaseProductName = comm.GetString (nameLength);
646                                 comm.Skip (1);
647                                 databaseMajorVersion = comm.GetByte ();
648                                 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
649                                 comm.Skip (1);
650                         }
651                         Console.WriteLine ("Connected to {0} {1}", databaseProductName, databaseProductVersion);
652
653                         if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
654                                 int last = databaseProductName.IndexOf ('\0');
655                                 databaseProductName = databaseProductName.Substring (0, last);
656                         }
657
658                         return new TdsPacketResult (TdsPacketSubType.LoginAck);
659                 }
660
661                 private TdsPacketMessageResult ProcessMessage (TdsPacketSubType subType)
662                 {
663                         TdsMessage message = new TdsMessage ();
664                         GetSubPacketLength ();
665                         int len;
666
667                         message.Number = comm.GetTdsInt ();
668                         message.State = comm.GetByte ();
669                         message.Severity = comm.GetByte ();
670                         
671                         len = comm.GetTdsShort ();
672
673                         message.Message = comm.GetString (len);
674
675                         len = comm.GetByte ();
676
677                         message.Server = comm.GetString (len);
678
679                         if (subType == TdsPacketSubType.Error | subType == TdsPacketSubType.Info) {
680                                 len = comm.GetByte ();
681                                 message.ProcName = comm.GetString (len);
682                         }
683                         else 
684                                 throw new TdsException ("Invalid subtype");
685                         message.Line = comm.GetByte ();
686                         comm.GetByte ();
687
688                         Console.WriteLine (message.ToString ());
689
690                         lastServerMessage = message;
691                         if (subType == TdsPacketSubType.Error)
692                                 return new TdsPacketErrorResult (subType, message);
693                         else
694                                 return new TdsPacketMessageResult (subType, message);
695                 }
696
697                 private TdsPacketOutputParam ProcessOutputParam ()
698                 {
699                         GetSubPacketLength ();
700                         comm.GetString (comm.GetByte () & 0xff);
701                         comm.Skip (5);
702                         TdsColumnType colType = (TdsColumnType) comm.GetByte ();
703                         int len;
704
705                         object element = null;
706                         switch (colType) {
707                         case TdsColumnType.IntN :
708                                 comm.GetByte ();
709                                 element = GetIntValue (colType);
710                                 break;
711                         case TdsColumnType.Int1 :
712                         case TdsColumnType.Int2 :
713                         case TdsColumnType.Int4 :
714                                 element = GetIntValue (colType);
715                                 break;
716                         case TdsColumnType.Image :
717                                 comm.GetByte ();
718                                 element = GetImageValue ();
719                                 break;
720                         case TdsColumnType.Text :
721                                 comm.GetByte ();
722                                 element = GetTextValue (false);
723                                 break;
724                         case TdsColumnType.NText :
725                                 comm.GetByte ();
726                                 element = GetTextValue (true);
727                                 break;
728                         case TdsColumnType.Char :
729                         case TdsColumnType.VarChar :
730                                 comm.GetByte ();
731                                 element = GetCharValue (false, true);
732                                 break;
733                         case TdsColumnType.BigVarBinary :
734                                 comm.GetTdsShort ();
735                                 len = comm.GetTdsShort ();
736                                 element = comm.GetBytes (len, true);
737                                 break;
738                         case TdsColumnType.BigVarChar :
739                                 comm.GetTdsShort ();
740                                 element = GetCharValue (false, false);
741                                 break;
742                         case TdsColumnType.NChar :
743                         case TdsColumnType.NVarChar :
744                                 comm.GetByte ();
745                                 element = GetCharValue (true, true);
746                                 break;
747                         case TdsColumnType.Real :
748                                 element = ReadFloatN (4);
749                                 break;
750                         case TdsColumnType.Float8 :
751                                 element = ReadFloatN (8);
752                                 break;
753                         case TdsColumnType.FloatN :
754                                 comm.GetByte ();
755                                 int actualSize = comm.GetByte ();
756                                 element = ReadFloatN (actualSize);
757                                 break;
758                         case TdsColumnType.SmallMoney :
759                         case TdsColumnType.Money :
760                         case TdsColumnType.MoneyN :
761                                 comm.GetByte ();
762                                 element = GetMoneyValue (colType);
763                                 break;
764                         case TdsColumnType.Numeric :
765                         case TdsColumnType.Decimal :
766                                 comm.GetByte ();
767                                 comm.GetByte ();
768                                 int scale = comm.GetByte ();
769                                 element = GetDecimalValue (scale);
770                                 break;
771                         case TdsColumnType.DateTimeN :
772                                 comm.GetByte ();
773                                 element = GetDateTimeValue (colType);
774                                 break;
775                         case TdsColumnType.DateTime4 :
776                         case TdsColumnType.DateTime :
777                                 element = GetDateTimeValue (colType);
778                                 break;
779                         case TdsColumnType.VarBinary :
780                         case TdsColumnType.Binary :
781                                 comm.GetByte ();
782                                 len = (comm.GetByte () & 0xff);
783                                 element = comm.GetBytes (len, true);
784                                 break;
785                         case TdsColumnType.BitN :
786                                 comm.GetByte ();
787                                 if (comm.GetByte () == 0)
788                                         element = null;
789                                 else
790                                         element = (comm.GetByte() != 0);
791                                 break;
792                         case TdsColumnType.Bit :
793                                 int columnSize = comm.GetByte ();
794                                 element = (columnSize != 0);
795                                 break;
796                         case TdsColumnType.UniqueIdentifier :
797                                 len = comm.GetByte () & 0xff;
798                                 //element = (len == 0 ? null : new Guid (comm.GetBytes (len, false)));
799                                 break;
800                         default :
801                                 throw new TdsException ("");
802                         }
803
804                         return new TdsPacketOutputParam (element);
805                 }
806
807                 private TdsPacketResult ProcessProcId ()
808                 {
809                         comm.Skip (8);
810                         return new TdsPacketResult (TdsPacketSubType.ProcId);
811                 }
812
813                 private TdsPacketRetStatResult ProcessReturnStatus ()
814                 {
815                         return new TdsPacketRetStatResult (comm.GetTdsInt ());
816                 }
817
818                 [MonoTODO]
819                 private TdsPacketResult ProcessSubPacket ()
820                 {
821                         TdsPacketResult result = null;
822                         moreResults = false;
823
824                         TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
825
826                         switch (subType) {
827                         case TdsPacketSubType.EnvChange :
828 Console.WriteLine ("Environment change");
829                                 result = ProcessEnvChange ();
830                                 break;
831                         case TdsPacketSubType.Error :
832                         case TdsPacketSubType.Info :
833                         case TdsPacketSubType.Msg50Token :
834 Console.WriteLine ("Error/info/message");
835                                 result = ProcessMessage (subType);
836                                 break;
837                         case TdsPacketSubType.Param :
838 Console.WriteLine ("Param");
839                                 result = ProcessOutputParam ();
840                                 break;
841                         case TdsPacketSubType.LoginAck :
842 Console.WriteLine ("Login Ack");
843                                 result = ProcessLoginAck ();
844                                 break;
845                         case TdsPacketSubType.ReturnStatus :
846 Console.WriteLine ("Return Status");
847                                 result = ProcessReturnStatus ();
848                                 break;
849                         case TdsPacketSubType.ProcId :
850 Console.WriteLine ("Proc Id");
851                                 result = ProcessProcId ();
852                                 break;
853                         case TdsPacketSubType.Done :
854                         case TdsPacketSubType.DoneProc :
855                         case TdsPacketSubType.DoneInProc :
856 Console.WriteLine ("Done");
857                                 result = ProcessEndToken (subType);
858                                 moreResults2 = ((TdsPacketEndTokenResult) result).MoreResults;
859                                 break;
860                         case TdsPacketSubType.ColumnNameToken :
861 Console.WriteLine ("Column Name");
862                                 result = ProcessColumnNames ();
863                                 break;
864                         case TdsPacketSubType.ColumnInfoToken :
865 Console.WriteLine ("Column Info");
866                                 result = ProcessColumnInfo ();
867                                 break;
868                         case TdsPacketSubType.Unknown0xA5 :
869                         case TdsPacketSubType.Unknown0xA7 :
870                         case TdsPacketSubType.Unknown0xA8 :
871 Console.WriteLine ("Unknown");
872                                 comm.Skip (comm.GetTdsShort ());
873                                 result = new TdsPacketUnknown (subType);
874                                 break;
875                         case TdsPacketSubType.TableName :
876 Console.WriteLine ("Table Name");
877                                 result = ProcessTableName ();
878                                 break;
879                         case TdsPacketSubType.Order :
880 Console.WriteLine ("Order");
881                                 comm.Skip (comm.GetTdsShort ());
882                                 result = new TdsPacketColumnOrderResult ();
883                                 break;
884                         case TdsPacketSubType.Control :
885 Console.WriteLine ("Control");
886                                 comm.Skip (comm.GetTdsShort ());
887                                 result = new TdsPacketControlResult ();
888                                 break;
889                         case TdsPacketSubType.Row :
890 Console.WriteLine ("Row");
891                                 result = LoadRow (new TdsPacketRowResult (null));
892                                 break;
893                         case TdsPacketSubType.ColumnMetadata :
894 Console.WriteLine ("Column Metadata");
895                                 result = ProcessTds7Result ();
896                                 break;
897                         default:
898                                 throw new TdsException ("oops!");
899                         }
900
901                         return result;
902                 }
903
904                 private TdsPacketTableNameResult ProcessTableName ()
905                 {
906                         int totalLength = comm.GetTdsShort ();
907                         comm.Skip (totalLength);
908                         return new TdsPacketTableNameResult ();
909                 }
910
911                 [MonoTODO ("complete")]
912                 private TdsPacketResult ProcessTds7Result ()
913                 {
914                         int numColumns = comm.GetTdsShort ();
915
916                         return null;
917
918                 }
919
920                 private object ReadFloatN (int len)
921                 {
922                         object tmp;
923                         long l;
924                         switch (len) {
925                         case 8 :
926                                 l = comm.GetTdsInt64 ();
927                                 tmp = BitConverter.Int64BitsToDouble (l);
928                                 break;
929                         case 4 :
930                                 l = comm.GetTdsInt ();
931                                 tmp = BitConverter.Int64BitsToDouble (l);
932                                 break;
933                         case 0 :
934                                 tmp = null;
935                                 break;
936                         default:
937                                 throw new TdsException ("");
938                         }
939
940                         return tmp;
941                 }
942
943                 [MonoTODO]
944                 public void RollbackTransaction ()
945                 {
946                         SubmitProcedure ("IF @@TRANCOUNT>0 ROLLBACK TRAN");
947                 }
948
949                 [MonoTODO]
950                 public void SaveTransaction (string savePointName)
951                 {
952                         string sql = String.Format ("SAVE TRAN {0}", savePointName);
953                         SubmitProcedure (sql);
954                 }
955
956                 private void SetCharset (string charset)
957                 {
958                         if (charset == null || charset.Length > 30)
959                                 charset = "iso-8859-1";
960                         if (this.charset != charset) {
961                                 encoder = Encoding.GetEncoding (charset);
962                                 this.charset = charset;
963                         }
964                 }
965
966                 public void SubmitProcedure (string sql)
967                 {
968                         TdsPacketResult result;
969                         ExecuteQuery (sql);
970                         bool done;
971                         do {
972                                 result = ProcessSubPacket ();
973                                 if (result is TdsPacketMessageResult) {
974                                         Console.WriteLine (((TdsPacketMessageResult) result).Message);
975                                 }
976                                 done = (result is TdsPacketEndTokenResult) && (!((TdsPacketEndTokenResult) result).MoreResults);
977                                 
978                         } while (!done);
979                 }
980
981                 public bool Logon (TdsConnectionParameters parms)
982                 {
983                         byte pad = (byte) 0;
984                         byte[] empty = new byte[0];
985                         bool isOkay = true;
986
987                         if (tdsVersion == TdsVersion.tds70) {
988                                 Send70Logon (parms);
989                         } else {
990                                 comm.StartPacket (TdsPacketType.Logon);
991
992                                 // hostname (offset 0)
993                                 byte[] tmp = comm.Append (hostname, 30, pad);
994                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
995
996                                 // username (offset 31 0x1f)
997                                 tmp = comm.Append (user, 30, pad);
998                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
999
1000                                 // password (offset 62 0x3e)
1001                                 tmp = comm.Append (password, 30, pad);
1002                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
1003
1004                                 // hostproc (offset 93 0x5d)
1005                                 comm.Append ("00000116", 8, pad);
1006
1007                                 // unused (offset 109 0x6d)
1008                                 comm.Append (empty, (30-14), pad);
1009
1010                                 // apptype 
1011                                 comm.Append ((byte) 0x0);
1012                                 comm.Append ((byte) 0xa0);
1013                                 comm.Append ((byte) 0x24);
1014                                 comm.Append ((byte) 0xcc);
1015                                 comm.Append ((byte) 0x50);
1016                                 comm.Append ((byte) 0x12);
1017
1018                                 // hostproc length 
1019                                 comm.Append ((byte) 8);
1020
1021                                 // type of int2
1022                                 comm.Append ((byte) 3);
1023
1024                                 // type of int4
1025                                 comm.Append ((byte) 1);
1026
1027                                 // type of char
1028                                 comm.Append ((byte) 6);
1029
1030                                 // type of flt
1031                                 comm.Append ((byte) 10);
1032
1033                                 // type of date
1034                                 comm.Append ((byte) 9);
1035                                 
1036                                 // notify of use db
1037                                 comm.Append ((byte) 1);
1038
1039                                 // disallow dump/load and bulk insert
1040                                 comm.Append ((byte) 1);
1041
1042                                 // sql interface type
1043                                 comm.Append ((byte) 0);
1044
1045                                 // type of network connection
1046                                 comm.Append ((byte) 0);
1047
1048
1049                                 // spare [7]
1050                                 comm.Append (empty, 7, pad);
1051                                 // appname
1052                                 tmp = comm.Append (applicationName, 30, pad);
1053                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
1054
1055                                 // server name
1056                                 tmp = comm.Append (server, 30, pad);
1057                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
1058
1059                                 // remote passwords
1060                                 comm.Append (empty, 2, pad);
1061                                 tmp = comm.Append (password, 253, pad);
1062                                 comm.Append ((byte) (tmp.Length < 253 ? tmp.Length + 2 : 253 + 2));
1063
1064                                 // tds version
1065                                 comm.Append ((byte) (((byte) tdsVersion) / 10));
1066                                 comm.Append ((byte) (((byte) tdsVersion) % 10));
1067                                 comm.Append ((byte) 0);
1068                                 comm.Append ((byte) 0);
1069
1070                                 // prog name
1071                                 tmp = comm.Append (progName, 10, pad);
1072                                 comm.Append ((byte) (tmp.Length < 10 ? tmp.Length : 10));
1073
1074                                 // prog version
1075                                 comm.Append ((byte) 6);
1076
1077                                 // Tell the server we can handle SQLServer version 6
1078                                 comm.Append ((byte) 0);
1079
1080                                 // Send zero to tell the server we can't handle any other version
1081                                 comm.Append ((byte) 0);
1082                                 comm.Append ((byte) 0);
1083
1084                                 // auto convert short
1085                                 comm.Append ((byte) 0);
1086
1087                                 // type of flt4
1088                                 comm.Append ((byte) 0x0d);
1089
1090                                 // type of date4
1091                                 comm.Append ((byte) 0x11);
1092
1093                                 // language
1094                                 tmp = comm.Append (language, 30, pad);
1095                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
1096
1097                                 // notify on lang change
1098                                 comm.Append ((byte) 1);
1099
1100                                 // security label hierarchy
1101                                 comm.Append ((short) 0);
1102
1103                                 // security components
1104                                 comm.Append (empty, 8, pad);
1105
1106                                 // security spare
1107                                 comm.Append ((short) 0);
1108
1109                                 // security login role
1110                                 comm.Append ((byte) 0);
1111
1112                                 // charset
1113                                 tmp = comm.Append (charset, 30, pad);
1114                                 comm.Append ((byte) (tmp.Length < 30 ? tmp.Length : 30));
1115
1116                                 // notify on charset change
1117                                 comm.Append ((byte) 1);
1118
1119                                 // length of tds packets
1120                                 tmp = comm.Append (packetSize.ToString (), 6, pad);
1121                                 comm.Append ((byte) 3);
1122
1123                                 // pad out to a longword
1124                                 comm.Append (empty, 8, pad);
1125                         }
1126
1127                         comm.SendPacket ();
1128
1129                         TdsPacketResult result;
1130
1131                         while (!((result = ProcessSubPacket()) is TdsPacketEndTokenResult)) {
1132                                 if (result is TdsPacketErrorResult) {
1133                                         isOkay = false;
1134                                 }
1135                                 // XXX Should really process some more types of packets.
1136                         }
1137
1138                         if (isOkay) {
1139                                 // XXX Should we move this to the Connection class?
1140                                 //isOkay = initSettings(_database);
1141                         }
1142
1143                         // XXX Possible bug.  What happend if this is cancelled before the logon
1144                         // takes place?  Should isOkay be false?
1145                         return isOkay;
1146                         
1147                 }
1148
1149                 // This packet is documented at 
1150                 // http://www.freetds.org/tds.htm#login7
1151                 public void Send70Logon (TdsConnectionParameters parms)
1152                 {
1153                         byte[] empty = new byte[0];
1154                         byte pad = (byte) 0;
1155
1156                         byte[] magic1 = {0x06, 0x83, 0xf2, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x88, 0xff, 0xff, 0xff, 0x36, 0x04, 0x00, 0x00};
1157                         byte[] magic2 = {0x00, 0x40, 0x33, 0x9a, 0x6b, 0x50};
1158                         byte[] magic3 = {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50}; // NTLMSSP
1159                         short partialPacketSize = (short) (86 + 2 * (
1160                                         hostname.Length + 
1161                                         user.Length + 
1162                                         applicationName.Length + 
1163                                         password.Length + 
1164                                         server.Length +
1165                                         libraryName.Length +
1166                                         language.Length +
1167                                         connectDB.Length)); 
1168                         short totalPacketSize = (short) (partialPacketSize + 48);
1169                         comm.StartPacket (TdsPacketType.Logon70);
1170                         comm.Append (totalPacketSize);
1171                         comm.Append (empty, 5, pad);
1172
1173                         if (tdsVersion == TdsVersion.tds80)
1174                                 comm.Append ((byte) 0x80);
1175                         else
1176                                 comm.Append ((byte) 0x70);
1177
1178                         comm.Append (empty, 7, pad);
1179                         comm.Append (magic1);
1180
1181                         short curPos = 86;
1182
1183                         // Hostname 
1184                         comm.Append (curPos);
1185                         comm.Append ((short) hostname.Length);
1186                         curPos += (short) (hostname.Length * 2);
1187
1188                         // Username
1189                         comm.Append (curPos);
1190                         comm.Append ((short) user.Length);
1191                         curPos += (short) (user.Length * 2);
1192
1193                         // Password
1194                         comm.Append (curPos);
1195                         comm.Append ((short) password.Length);
1196                         curPos += (short) (password.Length * 2);
1197
1198                         // AppName
1199                         comm.Append (curPos);
1200                         comm.Append ((short) applicationName.Length);
1201                         curPos += (short) (applicationName.Length * 2);
1202
1203                         // Server Name
1204                         comm.Append (curPos);
1205                         comm.Append ((short) server.Length);
1206                         curPos += (short) (server.Length * 2);
1207
1208                         // Unknown
1209                         comm.Append ((short) 0);
1210                         comm.Append ((short) 0);
1211
1212                         // Library Name
1213                         comm.Append (curPos);
1214                         comm.Append ((short) libraryName.Length);
1215                         curPos += (short) (libraryName.Length * 2);
1216
1217                         // Character Set
1218                         comm.Append (curPos);
1219                         comm.Append ((short) language.Length);
1220                         curPos += (short) (language.Length * 2);
1221
1222                         // Database
1223                         comm.Append (curPos);
1224                         comm.Append ((short) connectDB.Length);
1225                         curPos += (short) (connectDB.Length * 2);
1226
1227                         comm.Append (magic2);
1228                         comm.Append (partialPacketSize);
1229                         comm.Append ((short) 48);
1230                         comm.Append (totalPacketSize);
1231                         comm.Append ((short) 0);
1232
1233                         string scrambledPwd = Tds7CryptPass (password);
1234
1235                         comm.Append (hostname);
1236                         comm.Append (user);
1237                         comm.Append (scrambledPwd);
1238                         comm.Append (applicationName);
1239                         comm.Append (server);
1240                         comm.Append (libraryName);
1241                         comm.Append (language);
1242                         comm.Append (connectDB);
1243                         comm.Append (magic3);
1244
1245                         comm.Append ((byte) 0x0);
1246                         comm.Append ((byte) 0x1);
1247                         comm.Append (empty, 3, pad);
1248                         comm.Append ((byte) 0x6);
1249                         comm.Append ((byte) 0x82);
1250                         comm.Append (empty, 22, pad);
1251                         comm.Append ((byte) 0x30);
1252                         comm.Append (empty, 7, pad);
1253                         comm.Append ((byte) 0x30);
1254                         comm.Append (empty, 3, pad);
1255                 }
1256
1257                 private string SqlStatementForSettings (bool autoCommit, IsolationLevel isolationLevel)
1258                 {
1259                         if (autoCommit == this.autoCommit && isolationLevel == this.isolationLevel)
1260                                 return null;
1261                         StringBuilder res = new StringBuilder ();
1262                         if (autoCommit != this.autoCommit) {
1263                                 this.autoCommit = autoCommit;
1264                                 res.Append (SqlStatementToSetCommit ());
1265                                 res.Append (' ');
1266                         }
1267                         if (isolationLevel != this.isolationLevel) {
1268                                 this.isolationLevel = isolationLevel;
1269                                 res.Append (SqlStatementToSetIsolationLevel ());
1270                                 res.Append (' ');
1271                         }
1272                         return res.ToString ();
1273                 }
1274
1275                 private string SqlStatementToSetCommit ()
1276                 {
1277                         string result;
1278                         if (serverType == TdsServerType.Sybase) {
1279                                 if (autoCommit) 
1280                                         result = "set CHAINED off";
1281                                 else
1282                                         result = "set CHAINED on";
1283                         }
1284                         else {
1285                                 if (autoCommit)
1286                                         result = "set implicit_transactions off";
1287                                 else
1288                                         result = "set implicit_transactions on";
1289                         }
1290                         return result;
1291                 }
1292
1293                 [MonoTODO]
1294                 private string SqlStatementToSetIsolationLevel ()
1295                 {
1296                         string result = "";
1297                         return result;
1298                 }
1299
1300                 private static string Tds7CryptPass (string pass)
1301                 {
1302                         int xormask = 0x5a5a;
1303                         int len = pass.Length;
1304                         char[] chars = new char[len];
1305
1306                         for (int i = 0; i < len; ++i) {
1307                                 int c = ((int) (pass[i])) ^ xormask;
1308                                 int m1 = (c >> 4) & 0x0f0f;
1309                                 int m2 = (c << 4) & 0xf0f0;
1310                                 chars[i] = (char) (m1 | m2);
1311                         }
1312
1313                         return new String (chars);
1314                 }
1315
1316                 #endregion // Methods
1317         }
1318 }