* Tds.cs: Avoid accessing TdsDataColumnCollection indexer. On 2.0
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds.cs
1 //
2 // Mono.Data.Tds.Protocol.Tds.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Sebastien Pouliot (spouliot@motus.com)
7 //   Daniel Morgan (danielmorgan@verizon.net)
8 //
9 // Copyright (C) 2002 Tim Coleman
10 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
11 // Portions (C) 2003,2005 Daniel Morgan
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using Mono.Security.Protocol.Ntlm;
36 using System;
37 using System.IO;
38 using System.Collections;
39 using System.ComponentModel;
40 using System.Diagnostics;
41 using System.Net.Sockets;
42 using System.Globalization;
43 using System.Text;
44
45 namespace Mono.Data.Tds.Protocol
46 {
47         public abstract class Tds
48         {
49                 #region Fields
50
51                 TdsComm comm;
52                 TdsVersion tdsVersion;
53                 
54                 protected internal TdsConnectionParameters connectionParms;
55                 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
56
57                 int packetSize;
58                 string dataSource;
59                 string database;
60                 string originalDatabase = string.Empty;
61                 string databaseProductName;
62                 string databaseProductVersion;
63                 int databaseMajorVersion;
64                 CultureInfo locale = CultureInfo.InvariantCulture;
65
66                 string charset;
67                 string language;
68
69                 bool connected;
70                 bool moreResults;
71
72                 Encoding encoder;
73 //              bool autoCommit;
74
75                 bool doneProc;
76                 bool pooling = true;
77                 TdsDataRow currentRow;
78                 TdsDataColumnCollection columns;
79
80                 ArrayList tableNames;
81                 ArrayList columnNames;
82
83                 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
84
85                 bool queryInProgress;
86                 int cancelsRequested;
87                 int cancelsProcessed;
88
89 //              bool isDone;
90 //              bool isDoneInProc;
91
92                 ArrayList outputParameters = new ArrayList ();
93                 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
94
95                 int recordsAffected = -1;
96
97                 long StreamLength;
98                 long StreamIndex;
99                 int StreamColumnIndex;
100
101                 bool sequentialAccess;
102                 bool isRowRead;
103                 bool isResultRead;
104                 bool LoadInProgress;
105
106                 internal int poolStatus = 0;
107
108                 #endregion // Fields
109
110                 #region Properties
111
112                 protected string Charset {
113                         get { return charset; }
114                 }
115
116                 protected CultureInfo Locale {
117                         get { return locale; }
118                 }
119
120                 public bool DoneProc {
121                         get { return doneProc; }
122                 }
123
124                 protected string Language {
125                         get { return language; }
126                 }
127
128                 protected ArrayList ColumnNames {
129                         get { return columnNames; }
130                 }
131
132                 public TdsDataRow ColumnValues {
133                         get { return currentRow; }
134                 }
135
136                 internal TdsComm Comm {
137                         get { return comm; }
138                 }
139
140                 public string Database {
141                         get { return database; }
142                 }
143
144                 public string DataSource {
145                         get { return dataSource; }
146                 }
147
148                 public bool IsConnected {
149                         get { return connected; }
150                         set { connected = value; }
151                 }
152
153                 public bool Pooling {
154                         get { return pooling; }
155                         set { pooling = value; }
156                 }
157
158                 public bool MoreResults {
159                         get { return moreResults; }
160                         set { moreResults = value; }
161                 }
162
163                 public int PacketSize {
164                         get { return packetSize; }
165                 }
166
167                 public int RecordsAffected {
168                         get { return recordsAffected; }
169                         set { recordsAffected = value; }
170                 }
171
172                 public string ServerVersion {
173                         get { return databaseProductVersion; }
174                 }
175
176                 public TdsDataColumnCollection Columns {
177                         get { return columns; }
178                 }
179
180                 public TdsVersion TdsVersion {
181                         get { return tdsVersion; }
182                 }
183
184                 public ArrayList OutputParameters {
185                         get { return outputParameters; }
186                         set { outputParameters = value; }
187                 }
188
189                 protected TdsMetaParameterCollection Parameters {
190                         get { return parameters; }
191                         set { parameters = value; }
192                 }
193
194                 public bool SequentialAccess {
195                         get { return sequentialAccess; }
196                         set { sequentialAccess = value; }
197                 }
198
199                 private void SkipRow ()
200                 {
201                         SkipToColumnIndex (Columns.Count);
202
203                         StreamLength = 0;
204                         StreamColumnIndex = 0;
205                         StreamIndex = 0;
206                         LoadInProgress = false;
207                 }
208
209                 private void SkipToColumnIndex (int colIndex)
210                 {
211                         if (LoadInProgress)
212                                 EndLoad ();
213
214                         if (colIndex < StreamColumnIndex)
215                                 throw new Exception ("Cannot Skip to a colindex less than the curr index");
216
217                         while (colIndex != StreamColumnIndex) {
218 #if NET_2_0
219                                 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
220                                 if (colType == null)
221                                         throw new Exception ("Column type unset.");
222 #else
223                                 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
224 #endif
225                                 if (!(colType == TdsColumnType.Image ||
226                                         colType == TdsColumnType.Text ||
227                                         colType == TdsColumnType.NText)) {
228                                         GetColumnValue (colType, false, StreamColumnIndex);
229                                         StreamColumnIndex ++;
230                                 }
231                                 else {
232                                         BeginLoad (colType);
233                                         Comm.Skip (StreamLength);
234                                         StreamLength = 0;
235                                         EndLoad ();
236                                 }
237                         }
238                 }
239
240                 public object GetSequentialColumnValue (int colIndex)
241                 {
242                         if (colIndex < StreamColumnIndex)
243                                 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex); 
244
245                         if (LoadInProgress)
246                                 EndLoad ();
247
248                         if (colIndex != StreamColumnIndex)
249                                 SkipToColumnIndex (colIndex);
250
251 #if NET_2_0
252                         object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
253 #else
254                         object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
255 #endif
256                         StreamColumnIndex++;
257                         return o;
258                 }
259
260                 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size) 
261                 {
262                         if (colIndex < StreamColumnIndex)
263                                 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex); 
264                         try {
265                                 if (colIndex != StreamColumnIndex) 
266                                         SkipToColumnIndex (colIndex);
267
268                                 if (!LoadInProgress) {
269 #if NET_2_0
270                                         BeginLoad (Columns[colIndex].ColumnType);
271 #else
272                                         BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
273 #endif
274                                 }
275                                 
276                                 if (buffer == null) {
277                                         return StreamLength;
278                                 }
279                                 return LoadData (fieldIndex, buffer, bufferIndex, size);
280                         } catch (IOException ex) {
281                                 connected = false;
282                                 throw new TdsInternalException ("Server closed the connection.", ex);
283                         }
284                 }
285
286                 private void BeginLoad (
287 #if NET_2_0
288                         TdsColumnType? colType
289 #else
290                         TdsColumnType colType
291 #endif
292                 ) 
293                 {
294                         if (LoadInProgress)
295                                 EndLoad ();
296
297                         StreamLength = 0;
298
299 #if NET_2_0
300                         if (colType == null)
301                                 throw new ArgumentNullException ("colType");
302 #endif
303                         switch (colType) {
304                         case TdsColumnType.Text :
305                         case TdsColumnType.NText:
306                         case TdsColumnType.Image:
307                                 if (Comm.GetByte () != 0) {
308                                         Comm.Skip (24);
309                                         StreamLength = Comm.GetTdsInt ();
310                                 }
311                                 break;
312                         case TdsColumnType.BigVarChar:
313                         case TdsColumnType.BigChar:
314                         case TdsColumnType.BigBinary:
315                         case TdsColumnType.BigVarBinary:
316                                 Comm.GetTdsShort ();
317                                 StreamLength = Comm.GetTdsShort ();
318                                 break;
319                         case TdsColumnType.VarChar :
320                         case TdsColumnType.NVarChar :
321                         case TdsColumnType.Char:
322                         case TdsColumnType.NChar:
323                         case TdsColumnType.Binary:
324                         case TdsColumnType.VarBinary:
325                                 StreamLength = Comm.GetTdsShort ();
326                                 break;
327                         default :
328                                 StreamLength = -1;
329                                 break;
330                         }
331
332                         StreamIndex = 0;
333                         LoadInProgress = true;
334                 }
335
336                 private void EndLoad()
337                 {
338                         if (StreamLength > 0)
339                                 Comm.Skip (StreamLength);
340                         StreamLength = 0;
341                         StreamIndex = 0;
342                         StreamColumnIndex++;
343                         LoadInProgress = false;
344                 }
345
346                 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
347                 {
348                         if (StreamLength <= 0)
349                                 return StreamLength;
350
351                         if (fieldIndex < StreamIndex)
352                                 throw new InvalidOperationException ("field index less than stream pos");
353
354                         if (fieldIndex >= (StreamLength + StreamIndex))
355                                 return 0;
356
357                         // Skip to the index
358                         Comm.Skip ((int) (fieldIndex - StreamIndex));
359                         StreamIndex += (fieldIndex - StreamIndex);
360
361                         // Load the reqd amt of bytes   
362                         int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
363                         byte[] arr = Comm.GetBytes (loadlen, true);
364
365                         // update the index and stream length
366                         StreamIndex +=  loadlen + (fieldIndex - StreamIndex);
367                         StreamLength -= loadlen;
368                         arr.CopyTo (buffer, bufferIndex);
369
370                         return arr.Length;
371                 }
372
373                 #endregion // Properties
374
375                 #region Events
376
377                 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
378                 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
379
380                 #endregion // Events
381
382                 #region Constructors
383
384                 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
385                 {
386                         this.tdsVersion = tdsVersion;
387                         this.packetSize = packetSize;
388                         this.dataSource = dataSource;
389
390                         comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
391                 }
392
393                 #endregion // Constructors
394
395                 #region Public Methods
396
397                 internal protected void InitExec () 
398                 {
399                         // clean up 
400                         moreResults = true;
401                         doneProc = false;
402                         messages.Clear ();
403                         outputParameters.Clear ();
404                 }
405
406                 public void Cancel ()
407                 {
408                         if (queryInProgress) {
409                                 if (cancelsRequested == cancelsProcessed) {
410                                         comm.StartPacket (TdsPacketType.Cancel);
411                                         try {
412                                                 Comm.SendPacket ();
413                                         } catch (IOException ex) {
414                                                 connected = false;
415                                                 throw new TdsInternalException ("Server closed the connection.", ex);
416                                         }
417                                         cancelsRequested += 1;
418                                 }
419                         }
420                 }
421         
422                 public abstract bool Connect (TdsConnectionParameters connectionParameters);
423
424                 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
425                 {
426                         string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
427                         return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
428                 }
429
430                 public void Disconnect ()
431                 {
432                         comm.StartPacket (TdsPacketType.Logoff);
433                         comm.Append ((byte) 0);
434                         comm.SendPacket ();     
435                         comm.Close ();
436                         connected = false;
437                 }
438                 
439                 public virtual bool Reset ()
440                 {
441                         database = originalDatabase;
442                         return true;
443                 }
444
445                 protected virtual bool IsValidRowCount (byte status, byte op)
446                 {
447                         return ((status & (0x10)) != 0) ;
448                 }
449
450                 public void Execute (string sql)
451                 {
452                         Execute (sql, null, 0, false);
453                 }
454
455                 public void ExecProc (string sql)
456                 {
457                         ExecProc (sql, null, 0, false);
458                 }
459
460                 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
461                 {
462                         ExecuteQuery (sql, timeout, wantResults);       
463                 }
464
465                 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
466                 {
467                         ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
468                 }
469
470                 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
471                 {
472                         throw new NotSupportedException ();
473                 }
474
475                 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
476                 {
477                         moreResults = true;
478                         try {
479                                 Comm.SendPacket ();
480                                 CheckForData (timeout);
481                                 if (!wantResults) 
482                                         SkipToEnd ();
483                         } catch (IOException ex) {
484                                 connected = false;
485                                 throw new TdsInternalException ("Server closed the connection.", ex);
486                         }
487                 }
488
489                 internal void ExecBulkCopy (int timeout, bool wantResults)
490                 {
491                         moreResults = true;
492                         try {
493                                 Comm.SendPacket ();
494                                 CheckForData (timeout);
495                                 if (!wantResults) 
496                                         SkipToEnd ();
497                         } catch (IOException ex) {
498                                 connected = false;
499                                 throw new TdsInternalException ("Server closed the connection.", ex);
500                         }
501                 }
502
503                 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
504                 {
505                         InitExec ();
506
507                         Comm.StartPacket (TdsPacketType.Query);
508                         Comm.Append (sql);
509                         try {
510                                 Comm.SendPacket ();
511                                 CheckForData (timeout);
512                                 if (!wantResults) 
513                                         SkipToEnd ();
514                         } catch (IOException ex) {
515                                 connected = false;
516                                 throw new TdsInternalException ("Server closed the connection.", ex);
517                         }
518                 }
519
520                 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
521                                                 int timeout, bool wantResults)
522                 {
523                         Comm.StartPacket (TdsPacketType.DBRPC);
524
525                         byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
526                         byte rpcNameLength = (byte) rpcNameBytes.Length;
527                         ushort mask = 0x0000;
528                         ushort packetLength =  (ushort) (sizeof (byte) + rpcNameLength +
529                                                 sizeof (ushort));
530
531                         Comm.Append (packetLength);
532                         Comm.Append (rpcNameLength);
533                         Comm.Append (rpcNameBytes);
534                         Comm.Append (mask);
535                         
536                         try {
537                                 Comm.SendPacket ();
538                                 CheckForData (timeout);
539                                 if (!wantResults) 
540                                         SkipToEnd ();
541                         } catch (IOException ex) {
542                                 connected = false;
543                                 throw new TdsInternalException ("Server closed the connection.", ex);
544                         }
545                 }
546
547                 public bool NextResult ()
548                 {
549                         if (SequentialAccess) {
550                                 if (isResultRead) {
551                                         while (NextRow ()) {}
552                                         isRowRead = false;
553                                         isResultRead = false;
554                                 }
555                         }
556                         if (!moreResults)
557                                 return false;
558
559                         TdsPacketSubType subType;
560
561                         bool done = false;
562                         bool outputParams = false;
563
564                         while (!done) {
565                                 subType = ProcessSubPacket ();
566                                 if (outputParams) {
567                                         moreResults = false;
568                                         break;
569                                 }
570                                 switch (subType) {
571                                 case TdsPacketSubType.ColumnInfo:
572                                 case TdsPacketSubType.ColumnMetadata: 
573                                 case TdsPacketSubType.RowFormat:
574                                         byte peek = Comm.Peek ();
575                                         done = (peek != (byte) TdsPacketSubType.TableName);
576                                         if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
577                                                 outputParams = true;
578                                                 done = false;
579                                         }
580                                         break;
581                                 case TdsPacketSubType.TableName:
582                                 //      done = true;
583                                         peek = Comm.Peek ();
584                                         done = (peek != (byte) TdsPacketSubType.ColumnDetail);
585                                         break;
586                                 case TdsPacketSubType.ColumnDetail:
587                                         done = true;
588                                         break;
589                                 default:
590                                         done = !moreResults;
591                                         break;
592                                 }
593                         }
594
595                         return moreResults;
596                 }
597
598                 public bool NextRow ()
599                 {
600                         if (SequentialAccess) {
601                                 if (isRowRead) {
602                                         SkipRow ();
603                                         isRowRead = false;
604                                 }
605                         }
606
607                         TdsPacketSubType subType;
608                         bool done = false;
609                         bool result = false;
610
611                         do {
612                                 subType = ProcessSubPacket ();
613                                 switch (subType) {
614                                 case TdsPacketSubType.Row:
615                                         result = true;
616                                         done = true;
617                                         break;
618                                 case TdsPacketSubType.Done:
619                                 case TdsPacketSubType.DoneProc:
620                                 case TdsPacketSubType.DoneInProc:
621                                         result = false;
622                                         done = true;
623                                         break;
624                                 }
625                         } while (!done);
626
627                         return result;
628                 }
629
630                 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
631                 {
632                         throw new NotSupportedException ();
633                 }
634
635                 public void SkipToEnd ()
636                 {
637                         try {
638                                 while (NextResult ()) { /* DO NOTHING */ }
639                         } catch (IOException ex) {
640                                 connected = false;
641                                 throw new TdsInternalException ("Server closed the connection.", ex);
642                         }
643                 }
644
645                 public virtual void Unprepare (string statementId) 
646                 {
647                         throw new NotSupportedException ();
648                 }
649
650                 #endregion // Public Methods
651
652                 #region // Private Methods
653
654                 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
655                 protected void CheckForData (int timeout) 
656                 {
657                         if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
658                                 Cancel ();
659                                 throw CreateTimeoutException (dataSource, "CheckForData()");
660                         }
661                 }
662         
663                 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
664                 {
665                         return new TdsInternalInfoMessageEventArgs (errors);
666                 }
667
668                 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
669                 {
670                         return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
671                 }
672
673                 private object GetColumnValue (
674 #if NET_2_0
675                         TdsColumnType? colType,
676 #else
677                         TdsColumnType colType,
678 #endif
679                         bool outParam)
680                 {
681                         return GetColumnValue (colType, outParam, -1);
682                 }
683
684                 private object GetColumnValue (
685 #if NET_2_0
686                         TdsColumnType? colType,
687 #else
688                         TdsColumnType colType,
689 #endif
690                         bool outParam, int ordinal)
691                 {
692                         int len;
693                         object element = null;
694
695 #if NET_2_0
696                         if (colType == null)
697                                 throw new ArgumentNullException ("colType");
698 #endif
699                         switch (colType) {                              
700                         case TdsColumnType.IntN :
701                                 if (outParam)
702                                         comm.Skip (1);
703                                 element = GetIntValue (colType);
704                                 break;
705                         case TdsColumnType.Int1 :
706                         case TdsColumnType.Int2 :
707                         case TdsColumnType.Int4 :
708                                 element = GetIntValue (colType);
709                                 break;
710                         case TdsColumnType.Image :
711                                 if (outParam)
712                                         comm.Skip (1);
713                                 element = GetImageValue ();
714                                 break;
715                         case TdsColumnType.Text :
716                                 if (outParam) 
717                                         comm.Skip (1);
718                                 element = GetTextValue (false);
719                                 break;
720                         case TdsColumnType.NText :
721                                 if (outParam) 
722                                         comm.Skip (1);
723                                 element = GetTextValue (true);
724                                 break;
725                         case TdsColumnType.Char :
726                         case TdsColumnType.VarChar :
727                                 if (outParam)
728                                         comm.Skip (1);
729                                 element = GetStringValue (false, false);
730                                 break;
731                         case TdsColumnType.BigVarBinary :
732                                 if (outParam)
733                                         comm.Skip (1);
734                                 len = comm.GetTdsShort ();
735                                 element = comm.GetBytes (len, true);
736                                 break;
737                                 /*
738                         case TdsColumnType.BigBinary :
739                                 if (outParam)
740                                         comm.Skip (2);
741                                 len = comm.GetTdsShort ();
742                                 element = comm.GetBytes (len, true);
743                                 break;
744                                 */
745                         case TdsColumnType.BigBinary :
746                                 if (outParam)
747                                         comm.Skip (2);
748                                 element = GetBinaryValue ();
749                                 break;
750                         case TdsColumnType.BigChar :
751                         case TdsColumnType.BigVarChar :
752                                 if (outParam)
753                                         comm.Skip (2);
754                                 element = GetStringValue (false, false);
755                                 break;
756                         case TdsColumnType.NChar :
757                         case TdsColumnType.BigNVarChar :
758                                 if (outParam)
759                                         comm.Skip(2);
760                                 element = GetStringValue (true, false);
761                                 break;
762                         case TdsColumnType.NVarChar :
763                                 if (outParam) 
764                                         comm.Skip (1);
765                                 element = GetStringValue (true, false);
766                                 break;
767                         case TdsColumnType.Real :
768                         case TdsColumnType.Float8 :
769                                 element = GetFloatValue (colType);
770                                 break;
771                         case TdsColumnType.FloatN :
772                                 if (outParam) 
773                                         comm.Skip (1);
774                                 element = GetFloatValue (colType);
775                                 break;
776                         case TdsColumnType.SmallMoney :
777                         case TdsColumnType.Money :
778                                 element = GetMoneyValue (colType);
779                                 break;
780                         case TdsColumnType.MoneyN :
781                                 if (outParam)
782                                         comm.Skip (1);
783                                 element = GetMoneyValue (colType);
784                                 break;
785                         case TdsColumnType.Numeric :
786                         case TdsColumnType.Decimal :
787                                 byte precision;
788                                 byte scale;
789                                 if (outParam) {
790                                         comm.Skip (1);
791                                         precision = comm.GetByte ();
792                                         scale = comm.GetByte ();
793                                 }
794                                 else {
795 #if NET_2_0
796                                         precision = (byte) columns[ordinal].NumericPrecision;
797                                         scale = (byte) columns[ordinal].NumericScale;
798 #else
799                                         precision = (byte) columns[ordinal]["NumericPrecision"];
800                                         scale = (byte) columns[ordinal]["NumericScale"];
801 #endif
802                                 }
803
804                                 element = GetDecimalValue (precision, scale);
805                                 
806                                 // workaround for fact that TDS 7.0 returns
807                                 // bigint as decimal (19,0), and client code
808                                 // expects it to be returned as a long
809                                 if (scale == 0 && precision <= 19) {
810                                         if (!(element is System.DBNull))
811                                                 element = Convert.ToInt64 (element);
812                                 }
813                                 break;
814                         case TdsColumnType.DateTimeN :
815                                 if (outParam) 
816                                         comm.Skip (1);
817                                 element = GetDateTimeValue (colType);
818                                 break;
819                         case TdsColumnType.DateTime4 :
820                         case TdsColumnType.DateTime :
821                                 element = GetDateTimeValue (colType);
822                                 break;
823                         case TdsColumnType.VarBinary :
824                         case TdsColumnType.Binary :
825                                 if (outParam) 
826                                         comm.Skip (1);
827                                 element = GetBinaryValue ();
828                                 break;
829                         case TdsColumnType.BitN :
830                                 if (outParam) 
831                                         comm.Skip (1);
832                                 if (comm.GetByte () == 0)
833                                         element = DBNull.Value;
834                                 else
835                                         element = (comm.GetByte() != 0);
836                                 break;
837                         case TdsColumnType.Bit :
838                                 int columnSize = comm.GetByte ();
839                                 element = (columnSize != 0);
840                                 break;
841                         case TdsColumnType.UniqueIdentifier :
842                                 if (comm.Peek () != 16) { // If it's null, then what to do?
843                                         /*byte swallowed =*/ comm.GetByte();
844                                         element = DBNull.Value;
845                                         break;
846                                 }
847                                 if (outParam)
848                                         comm.Skip (1);
849                                 
850                                 len = comm.GetByte () & 0xff;
851                                 if (len > 0) {
852                                         byte[] guidBytes = comm.GetBytes (len, true);
853                                         if (!BitConverter.IsLittleEndian) {
854                                                 byte[] swappedguidBytes = new byte[len];
855                                                 for (int i = 0; i < 4; i++)
856                                                         swappedguidBytes[i] = guidBytes[4-i-1];
857                                                 for (int i = 4; i < 6; i++)
858                                                         swappedguidBytes[i] = guidBytes[6-(i-4)-1];
859                                                 for (int i = 6; i < 8; i++)
860                                                         swappedguidBytes[i] = guidBytes[8-(i-6)-1];
861                                                 for (int i = 8; i < 16; i++)
862                                                         swappedguidBytes[i] = guidBytes[i];
863                                                 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
864                                         }
865                                         element = new Guid (guidBytes);
866                                 }
867                                 break;
868                         default :
869                                 return DBNull.Value;
870                         }
871                         return element;
872                 }
873
874                 private object GetBinaryValue ()
875                 {
876                         int len;
877                         object result = DBNull.Value;
878
879                         if (tdsVersion == TdsVersion.tds70) {
880                                 len = comm.GetTdsShort ();
881                                 if (len != 0xffff && len > 0)
882                                         result = comm.GetBytes (len, true);
883                         } else {
884                                 len = (comm.GetByte () & 0xff);
885                                 if (len != 0)
886                                         result = comm.GetBytes (len, true);
887                         }
888
889                         return result;
890                 }
891
892                 private object GetDateTimeValue (
893 #if NET_2_0
894                         TdsColumnType? type
895 #else
896                         TdsColumnType type
897 #endif
898                 )
899                 {
900                         int len = 0;
901                         object result;
902
903 #if NET_2_0
904                         if (type == null)
905                                 throw new ArgumentNullException ("type");
906 #endif
907                         switch (type) {
908                         case TdsColumnType.DateTime4:
909                                 len = 4;
910                                 break;
911                         case TdsColumnType.DateTime:
912                                 len = 8;
913                                 break;
914                         case TdsColumnType.DateTimeN:
915                                 byte tmp = comm.Peek ();
916                                 if (tmp != 0 && tmp != 4 && tmp != 8)
917                                         break;
918                                 len = comm.GetByte ();
919                                 break;
920                         }
921         
922                         DateTime epoch = new DateTime (1900, 1, 1);
923         
924                         switch (len) {
925                         case 8 :
926                                 result = epoch.AddDays (comm.GetTdsInt ());
927                                 int seconds = comm.GetTdsInt ();
928                                 long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f);
929                                 if (seconds != 0 || millis != 0) {
930                                         result = ((DateTime) result).AddSeconds (seconds / 300);
931                                         result = ((DateTime) result).AddMilliseconds (millis);
932                                 }
933                                 break;
934                         case 4 :
935                                 // MSDN says small datetime is stored in 2 bytes as no of days
936                                 // *after* 1/1/1900. so, cast to unsigned short
937                                 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
938                                 short minutes = comm.GetTdsShort ();
939                                 if (minutes != 0) 
940                                         result = ((DateTime) result).AddMinutes ((int) minutes);
941                                 break;
942                         default:
943                                 result = DBNull.Value;
944                                 break;
945                         }
946
947                         return result;
948                 }
949
950                 private object GetDecimalValue (byte precision, byte scale)
951                 {
952                         if (tdsVersion < TdsVersion.tds70)
953                                 return GetDecimalValueTds50 (precision, scale);
954                         else
955                                 return GetDecimalValueTds70 (precision, scale);
956                 }
957                 
958                 private object GetDecimalValueTds70 (byte precision, byte scale)
959                 {
960                         int[] bits = new int[4] {0,0,0,0};
961
962                         int len = (comm.GetByte() & 0xff) - 1;
963                         if (len < 0)
964                                 return DBNull.Value;
965                         
966                         bool positive = (comm.GetByte () == 1);
967                         if (len > 16)
968                                 throw new OverflowException ();
969
970                         for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1) 
971                                 bits[index] = comm.GetTdsInt ();
972                         
973                         if (bits [3] != 0) 
974                                 return new TdsBigDecimal (precision, scale, !positive, bits);
975                         else
976                                 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
977                 }
978
979                 private object GetDecimalValueTds50 (byte precision, byte scale)
980                 {
981                         int[] bits = new int[4] {0,0,0,0};
982
983                         int len = (comm.GetByte() & 0xff);
984                         if (len == 0)
985                                 return DBNull.Value;
986
987                         byte[] dec_bytes=comm.GetBytes(len,false);
988                 
989                         byte[] easy=new byte[4];
990
991                         bool positive = dec_bytes[0]==1;
992
993                         if (len > 17)
994                                 throw new OverflowException ();
995
996                         for (int i = 1, index = 0; i < len && i < 16; i += 
997                                 4, index += 1) {
998                                 for(int j=0; j<4; j++)
999                                         if(i+j<len)
1000                                                 easy[j]=dec_bytes[len-
1001                                                         (i+j)];
1002                                         else
1003                                                 easy[j]=0;
1004                                 if(!BitConverter.IsLittleEndian)
1005                                         easy=comm.Swap(easy);
1006                                 bits[index] = BitConverter.ToInt32(easy,0);
1007                         }
1008                         if (bits [3] != 0) 
1009                                 return new TdsBigDecimal (precision, 
1010                                         scale, positive, bits);
1011                         else
1012                                 return new Decimal(bits[0], bits[1], bits
1013                                         [2], positive, scale);
1014                         
1015                 }
1016
1017                 private object GetFloatValue (
1018 #if NET_2_0
1019                         TdsColumnType? columnType
1020 #else
1021                         TdsColumnType columnType
1022 #endif
1023                 )
1024                 {
1025 #if NET_2_0
1026                         if (columnType == null)
1027                                 throw new ArgumentNullException ("columnType");
1028 #endif
1029                         int columnSize = 0;
1030
1031                         switch (columnType) {
1032                         case TdsColumnType.Real:
1033                                 columnSize = 4;
1034                                 break;
1035                         case TdsColumnType.Float8:
1036                                 columnSize = 8;
1037                                 break;
1038                         case TdsColumnType.FloatN:
1039                                 columnSize = comm.GetByte ();
1040                                 break;
1041                         }
1042
1043                         switch (columnSize) {
1044                         case 8 :
1045                                 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1046                         case 4 :
1047                                 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1048                         default :
1049                                 return DBNull.Value;
1050                         }
1051                 }
1052
1053                 private object GetImageValue ()
1054                 {
1055                         byte hasValue = comm.GetByte ();
1056
1057                         if (hasValue == 0)
1058                                 return DBNull.Value;
1059                         
1060                         comm.Skip (24);
1061                         int len = comm.GetTdsInt ();
1062
1063                         if (len < 0)
1064                                 return DBNull.Value;
1065
1066                         return (comm.GetBytes (len, true));
1067                 }
1068
1069                 private object GetIntValue (
1070 #if NET_2_0
1071                         TdsColumnType? type
1072 #else
1073                         TdsColumnType type
1074 #endif
1075                 )
1076                 {
1077                         int len;
1078
1079 #if NET_2_0
1080                         if (type == null)
1081                                 throw new ArgumentNullException ("type");
1082 #endif
1083                         switch (type) {
1084                         case TdsColumnType.IntN :
1085                                 len = comm.GetByte ();
1086                                 break;
1087                         case TdsColumnType.Int4 :
1088                                 len = 4; 
1089                                 break;
1090                         case TdsColumnType.Int2 :
1091                                 len = 2; 
1092                                 break;
1093                         case TdsColumnType.Int1 :
1094                                 len = 1; 
1095                                 break;
1096                         default:
1097                                 return DBNull.Value;
1098                         }
1099
1100                         switch (len) {
1101                         case 4 :
1102                                 return (comm.GetTdsInt ());
1103                         case 2 :
1104                                 return (comm.GetTdsShort ());
1105                         case 1 :
1106                                 return (comm.GetByte ());
1107                         default:
1108                                 return DBNull.Value;
1109                         }
1110                 }
1111
1112                 private object GetMoneyValue (
1113 #if NET_2_0
1114                         TdsColumnType? type
1115 #else
1116                         TdsColumnType type
1117 #endif
1118                 )
1119                 {
1120                         int len;
1121
1122 #if NET_2_0
1123                         if (type == null)
1124                                 throw new ArgumentNullException ("type");
1125 #endif
1126                         switch (type) {
1127                         case TdsColumnType.SmallMoney :
1128                         case TdsColumnType.Money4 :
1129                                 len = 4;
1130                                 break;
1131                         case TdsColumnType.Money :
1132                                 len = 8;
1133                                 break;
1134                         case TdsColumnType.MoneyN :
1135                                 len = comm.GetByte ();
1136                                 break;
1137                         default:
1138                                 return DBNull.Value;
1139                         }
1140
1141                         switch (len) {
1142                         case 4:
1143                                 return new Decimal (Comm.GetTdsInt (), 0, 0, false, 4);
1144                         case 8:
1145                                 int hi = Comm.GetTdsInt ();
1146                                 int lo = Comm.GetTdsInt ();
1147                                 return new Decimal (lo, hi, 0, false, 4);
1148                         default:
1149                                 return DBNull.Value;
1150                         }
1151                 }
1152
1153                 private object GetStringValue (bool wideChars, bool outputParam)
1154                 {
1155                         bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam);
1156                         int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1157
1158                         if (tdsVersion < TdsVersion.tds70 && len == 0)
1159                                 return DBNull.Value;
1160                         else if (len >= 0) {
1161                                 object result;
1162                                 if (wideChars)
1163                                         result = comm.GetString (len / 2);
1164                                 else
1165                                         result = comm.GetString (len, false);
1166                                 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1167                                         result = string.Empty;
1168                                 return result;
1169                         }
1170                         else
1171                                 return DBNull.Value;
1172                 }
1173
1174                 protected int GetSubPacketLength ()
1175                 {
1176                         return comm.GetTdsShort ();
1177                 }
1178
1179                 private object GetTextValue (bool wideChars)
1180                 {
1181                         string result = null;
1182                         byte hasValue = comm.GetByte ();
1183
1184                         if (hasValue != 16)
1185                                 return DBNull.Value;
1186
1187                         // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1188                         comm.Skip (24);
1189
1190                         int len = comm.GetTdsInt ();
1191
1192                         //if the len is 0 , then the string can be a '' string 
1193                         // this method is called only for Text and NText. Hence will
1194                         // return a empty string
1195                         if (len == 0)
1196                                 return string.Empty;
1197
1198                         if (wideChars)
1199                                 result = comm.GetString (len / 2);
1200                         else
1201                                 result = comm.GetString (len, false);
1202                                 len /= 2;
1203
1204                         if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1205                                 result = string.Empty;
1206
1207                         return result;
1208                 }
1209
1210                 internal bool IsBlobType (TdsColumnType columnType)
1211                 {
1212                         return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1213                 }
1214
1215                 internal bool IsLargeType (TdsColumnType columnType)
1216                 {
1217                         return ((byte) columnType > 128);
1218                 }
1219
1220                 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1221                 {
1222                         switch (columnType) {
1223                                 case TdsColumnType.Int1 :
1224                                 case TdsColumnType.Int2 :
1225                                 case TdsColumnType.Int4 :
1226                                 case TdsColumnType.Float8 :
1227                                 case TdsColumnType.DateTime :
1228                                 case TdsColumnType.Bit :
1229                                 case TdsColumnType.Money :
1230                                 case TdsColumnType.Money4 :
1231                                 case TdsColumnType.SmallMoney :
1232                                 case TdsColumnType.Real :
1233                                 case TdsColumnType.DateTime4 :
1234                                   /*
1235                                 case TdsColumnType.Decimal:
1236                                 case TdsColumnType.Numeric:
1237                                   */
1238                                         return true;
1239                                 default :
1240                                         return false;
1241                         }
1242                 }
1243
1244                 protected void LoadRow ()
1245                 {
1246                         if (SequentialAccess) {
1247                                 if (isRowRead)
1248                                         SkipRow ();
1249                                 isRowRead = true;
1250                                 isResultRead = true;
1251                                 return;
1252                         }
1253
1254                         currentRow = new TdsDataRow ();
1255
1256                         int i = 0;
1257                         foreach (TdsDataColumn column in columns) {
1258 #if NET_2_0
1259                                 object o = GetColumnValue (column.ColumnType, false, i);
1260 #else
1261                                 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1262 #endif
1263                                 currentRow.Add (o);
1264                                 if (doneProc)
1265                                         outputParameters.Add (o);
1266
1267                                 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0) 
1268                                         currentRow.BigDecimalIndex = i;
1269                                 i += 1;
1270                         }
1271                 }
1272
1273                 internal static int LookupBufferSize (TdsColumnType columnType)
1274                 {
1275                         switch (columnType) {
1276                                 case TdsColumnType.Int1 :
1277                                 case TdsColumnType.Bit :
1278                                         return 1;
1279                                 case TdsColumnType.Int2 :
1280                                         return 2;
1281                                 case TdsColumnType.Int4 :
1282                                 case TdsColumnType.Real :
1283                                 case TdsColumnType.DateTime4 :
1284                                 case TdsColumnType.Money4 :
1285                                 case TdsColumnType.SmallMoney :
1286                                         return 4;
1287                                 case TdsColumnType.Float8 :
1288                                 case TdsColumnType.DateTime :
1289                                 case TdsColumnType.Money :
1290                                         return 8;
1291                                 default :
1292                                         return 0;
1293                         }
1294                 }
1295
1296                 protected internal int ProcessAuthentication ()
1297                 {
1298                         int pdu_size = Comm.GetTdsShort ();
1299                         byte[] msg2 = Comm.GetBytes (pdu_size, true);
1300
1301                         Type2Message t2 = new Type2Message (msg2);
1302                         // 0x0001       Negotiate Unicode
1303                         // 0x0200       Negotiate NTLM
1304                         // 0x8000       Negotiate Always Sign
1305
1306                         Type3Message t3 = new Type3Message ();
1307                         t3.Challenge = t2.Nonce;
1308                         
1309                         t3.Domain = this.connectionParms.DefaultDomain;
1310                         t3.Host = this.connectionParms.Hostname;
1311                         t3.Username = this.connectionParms.User;
1312                         t3.Password = this.connectionParms.Password;
1313
1314                         Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1315                         Comm.Append (t3.GetBytes ());
1316                         try {
1317                                 Comm.SendPacket ();
1318                         } catch (IOException ex) {
1319                                 connected = false;
1320                                 throw new TdsInternalException ("Server closed the connection.", ex);
1321                         }
1322                         return 1; // TDS_SUCCEED
1323                 }
1324
1325                 protected void ProcessColumnDetail ()
1326                 {
1327                         int len = GetSubPacketLength ();
1328                         byte[] values = new byte[3];
1329                         int columnNameLength;
1330                         string baseColumnName = String.Empty;
1331                         int position = 0;
1332
1333                         while (position < len) {
1334                                 for (int j = 0; j < 3; j += 1) 
1335                                         values[j] = comm.GetByte ();
1336                                 position += 3;
1337
1338                                 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1339                                 if (isAlias) {
1340                                         if (tdsVersion == TdsVersion.tds70) {
1341                                                 columnNameLength = comm.GetByte ();
1342                                                 position += 2 * columnNameLength + 1;
1343                                         }
1344                                         else {
1345                                                 columnNameLength = comm.GetByte ();
1346                                                 position += columnNameLength + 1;
1347                                         }
1348                                         baseColumnName = comm.GetString (columnNameLength);
1349                                 }
1350
1351                                 byte index = (byte) (values[0] - (byte) 1);
1352                                 byte tableIndex = (byte) (values[1] - (byte) 1);
1353                                 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1354
1355                                 TdsDataColumn column = columns [index];
1356 #if NET_2_0
1357                                 column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1358                                 column.IsExpression = isExpression;
1359                                 column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1360                                 column.IsAliased = isAlias;
1361                                 column.BaseColumnName = ((isAlias) ? baseColumnName : null);
1362                                 column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null);
1363 #else
1364                                 column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0);
1365                                 column ["IsExpression"] = isExpression;
1366                                 column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0);
1367                                 column ["IsAliased"] = isAlias;
1368                                 column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1369                                 column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1370 #endif
1371                         }
1372                 }
1373
1374                 protected abstract TdsDataColumnCollection ProcessColumnInfo ();
1375
1376                 protected void ProcessColumnNames ()
1377                 {
1378                         columnNames = new ArrayList ();
1379
1380                         int totalLength = comm.GetTdsShort ();
1381                         int bytesRead = 0;
1382                         int i = 0;
1383
1384                         while (bytesRead < totalLength) {
1385                                 int columnNameLength = comm.GetByte ();
1386                                 string columnName = comm.GetString (columnNameLength);
1387                                 bytesRead = bytesRead + 1 + columnNameLength;
1388                                 columnNames.Add (columnName);
1389                                 i += 1;
1390                         }
1391                 }
1392
1393                 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1394                 protected void ProcessEndToken (TdsPacketSubType type)
1395                 {
1396                         byte status = Comm.GetByte ();
1397                         Comm.Skip (1);
1398                         byte op = comm.GetByte ();
1399                         Comm.Skip (1);
1400
1401                         int rowCount = comm.GetTdsInt ();
1402                         bool validRowCount = IsValidRowCount (status,op);
1403                         moreResults = ((status & 0x01) != 0);
1404                         bool cancelled = ((status & 0x20) != 0);
1405
1406                         switch (type) {
1407                         case TdsPacketSubType.DoneProc:
1408                                 doneProc = true;
1409                                 goto case TdsPacketSubType.Done;
1410                         case TdsPacketSubType.Done:
1411                         case TdsPacketSubType.DoneInProc:
1412                                 if (validRowCount) {
1413                                         if (recordsAffected == -1) 
1414                                                 recordsAffected = rowCount;
1415                                         else
1416                                                 recordsAffected += rowCount;
1417                                 }
1418                                 break;
1419                         }
1420
1421                         if (moreResults) 
1422                                 queryInProgress = false;
1423                         if (cancelled)
1424                                 cancelsProcessed += 1;
1425                         if (messages.Count > 0 && !moreResults) 
1426                                 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1427                 }
1428
1429                 protected void ProcessEnvironmentChange ()
1430                 {
1431                         int len = GetSubPacketLength ();
1432                         TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1433                         int cLen;
1434
1435                         switch (type) {
1436                         case TdsEnvPacketSubType.BlockSize :
1437                                 string blockSize;
1438                                 cLen = comm.GetByte ();
1439                                 blockSize = comm.GetString (cLen);
1440
1441                                 if (tdsVersion == TdsVersion.tds70) 
1442                                         comm.Skip (len - 2 - cLen * 2);
1443                                 else 
1444                                         comm.Skip (len - 2 - cLen);
1445
1446                                 packetSize = Int32.Parse (blockSize);   
1447                                 comm.ResizeOutBuf (packetSize);
1448                                 break;
1449                         case TdsEnvPacketSubType.CharSet :
1450                                 cLen = comm.GetByte ();
1451                                 if (tdsVersion == TdsVersion.tds70) {
1452                                         SetCharset (comm.GetString (cLen));
1453                                         comm.Skip (len - 2 - cLen * 2);
1454                                 }
1455                                 else {
1456                                         SetCharset (comm.GetString (cLen));
1457                                         comm.Skip (len - 2 - cLen);
1458                                 }
1459
1460                                 break;
1461                         case TdsEnvPacketSubType.Locale :
1462                                 cLen = comm.GetByte ();
1463                                 int lcid = 0;
1464                                 if (tdsVersion == TdsVersion.tds70) {
1465                                         lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1466                                         comm.Skip (len - 2 - cLen * 2);
1467                                 }
1468                                 else {
1469                                         lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1470                                         comm.Skip (len - 2 - cLen);
1471                                 }
1472                                 locale = new CultureInfo (lcid);
1473                                 break;
1474                         case TdsEnvPacketSubType.Database :
1475                                 cLen = comm.GetByte ();
1476                                 string newDB = comm.GetString (cLen);
1477                                 cLen = comm.GetByte () & 0xff;
1478                                 comm.GetString (cLen);
1479                                 if (originalDatabase == string.Empty)
1480                                         originalDatabase = newDB;
1481                                 database = newDB;
1482                                 break;
1483                         default:
1484                                 comm.Skip (len - 1);
1485                                 break;
1486                         }
1487                 }
1488
1489                 protected void ProcessLoginAck ()
1490                 {
1491                         GetSubPacketLength ();
1492
1493                         if (tdsVersion == TdsVersion.tds70) {
1494                                 comm.Skip (5);
1495                                 int nameLength = comm.GetByte ();
1496                                 databaseProductName = comm.GetString (nameLength);
1497                                 databaseMajorVersion = comm.GetByte ();
1498                                 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1499                                                                 comm.GetByte ().ToString("00"), 
1500                                                                 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1501                         }
1502                         else {
1503                                 comm.Skip (5);
1504                                 short nameLength = comm.GetByte ();
1505                                 databaseProductName = comm.GetString (nameLength);
1506                                 comm.Skip (1);
1507                                 databaseMajorVersion = comm.GetByte ();
1508                                 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1509                                 comm.Skip (1);
1510                         }
1511
1512                         if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1513                                 int last = databaseProductName.IndexOf ('\0');
1514                                 databaseProductName = databaseProductName.Substring (0, last);
1515                         }
1516
1517                         connected = true;
1518                 }
1519
1520                 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1521                 {
1522                         if (TdsErrorMessage != null)
1523                                 TdsErrorMessage (this, e);
1524                 }
1525
1526                 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1527                 {
1528                         if (TdsInfoMessage != null)
1529                                 TdsInfoMessage (this, e);
1530                         messages.Clear ();
1531                 }
1532
1533                 protected void ProcessMessage (TdsPacketSubType subType)
1534                 {
1535                         GetSubPacketLength ();
1536
1537                         int number = comm.GetTdsInt ();
1538                         byte state = comm.GetByte ();
1539                         byte theClass = comm.GetByte ();
1540                         string message;
1541                         string server;
1542                         string procedure;
1543                         byte lineNumber;
1544                         string source;
1545                         bool isError = false;
1546
1547                         if (subType == TdsPacketSubType.EED) {
1548                                 isError = (theClass > 10);
1549                                 comm.Skip (comm.GetByte ()); // SQL State
1550                                 comm.Skip (1);               // Status
1551                                 comm.Skip (2);               // TranState
1552                         } else 
1553                                 isError = (subType == TdsPacketSubType.Error);
1554
1555                         message = comm.GetString (comm.GetTdsShort ());
1556                         server = comm.GetString (comm.GetByte ());
1557                         procedure = comm.GetString (comm.GetByte ());
1558                         lineNumber = comm.GetByte ();
1559                         comm.Skip (1);
1560                         source = String.Empty; // FIXME
1561
1562                         if (isError)
1563                                 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1564                         else
1565                                 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1566                 }
1567
1568                 protected void ProcessOutputParam ()
1569                 {
1570                         GetSubPacketLength ();
1571                         /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1572                         comm.Skip (5);
1573
1574                         TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1575                         object value = GetColumnValue (colType, true);
1576                         outputParameters.Add (value);
1577                 }
1578
1579                 protected void ProcessDynamic ()
1580                 {
1581                         Comm.Skip (2);
1582                         /*byte type =*/ Comm.GetByte ();
1583                         /*byte status =*/ Comm.GetByte ();
1584                         /*string id =*/ Comm.GetString (Comm.GetByte ());
1585                 }
1586
1587                 protected virtual TdsPacketSubType ProcessSubPacket ()
1588                 {
1589                         TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1590
1591                         switch (subType) {
1592                         case TdsPacketSubType.Dynamic2:
1593                                 comm.Skip (comm.GetTdsInt ());
1594                                 break;
1595                         case TdsPacketSubType.AltName:
1596                         case TdsPacketSubType.AltFormat:
1597                         case TdsPacketSubType.Capability:
1598                         case TdsPacketSubType.ParamFormat:
1599                                 comm.Skip (comm.GetTdsShort ());
1600                                 break;
1601                         case TdsPacketSubType.Dynamic:
1602                                 ProcessDynamic ();
1603                                 break;
1604                         case TdsPacketSubType.EnvironmentChange:
1605                                 ProcessEnvironmentChange ();
1606                                 break;
1607                         case TdsPacketSubType.Info:  // TDS 4.2/7.0
1608                         case TdsPacketSubType.EED:   // TDS 5.0
1609                         case TdsPacketSubType.Error: // TDS 4.2/7.0
1610                                 ProcessMessage (subType);
1611                                 break;
1612                         case TdsPacketSubType.Param:
1613                                 ProcessOutputParam ();
1614                                 break;
1615                         case TdsPacketSubType.LoginAck:
1616                                 ProcessLoginAck ();
1617                                 break;
1618                         case TdsPacketSubType.Authentication: // TDS 7.0
1619                                 ProcessAuthentication ();
1620                                 break;
1621                         case TdsPacketSubType.ReturnStatus :
1622                                 ProcessReturnStatus ();
1623                                 break;
1624                         case TdsPacketSubType.ProcId:
1625                                 Comm.Skip (8);
1626                                 break;
1627                         case TdsPacketSubType.Done:
1628                         case TdsPacketSubType.DoneProc:
1629                         case TdsPacketSubType.DoneInProc:
1630                                 ProcessEndToken (subType);
1631                                 break;
1632                         case TdsPacketSubType.ColumnName:
1633                                 Comm.Skip (8);
1634                                 ProcessColumnNames ();
1635                                 break;
1636                         case TdsPacketSubType.ColumnInfo:      // TDS 4.2
1637                         case TdsPacketSubType.ColumnMetadata:  // TDS 7.0
1638                         case TdsPacketSubType.RowFormat:       // TDS 5.0
1639                                 columns = ProcessColumnInfo ();
1640                                 break;
1641                         case TdsPacketSubType.ColumnDetail:
1642                                 ProcessColumnDetail ();
1643                                 break;
1644                         case TdsPacketSubType.TableName:
1645                                 ProcessTableName ();
1646                                 break;
1647                         case TdsPacketSubType.ColumnOrder:
1648                                 comm.Skip (comm.GetTdsShort ());
1649                                 break;
1650                         case TdsPacketSubType.Control:
1651                                 comm.Skip (comm.GetTdsShort ());
1652                                 break;
1653                         case TdsPacketSubType.Row:
1654                                 LoadRow ();
1655                                 break;
1656                         }
1657
1658                         return subType;
1659                 }
1660
1661                 protected void ProcessTableName ()
1662                 {
1663                         tableNames = new ArrayList ();
1664                         int totalLength = comm.GetTdsShort ();
1665                         int position = 0;
1666                         int len;
1667
1668                         while (position < totalLength) {
1669                                 if (tdsVersion == TdsVersion.tds70) {
1670                                         len = comm.GetTdsShort ();
1671                                         position += 2 * (len + 1);
1672                                 }
1673                                 else {
1674                                         len = comm.GetByte ();
1675                                         position += len + 1;
1676                                 }
1677                                 tableNames.Add (comm.GetString (len));
1678                         }
1679                 }
1680
1681                 protected void SetCharset (string charset)
1682                 {
1683                         if (charset == null || charset.Length > 30)
1684                                 charset = "iso_1";
1685
1686                         if (this.charset != null && this.charset == charset)
1687                                 return;
1688
1689                         if (charset.StartsWith ("cp")) {
1690                                 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1691                                 this.charset = charset;
1692                         }
1693                         else {
1694                                 encoder = Encoding.GetEncoding ("iso-8859-1");
1695                                 this.charset = "iso_1";
1696                         }
1697                         comm.Encoder = encoder;
1698                 }
1699
1700                 protected void SetLanguage (string language)
1701                 {
1702                         if (language == null || language.Length > 30)
1703                                 language = "us_english";
1704
1705                         this.language = language;
1706                 }
1707
1708                 protected virtual void ProcessReturnStatus () 
1709                 {
1710                         comm.Skip(4);
1711                 }
1712
1713                 #endregion // Private Methods
1714
1715 #if NET_2_0
1716                 #region asynchronous methods
1717                 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults, 
1718                                                           AsyncCallback callback, object state)
1719                 {
1720                         InitExec ();
1721
1722                         TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1723                         ar.TdsAsyncState.WantResults = wantResults;
1724
1725                         Comm.StartPacket (TdsPacketType.Query);
1726                         Comm.Append (sql);
1727                         try {
1728                                 Comm.SendPacket ();
1729                                 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback), 
1730                                                       ar);
1731                         } catch (IOException ex) {
1732                                 connected = false;
1733                                 throw new TdsInternalException ("Server closed the connection.", ex);
1734                         }
1735
1736                         return ar;
1737                 }
1738                 
1739                 protected void EndExecuteQueryInternal (IAsyncResult ar)
1740                 {
1741                         if (!ar.IsCompleted)
1742                                 ar.AsyncWaitHandle.WaitOne ();
1743                         TdsAsyncResult result = (TdsAsyncResult) ar;
1744                         if (result.IsCompletedWithException)
1745                                 throw result.Exception;
1746                 }
1747
1748                 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1749                 {
1750                         TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1751                         TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1752
1753                         try {
1754                                 Comm.EndReadPacket (ar);
1755                                 if (!tdsState.WantResults)
1756                                         SkipToEnd ();
1757                         } catch (Exception e) {
1758                                 result.MarkComplete (e);
1759                                 return;
1760                         }
1761                         result.MarkComplete ();
1762                 }
1763                 
1764
1765                 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1766                                                                   TdsMetaParameterCollection parameters,
1767                                                                   AsyncCallback callback,
1768                                                                   object state)
1769                 {
1770                         // abstract, kept to be backward compatiable.
1771                         throw new NotImplementedException ("should not be called!");
1772                 }
1773                 
1774                 public virtual void EndExecuteNonQuery (IAsyncResult ar)
1775                 {
1776                         // abstract method
1777                         throw new NotImplementedException ("should not be called!");
1778                 }
1779                 
1780                 public virtual IAsyncResult BeginExecuteQuery (string sql,
1781                                                                   TdsMetaParameterCollection parameters,
1782                                                                   AsyncCallback callback,
1783                                                                   object state)
1784                 {
1785                         // abstract, kept to be backward compatiable.
1786                         throw new NotImplementedException ("should not be called!");
1787                 }
1788                 
1789                 public virtual void EndExecuteQuery (IAsyncResult ar)
1790                 {
1791                         // abstract method
1792                         throw new NotImplementedException ("should not be called!");
1793                 }
1794
1795                 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
1796                                                                     string epilog,
1797                                                                     string cmdText,
1798                                                                     bool IsNonQuery,
1799                                                                     TdsMetaParameterCollection parameters,
1800                                                                     AsyncCallback callback,
1801                                                                     object state)
1802                 {
1803                         throw new NotImplementedException ("should not be called!");
1804                 }
1805                 
1806                 public virtual void EndExecuteProcedure (IAsyncResult ar)
1807                 {
1808                         // abstract method
1809                         throw new NotImplementedException ("should not be called!");
1810                 }
1811                 
1812                 public void WaitFor (IAsyncResult ar)
1813                 {
1814                         if (! ar.IsCompleted)
1815                                 ar.AsyncWaitHandle.WaitOne ();
1816                 }
1817
1818                 public void CheckAndThrowException (IAsyncResult ar)
1819                 {
1820                         TdsAsyncResult result = (TdsAsyncResult) ar;
1821                         if (result.IsCompleted && result.IsCompletedWithException)
1822                                 throw result.Exception;
1823                 }
1824
1825                 #endregion // asynchronous methods
1826 #endif // NET_2_0
1827
1828
1829         }
1830 }