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