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