Add SqlCredential support -
[mono-project.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds.cs
blobf06932c802620d1fed8f3fcb74fb9d5e0fb1ee26
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.
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.
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;
48 namespace Mono.Data.Tds.Protocol
50 public abstract class Tds
52 #region Fields
54 TdsComm comm;
55 TdsVersion tdsVersion;
57 protected internal TdsConnectionParameters connectionParms;
58 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
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;
69 readonly int lifeTime;
70 readonly DateTime created = DateTime.Now;
72 string charset;
73 string language;
75 bool connected;
76 bool moreResults;
78 Encoding encoder;
79 // bool autoCommit;
81 bool doneProc;
82 bool pooling = true;
83 TdsDataRow currentRow;
84 TdsDataColumnCollection columns;
86 ArrayList tableNames;
87 ArrayList columnNames;
89 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
91 bool queryInProgress;
92 int cancelsRequested;
93 int cancelsProcessed;
95 // bool isDone;
96 // bool isDoneInProc;
98 ArrayList outputParameters = new ArrayList ();
99 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
101 int recordsAffected = -1;
103 long StreamLength;
104 long StreamIndex;
105 int StreamColumnIndex;
107 bool sequentialAccess;
108 bool isRowRead;
109 bool isResultRead;
110 bool LoadInProgress;
111 byte [] collation;
113 internal int poolStatus = 0;
115 #endregion // Fields
117 #region Properties
119 protected string Charset {
120 get { return charset; }
123 protected CultureInfo Locale {
124 get { return locale; }
127 public bool DoneProc {
128 get { return doneProc; }
131 protected string Language {
132 get { return language; }
135 protected ArrayList ColumnNames {
136 get { return columnNames; }
139 public TdsDataRow ColumnValues {
140 get { return currentRow; }
143 internal TdsComm Comm {
144 get { return comm; }
147 public string Database {
148 get { return database; }
151 public string DataSource {
152 get { return dataSource; }
155 public virtual bool IsConnected {
156 get { return connected && comm != null && comm.IsConnected (); }
157 set { connected = value; }
160 public bool Pooling {
161 get { return pooling; }
162 set { pooling = value; }
165 public bool MoreResults {
166 get { return moreResults; }
167 set { moreResults = value; }
170 public int PacketSize {
171 get { return packetSize; }
174 public int RecordsAffected {
175 get { return recordsAffected; }
176 set { recordsAffected = value; }
179 public string ServerVersion {
180 get { return databaseProductVersion; }
183 public TdsDataColumnCollection Columns {
184 get { return columns; }
187 public TdsVersion TdsVersion {
188 get { return tdsVersion; }
191 public ArrayList OutputParameters {
192 get { return outputParameters; }
193 set { outputParameters = value; }
196 protected TdsMetaParameterCollection Parameters {
197 get { return parameters; }
198 set { parameters = value; }
201 public bool SequentialAccess {
202 get { return sequentialAccess; }
203 set { sequentialAccess = value; }
206 public byte[] Collation {
207 get {return collation; }
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
224 private void SkipRow ()
226 SkipToColumnIndex (Columns.Count);
228 StreamLength = 0;
229 StreamColumnIndex = 0;
230 StreamIndex = 0;
231 LoadInProgress = false;
234 private void SkipToColumnIndex (int colIndex)
236 if (LoadInProgress)
237 EndLoad ();
239 if (colIndex < StreamColumnIndex)
240 throw new Exception ("Cannot Skip to a colindex less than the curr index");
242 while (colIndex != StreamColumnIndex) {
243 #if NET_2_0
244 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
245 if (colType == null)
246 throw new Exception ("Column type unset.");
247 #else
248 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
249 #endif
250 if (!(colType == TdsColumnType.Image ||
251 colType == TdsColumnType.Text ||
252 colType == TdsColumnType.NText)) {
253 GetColumnValue (colType, false, StreamColumnIndex);
254 StreamColumnIndex ++;
256 else {
257 BeginLoad (colType);
258 Comm.Skip (StreamLength);
259 StreamLength = 0;
260 EndLoad ();
265 public object GetSequentialColumnValue (int colIndex)
267 if (colIndex < StreamColumnIndex)
268 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
270 if (LoadInProgress)
271 EndLoad ();
273 if (colIndex != StreamColumnIndex)
274 SkipToColumnIndex (colIndex);
276 #if NET_2_0
277 object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
278 #else
279 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
280 #endif
281 StreamColumnIndex++;
282 return o;
285 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
287 if (colIndex < StreamColumnIndex)
288 throw new InvalidOperationException ("Invalid attempt to read from column ordinal" + colIndex);
289 try {
290 if (colIndex != StreamColumnIndex)
291 SkipToColumnIndex (colIndex);
293 if (!LoadInProgress) {
294 #if NET_2_0
295 BeginLoad (Columns[colIndex].ColumnType);
296 #else
297 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
298 #endif
301 if (buffer == null)
302 return StreamLength;
303 return LoadData (fieldIndex, buffer, bufferIndex, size);
304 } catch (IOException ex) {
305 connected = false;
306 throw new TdsInternalException ("Server closed the connection.", ex);
310 private void BeginLoad (
311 #if NET_2_0
312 TdsColumnType? colType
313 #else
314 TdsColumnType colType
315 #endif
318 if (LoadInProgress)
319 EndLoad ();
321 StreamLength = 0;
323 #if NET_2_0
324 if (colType == null)
325 throw new ArgumentNullException ("colType");
326 #endif
328 switch (colType) {
329 case TdsColumnType.Text :
330 case TdsColumnType.NText:
331 case TdsColumnType.Image:
332 if (Comm.GetByte () != 0) {
333 Comm.Skip (24);
334 StreamLength = Comm.GetTdsInt ();
335 } else {
336 // use -2 to indicate that we're dealing
337 // with a NULL value
338 StreamLength = -2;
340 break;
341 case TdsColumnType.BigVarChar:
342 case TdsColumnType.BigChar:
343 case TdsColumnType.BigBinary:
344 case TdsColumnType.BigVarBinary:
345 Comm.GetTdsShort ();
346 StreamLength = Comm.GetTdsShort ();
347 break;
348 case TdsColumnType.VarChar :
349 case TdsColumnType.NVarChar :
350 case TdsColumnType.Char:
351 case TdsColumnType.NChar:
352 case TdsColumnType.Binary:
353 case TdsColumnType.VarBinary:
354 StreamLength = Comm.GetTdsShort ();
355 break;
356 default :
357 StreamLength = -1;
358 break;
361 StreamIndex = 0;
362 LoadInProgress = true;
365 private void EndLoad()
367 if (StreamLength > 0)
368 Comm.Skip (StreamLength);
369 StreamLength = 0;
370 StreamIndex = 0;
371 StreamColumnIndex++;
372 LoadInProgress = false;
375 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
377 if (StreamLength <= 0)
378 return StreamLength;
380 if (fieldIndex < StreamIndex)
381 throw new InvalidOperationException (string.Format (
382 "Attempting to read at dataIndex '{0}' is " +
383 "not allowed as this is less than the " +
384 "current position. You must read from " +
385 "dataIndex '{1}' or greater.",
386 fieldIndex, StreamIndex));
388 if (fieldIndex >= (StreamLength + StreamIndex))
389 return 0;
391 // determine number of bytes to skip
392 int skip = (int) (fieldIndex - StreamIndex);
393 // skip bytes
394 Comm.Skip (skip);
395 // update the current position
396 StreamIndex += (fieldIndex - StreamIndex);
397 // update the remaining length
398 StreamLength -= skip;
400 // Load the reqd amt of bytes
401 int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
402 byte[] arr = Comm.GetBytes (loadlen, true);
404 // update the index and stream length
405 StreamIndex += loadlen + (fieldIndex - StreamIndex);
406 StreamLength -= loadlen;
407 arr.CopyTo (buffer, bufferIndex);
409 return arr.Length;
412 #endregion // Properties
414 #region Events
416 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
417 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
419 #endregion // Events
421 #region Constructors
423 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
424 : this (dataSource, port, packetSize, timeout, 0, tdsVersion)
428 public Tds (string dataSource, int port, int packetSize, int timeout, int lifeTime, TdsVersion tdsVersion)
430 this.tdsVersion = tdsVersion;
431 this.packetSize = packetSize;
432 this.dataSource = dataSource;
433 this.columns = new TdsDataColumnCollection ();
434 this.lifeTime = lifeTime;
436 InitComm (port, timeout);
439 protected virtual void InitComm (int port, int timeout)
441 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
444 #endregion // Constructors
446 #region Public Methods
448 internal bool Expired {
449 get {
450 if (lifeTime == 0)
451 return false;
452 return DateTime.Now > (created + TimeSpan.FromSeconds (lifeTime));
456 internal protected void InitExec ()
458 // clean up
459 moreResults = true;
460 doneProc = false;
462 // Reset "read" status variables - used in case of SequentialAccess
463 isResultRead = false;
464 isRowRead = false;
465 StreamLength = 0;
466 StreamIndex = 0;
467 StreamColumnIndex = 0;
468 LoadInProgress = false;
470 // Reset more variables
471 queryInProgress = false;
472 cancelsRequested = 0;
473 cancelsProcessed = 0;
474 recordsAffected = -1;
476 messages.Clear ();
477 outputParameters.Clear ();
480 public void Cancel ()
482 if (queryInProgress) {
483 if (cancelsRequested == cancelsProcessed) {
484 comm.StartPacket (TdsPacketType.Cancel);
485 try {
486 Comm.SendPacket ();
487 } catch (IOException ex) {
488 connected = false;
489 throw new TdsInternalException ("Server closed the connection.", ex);
491 cancelsRequested += 1;
496 public abstract bool Connect (TdsConnectionParameters connectionParameters);
498 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
500 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
501 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
504 public virtual void Disconnect ()
506 try {
507 comm.StartPacket (TdsPacketType.Logoff);
508 comm.Append ((byte) 0);
509 comm.SendPacket ();
510 } catch {
511 // We're closing the socket anyway
513 connected = false;
514 comm.Close ();
517 public virtual bool Reset ()
519 database = originalDatabase;
520 return true;
523 protected virtual bool IsValidRowCount (byte status, byte op)
525 return ((status & (0x10)) != 0) ;
528 public void Execute (string sql)
530 Execute (sql, null, 0, false);
533 public void ExecProc (string sql)
535 ExecProc (sql, null, 0, false);
538 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
540 ExecuteQuery (sql, timeout, wantResults);
543 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
545 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
548 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
550 throw new NotSupportedException ();
553 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
555 moreResults = true;
556 try {
557 Comm.SendPacket ();
558 CheckForData (timeout);
559 if (!wantResults)
560 SkipToEnd ();
561 } catch (IOException ex) {
562 connected = false;
563 throw new TdsInternalException ("Server closed the connection.", ex);
567 internal void ExecBulkCopy (int timeout, bool wantResults)
569 moreResults = true;
570 try {
571 Comm.SendPacket ();
572 CheckForData (timeout);
573 if (!wantResults)
574 SkipToEnd ();
575 } catch (IOException ex) {
576 connected = false;
577 throw new TdsInternalException ("Server closed the connection.", ex);
581 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
583 InitExec ();
585 Comm.StartPacket (TdsPacketType.Query);
586 Comm.Append (sql);
587 try {
588 Comm.SendPacket ();
589 CheckForData (timeout);
590 if (!wantResults)
591 SkipToEnd ();
592 } catch (IOException ex) {
593 connected = false;
594 throw new TdsInternalException ("Server closed the connection.", ex);
598 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
599 int timeout, bool wantResults)
601 Comm.StartPacket (TdsPacketType.DBRPC);
603 byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
604 byte rpcNameLength = (byte) rpcNameBytes.Length;
605 ushort mask = 0x0000;
606 ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
607 sizeof (ushort));
609 Comm.Append (packetLength);
610 Comm.Append (rpcNameLength);
611 Comm.Append (rpcNameBytes);
612 Comm.Append (mask);
614 try {
615 Comm.SendPacket ();
616 CheckForData (timeout);
617 if (!wantResults)
618 SkipToEnd ();
619 } catch (IOException ex) {
620 connected = false;
621 throw new TdsInternalException ("Server closed the connection.", ex);
625 public bool NextResult ()
627 if (SequentialAccess) {
628 if (isResultRead) {
629 while (NextRow ()) {}
630 isRowRead = false;
631 isResultRead = false;
634 if (!moreResults)
635 return false;
637 TdsPacketSubType subType;
639 bool done = false;
640 bool outputParams = false;
642 while (!done) {
643 subType = ProcessSubPacket ();
644 if (outputParams) {
645 moreResults = false;
646 break;
648 switch (subType) {
649 case TdsPacketSubType.ColumnInfo:
650 case TdsPacketSubType.ColumnMetadata:
651 case TdsPacketSubType.RowFormat:
652 byte peek = Comm.Peek ();
653 done = (peek != (byte) TdsPacketSubType.TableName);
654 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
655 outputParams = true;
656 done = false;
658 break;
659 case TdsPacketSubType.TableName:
660 // done = true;
661 peek = Comm.Peek ();
662 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
663 break;
664 case TdsPacketSubType.ColumnDetail:
665 done = true;
666 break;
667 default:
668 done = !moreResults;
669 break;
673 return moreResults;
676 public bool NextRow ()
678 if (SequentialAccess) {
679 if (isRowRead) {
680 SkipRow ();
681 isRowRead = false;
685 TdsPacketSubType subType;
686 bool done = false;
687 bool result = false;
689 do {
690 subType = ProcessSubPacket ();
691 switch (subType) {
692 case TdsPacketSubType.Row:
693 result = true;
694 done = true;
695 break;
696 case TdsPacketSubType.Done:
697 case TdsPacketSubType.DoneProc:
698 case TdsPacketSubType.DoneInProc:
699 result = false;
700 done = true;
701 break;
703 } while (!done);
705 return result;
708 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
710 throw new NotSupportedException ();
713 public void SkipToEnd ()
715 try {
716 while (NextResult ()) { /* DO NOTHING */ }
717 } catch (IOException ex) {
718 connected = false;
719 throw new TdsInternalException ("Server closed the connection.", ex);
723 public virtual void Unprepare (string statementId)
725 throw new NotSupportedException ();
728 #endregion // Public Methods
730 #region // Private Methods
732 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
733 protected void CheckForData (int timeout)
735 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
736 Cancel ();
737 throw CreateTimeoutException (dataSource, "CheckForData()");
741 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
743 return new TdsInternalInfoMessageEventArgs (errors);
746 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
748 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
751 private Encoding GetEncodingFromColumnCollation (int lcid, int sortId)
753 if (sortId != 0)
754 return TdsCharset.GetEncodingFromSortOrder (sortId);
755 else
756 return TdsCharset.GetEncodingFromLCID (lcid);
759 protected object GetColumnValue (
760 #if NET_2_0
761 TdsColumnType? colType,
762 #else
763 TdsColumnType colType,
764 #endif
765 bool outParam)
767 return GetColumnValue (colType, outParam, -1);
770 private object GetColumnValue (
771 #if NET_2_0
772 TdsColumnType? colType,
773 #else
774 TdsColumnType colType,
775 #endif
776 bool outParam, int ordinal)
778 int len;
779 object element = null;
780 Encoding enc = null;
781 int lcid = 0, sortId = 0;
783 #if NET_2_0
784 if (colType == null)
785 throw new ArgumentNullException ("colType");
786 #endif
787 if (ordinal > -1 && tdsVersion > TdsVersion.tds70) {
788 #if NET_2_0
789 lcid = (int) columns[ordinal].LCID;
790 sortId = (int) columns[ordinal].SortOrder;
791 #else
792 lcid = (int) columns[ordinal]["LCID"];
793 sortId = (int) columns[ordinal]["SortOrder"];
794 #endif
797 switch (colType) {
798 case TdsColumnType.IntN :
799 if (outParam)
800 comm.Skip (1);
801 element = GetIntValue (colType);
802 break;
803 case TdsColumnType.Int1 :
804 case TdsColumnType.Int2 :
805 case TdsColumnType.Int4 :
806 case TdsColumnType.BigInt :
807 element = GetIntValue (colType);
808 break;
809 case TdsColumnType.Image :
810 if (outParam)
811 comm.Skip (1);
812 element = GetImageValue ();
813 break;
814 case TdsColumnType.Text :
815 enc = GetEncodingFromColumnCollation (lcid, sortId);
816 if (outParam)
817 comm.Skip (1);
818 element = GetTextValue (false, enc);
819 break;
820 case TdsColumnType.NText :
821 enc = GetEncodingFromColumnCollation (lcid, sortId);
822 if (outParam)
823 comm.Skip (1);
824 element = GetTextValue (true, enc);
825 break;
826 case TdsColumnType.Char :
827 case TdsColumnType.VarChar :
828 enc = GetEncodingFromColumnCollation (lcid, sortId);
829 if (outParam)
830 comm.Skip (1);
831 element = GetStringValue (colType, false, outParam, enc);
832 break;
833 case TdsColumnType.BigVarBinary :
834 if (outParam)
835 comm.Skip (1);
836 len = comm.GetTdsShort ();
837 element = comm.GetBytes (len, true);
838 break;
840 case TdsColumnType.BigBinary :
841 if (outParam)
842 comm.Skip (2);
843 len = comm.GetTdsShort ();
844 element = comm.GetBytes (len, true);
845 break;
847 case TdsColumnType.BigBinary :
848 if (outParam)
849 comm.Skip (2);
850 element = GetBinaryValue ();
851 break;
852 case TdsColumnType.BigChar :
853 case TdsColumnType.BigVarChar :
854 enc = GetEncodingFromColumnCollation (lcid, sortId);
855 if (outParam)
856 comm.Skip (2);
857 element = GetStringValue (colType, false, outParam, enc);
858 break;
859 case TdsColumnType.NChar :
860 case TdsColumnType.BigNVarChar :
861 enc = GetEncodingFromColumnCollation (lcid, sortId);
862 if (outParam)
863 comm.Skip(2);
864 element = GetStringValue (colType, true, outParam, enc);
865 break;
866 case TdsColumnType.NVarChar :
867 enc = GetEncodingFromColumnCollation (lcid, sortId);
868 if (outParam)
869 comm.Skip (1);
870 element = GetStringValue (colType, true, outParam, enc);
871 break;
872 case TdsColumnType.Real :
873 case TdsColumnType.Float8 :
874 element = GetFloatValue (colType);
875 break;
876 case TdsColumnType.FloatN :
877 if (outParam)
878 comm.Skip (1);
879 element = GetFloatValue (colType);
880 break;
881 case TdsColumnType.SmallMoney :
882 case TdsColumnType.Money :
883 element = GetMoneyValue (colType);
884 break;
885 case TdsColumnType.MoneyN :
886 if (outParam)
887 comm.Skip (1);
888 element = GetMoneyValue (colType);
889 break;
890 case TdsColumnType.Numeric :
891 case TdsColumnType.Decimal :
892 byte precision;
893 byte scale;
894 if (outParam) {
895 comm.Skip (1);
896 precision = comm.GetByte ();
897 scale = comm.GetByte ();
899 else {
900 #if NET_2_0
901 precision = (byte) columns[ordinal].NumericPrecision;
902 scale = (byte) columns[ordinal].NumericScale;
903 #else
904 precision = (byte) columns[ordinal]["NumericPrecision"];
905 scale = (byte) columns[ordinal]["NumericScale"];
906 #endif
909 element = GetDecimalValue (precision, scale);
911 // workaround for fact that TDS 7.0 returns
912 // bigint as decimal (19,0), and client code
913 // expects it to be returned as a long
914 if (scale == 0 && precision <= 19 && tdsVersion == TdsVersion.tds70) {
915 if (!(element is System.DBNull))
916 element = Convert.ToInt64 (element);
918 break;
919 case TdsColumnType.DateTimeN :
920 if (outParam)
921 comm.Skip (1);
922 element = GetDateTimeValue (colType);
923 break;
924 case TdsColumnType.DateTime4 :
925 case TdsColumnType.DateTime :
926 element = GetDateTimeValue (colType);
927 break;
928 case TdsColumnType.VarBinary :
929 case TdsColumnType.Binary :
930 if (outParam)
931 comm.Skip (1);
932 element = GetBinaryValue ();
933 break;
934 case TdsColumnType.BitN :
935 if (outParam)
936 comm.Skip (1);
937 if (comm.GetByte () == 0)
938 element = DBNull.Value;
939 else
940 element = (comm.GetByte() != 0);
941 break;
942 case TdsColumnType.Bit :
943 int columnSize = comm.GetByte ();
944 element = (columnSize != 0);
945 break;
946 case TdsColumnType.UniqueIdentifier :
947 if (comm.Peek () != 16) { // If it's null, then what to do?
948 /*byte swallowed =*/ comm.GetByte();
949 element = DBNull.Value;
950 break;
952 if (outParam)
953 comm.Skip (1);
955 len = comm.GetByte () & 0xff;
956 if (len > 0) {
957 byte[] guidBytes = comm.GetBytes (len, true);
958 if (!BitConverter.IsLittleEndian) {
959 byte[] swappedguidBytes = new byte[len];
960 for (int i = 0; i < 4; i++)
961 swappedguidBytes[i] = guidBytes[4-i-1];
962 for (int i = 4; i < 6; i++)
963 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
964 for (int i = 6; i < 8; i++)
965 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
966 for (int i = 8; i < 16; i++)
967 swappedguidBytes[i] = guidBytes[i];
968 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
970 element = new Guid (guidBytes);
972 break;
973 default :
974 return DBNull.Value;
976 return element;
979 private object GetBinaryValue ()
981 int len;
982 object result = DBNull.Value;
984 if (tdsVersion >= TdsVersion.tds70) {
985 len = comm.GetTdsShort ();
986 if (len != 0xffff && len >= 0)
987 result = comm.GetBytes (len, true);
988 } else {
989 len = (comm.GetByte () & 0xff);
990 if (len != 0)
991 result = comm.GetBytes (len, true);
994 return result;
997 private object GetDateTimeValue (
998 #if NET_2_0
999 TdsColumnType? type
1000 #else
1001 TdsColumnType type
1002 #endif
1005 int len = 0;
1006 object result;
1008 #if NET_2_0
1009 if (type == null)
1010 throw new ArgumentNullException ("type");
1011 #endif
1012 switch (type) {
1013 case TdsColumnType.DateTime4:
1014 len = 4;
1015 break;
1016 case TdsColumnType.DateTime:
1017 len = 8;
1018 break;
1019 case TdsColumnType.DateTimeN:
1020 byte tmp = comm.Peek ();
1021 if (tmp != 0 && tmp != 4 && tmp != 8)
1022 break;
1023 len = comm.GetByte ();
1024 break;
1027 DateTime epoch = new DateTime (1900, 1, 1);
1029 switch (len) {
1030 case 8 :
1031 result = epoch.AddDays (comm.GetTdsInt ());
1032 int seconds = comm.GetTdsInt ();
1033 long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f);
1034 if (seconds != 0 || millis != 0) {
1035 result = ((DateTime) result).AddSeconds (seconds / 300);
1036 result = ((DateTime) result).AddMilliseconds (millis);
1038 break;
1039 case 4 :
1040 // MSDN says small datetime is stored in 2 bytes as no of days
1041 // *after* 1/1/1900. so, cast to unsigned short
1042 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
1043 short minutes = comm.GetTdsShort ();
1044 if (minutes != 0)
1045 result = ((DateTime) result).AddMinutes ((int) minutes);
1046 break;
1047 default:
1048 result = DBNull.Value;
1049 break;
1052 return result;
1055 private object GetDecimalValue (byte precision, byte scale)
1057 if (tdsVersion < TdsVersion.tds70)
1058 return GetDecimalValueTds50 (precision, scale);
1059 else
1060 return GetDecimalValueTds70 (precision, scale);
1063 private object GetDecimalValueTds70 (byte precision, byte scale)
1065 int[] bits = new int[4] {0,0,0,0};
1067 int len = (comm.GetByte() & 0xff) - 1;
1068 if (len < 0)
1069 return DBNull.Value;
1071 bool positive = (comm.GetByte () == 1);
1072 if (len > 16)
1073 throw new OverflowException ();
1075 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
1076 bits[index] = comm.GetTdsInt ();
1078 if (bits [3] != 0)
1079 return new TdsBigDecimal (precision, scale, !positive, bits);
1080 else
1081 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
1084 private object GetDecimalValueTds50 (byte precision, byte scale)
1086 int[] bits = new int[4] {0,0,0,0};
1088 int len = (comm.GetByte() & 0xff);
1089 if (len == 0)
1090 return DBNull.Value;
1092 byte[] dec_bytes=comm.GetBytes(len,false);
1094 byte[] easy=new byte[4];
1096 bool positive = dec_bytes[0]==1;
1098 if (len > 17)
1099 throw new OverflowException ();
1101 for (int i = 1, index = 0; i < len && i < 16; i +=
1102 4, index += 1) {
1103 for(int j=0; j<4; j++)
1104 if(i+j<len)
1105 easy[j]=dec_bytes[len-
1106 (i+j)];
1107 else
1108 easy[j]=0;
1109 if(!BitConverter.IsLittleEndian)
1110 easy=comm.Swap(easy);
1111 bits[index] = BitConverter.ToInt32(easy,0);
1113 if (bits [3] != 0)
1114 return new TdsBigDecimal (precision,
1115 scale, positive, bits);
1116 else
1117 return new Decimal(bits[0], bits[1], bits
1118 [2], positive, scale);
1122 private object GetFloatValue (
1123 #if NET_2_0
1124 TdsColumnType? columnType
1125 #else
1126 TdsColumnType columnType
1127 #endif
1130 #if NET_2_0
1131 if (columnType == null)
1132 throw new ArgumentNullException ("columnType");
1133 #endif
1134 int columnSize = 0;
1136 switch (columnType) {
1137 case TdsColumnType.Real:
1138 columnSize = 4;
1139 break;
1140 case TdsColumnType.Float8:
1141 columnSize = 8;
1142 break;
1143 case TdsColumnType.FloatN:
1144 columnSize = comm.GetByte ();
1145 break;
1148 switch (columnSize) {
1149 case 8 :
1150 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1151 case 4 :
1152 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1153 default :
1154 return DBNull.Value;
1158 private object GetImageValue ()
1160 byte hasValue = comm.GetByte ();
1162 if (hasValue == 0)
1163 return DBNull.Value;
1165 comm.Skip (24);
1166 int len = comm.GetTdsInt ();
1168 if (len < 0)
1169 return DBNull.Value;
1171 return (comm.GetBytes (len, true));
1174 private object GetIntValue (
1175 #if NET_2_0
1176 TdsColumnType? type
1177 #else
1178 TdsColumnType type
1179 #endif
1182 int len;
1184 #if NET_2_0
1185 if (type == null)
1186 throw new ArgumentNullException ("type");
1187 #endif
1188 switch (type) {
1189 case TdsColumnType.BigInt :
1190 len = 8;
1191 break;
1192 case TdsColumnType.IntN :
1193 len = comm.GetByte ();
1194 break;
1195 case TdsColumnType.Int4 :
1196 len = 4;
1197 break;
1198 case TdsColumnType.Int2 :
1199 len = 2;
1200 break;
1201 case TdsColumnType.Int1 :
1202 len = 1;
1203 break;
1204 default:
1205 return DBNull.Value;
1208 switch (len) {
1209 case 8:
1210 return (comm.GetTdsInt64 ());
1211 case 4 :
1212 return (comm.GetTdsInt ());
1213 case 2 :
1214 return (comm.GetTdsShort ());
1215 case 1 :
1216 return (comm.GetByte ());
1217 default:
1218 return DBNull.Value;
1222 private object GetMoneyValue (
1223 #if NET_2_0
1224 TdsColumnType? type
1225 #else
1226 TdsColumnType type
1227 #endif
1230 int len;
1232 #if NET_2_0
1233 if (type == null)
1234 throw new ArgumentNullException ("type");
1235 #endif
1236 switch (type) {
1237 case TdsColumnType.SmallMoney :
1238 case TdsColumnType.Money4 :
1239 len = 4;
1240 break;
1241 case TdsColumnType.Money :
1242 len = 8;
1243 break;
1244 case TdsColumnType.MoneyN :
1245 len = comm.GetByte ();
1246 break;
1247 default:
1248 return DBNull.Value;
1251 switch (len) {
1252 case 4: {
1253 int val = Comm.GetTdsInt ();
1254 bool negative = val < 0;
1255 if (negative)
1256 val = ~(val - 1);
1257 return new Decimal (val, 0, 0, negative, 4);
1259 case 8: {
1260 int hi = Comm.GetTdsInt ();
1261 int lo = Comm.GetTdsInt ();
1262 bool negative = hi < 0;
1264 if (negative) {
1265 hi = ~hi;
1266 lo = ~(lo - 1);
1268 return new Decimal (lo, hi, 0, negative, 4);
1270 default:
1271 return DBNull.Value;
1275 protected object GetStringValue (
1276 #if NET_2_0
1277 TdsColumnType? colType,
1278 #else
1279 TdsColumnType colType,
1280 #endif
1281 bool wideChars, bool outputParam, Encoding encoder)
1283 bool shortLen = false;
1284 Encoding enc = encoder;
1286 if (tdsVersion > TdsVersion.tds70 && outputParam &&
1287 (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
1288 colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
1289 colType == TdsColumnType.NVarChar)) {
1290 // Read collation for SqlServer 2000 and beyond
1291 byte[] collation;
1292 collation = Comm.GetBytes (5, true);
1293 enc = TdsCharset.GetEncoding (collation);
1294 shortLen = true;
1295 } else {
1296 shortLen = (tdsVersion >= TdsVersion.tds70) && (wideChars || !outputParam);
1299 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1300 return GetStringValue (wideChars, len, enc);
1303 protected object GetStringValue (bool wideChars, int len, Encoding enc)
1305 if (tdsVersion < TdsVersion.tds70 && len == 0)
1306 return DBNull.Value;
1308 else if (len >= 0) {
1309 object result;
1310 if (wideChars)
1311 result = comm.GetString (len / 2, enc);
1312 else
1313 result = comm.GetString (len, false, enc);
1314 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1315 result = string.Empty;
1316 return result;
1318 else
1319 return DBNull.Value;
1322 protected int GetSubPacketLength ()
1324 return comm.GetTdsShort ();
1327 private object GetTextValue (bool wideChars, Encoding encoder)
1329 string result = null;
1330 byte hasValue = comm.GetByte ();
1332 if (hasValue != 16)
1333 return DBNull.Value;
1335 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1336 comm.Skip (24);
1338 int len = comm.GetTdsInt ();
1340 //if the len is 0 , then the string can be a '' string
1341 // this method is called only for Text and NText. Hence will
1342 // return a empty string
1343 if (len == 0)
1344 return string.Empty;
1346 if (wideChars)
1347 result = comm.GetString (len / 2, encoder);
1348 else
1349 result = comm.GetString (len, false, encoder);
1350 len /= 2;
1352 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1353 result = string.Empty;
1355 return result;
1358 internal bool IsBlobType (TdsColumnType columnType)
1360 return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1363 internal bool IsLargeType (TdsColumnType columnType)
1365 return ((byte) columnType > 128);
1368 protected bool IsWideType (TdsColumnType columnType)
1370 switch (columnType) {
1371 case TdsColumnType.NChar:
1372 case TdsColumnType.NText:
1373 case TdsColumnType.NVarChar:
1374 return true;
1375 default:
1376 return false;
1380 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1382 switch (columnType) {
1383 case TdsColumnType.Int1 :
1384 case TdsColumnType.Int2 :
1385 case TdsColumnType.Int4 :
1386 case TdsColumnType.BigInt :
1387 case TdsColumnType.Float8 :
1388 case TdsColumnType.DateTime :
1389 case TdsColumnType.Bit :
1390 case TdsColumnType.Money :
1391 case TdsColumnType.Money4 :
1392 case TdsColumnType.SmallMoney :
1393 case TdsColumnType.Real :
1394 case TdsColumnType.DateTime4 :
1396 case TdsColumnType.Decimal:
1397 case TdsColumnType.Numeric:
1399 return true;
1400 default :
1401 return false;
1405 protected void LoadRow ()
1407 if (SequentialAccess) {
1408 if (isRowRead)
1409 SkipRow ();
1410 isRowRead = true;
1411 isResultRead = true;
1412 return;
1415 currentRow = new TdsDataRow ();
1417 int i = 0;
1418 foreach (TdsDataColumn column in columns) {
1419 #if NET_2_0
1420 object o = GetColumnValue (column.ColumnType, false, i);
1421 #else
1422 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1423 #endif
1424 currentRow.Add (o);
1425 if (doneProc)
1426 outputParameters.Add (o);
1428 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1429 currentRow.BigDecimalIndex = i;
1430 i += 1;
1434 internal static int LookupBufferSize (TdsColumnType columnType)
1436 switch (columnType) {
1437 case TdsColumnType.Int1 :
1438 case TdsColumnType.Bit :
1439 return 1;
1440 case TdsColumnType.Int2 :
1441 return 2;
1442 case TdsColumnType.Int4 :
1443 case TdsColumnType.Real :
1444 case TdsColumnType.DateTime4 :
1445 case TdsColumnType.Money4 :
1446 case TdsColumnType.SmallMoney :
1447 return 4;
1448 case TdsColumnType.Float8 :
1449 case TdsColumnType.DateTime :
1450 case TdsColumnType.Money :
1451 case TdsColumnType.BigInt :
1452 return 8;
1453 default :
1454 return 0;
1458 protected internal int ProcessAuthentication ()
1460 int pdu_size = Comm.GetTdsShort ();
1461 byte[] msg2 = Comm.GetBytes (pdu_size, true);
1463 Type2Message t2 = new Type2Message (msg2);
1464 // 0x0001 Negotiate Unicode
1465 // 0x0200 Negotiate NTLM
1466 // 0x8000 Negotiate Always Sign
1468 Type3Message t3 = new Type3Message (t2);
1470 t3.Domain = this.connectionParms.DefaultDomain;
1471 t3.Host = this.connectionParms.Hostname;
1472 t3.Username = this.connectionParms.User;
1473 t3.Password = GetPlainPassword(this.connectionParms.Password);
1475 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1476 Comm.Append (t3.GetBytes ());
1477 try {
1478 Comm.SendPacket ();
1479 } catch (IOException ex) {
1480 connected = false;
1481 throw new TdsInternalException ("Server closed the connection.", ex);
1483 return 1; // TDS_SUCCEED
1486 protected void ProcessColumnDetail ()
1488 int len = GetSubPacketLength ();
1489 byte[] values = new byte[3];
1490 int columnNameLength;
1491 string baseColumnName = String.Empty;
1492 int position = 0;
1494 while (position < len) {
1495 for (int j = 0; j < 3; j += 1)
1496 values[j] = comm.GetByte ();
1497 position += 3;
1499 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1500 if (isAlias) {
1501 if (tdsVersion >= TdsVersion.tds70) {
1502 columnNameLength = comm.GetByte ();
1503 position += 2 * columnNameLength + 1;
1505 else {
1506 columnNameLength = comm.GetByte ();
1507 position += columnNameLength + 1;
1509 baseColumnName = comm.GetString (columnNameLength);
1512 byte index = (byte) (values[0] - (byte) 1);
1513 byte tableIndex = (byte) (values[1] - (byte) 1);
1514 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1516 TdsDataColumn column = columns [index];
1517 #if NET_2_0
1518 column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1519 column.IsExpression = isExpression;
1520 column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1521 column.IsAliased = isAlias;
1522 column.BaseColumnName = ((isAlias) ? baseColumnName : null);
1523 column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null);
1524 #else
1525 column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0);
1526 column ["IsExpression"] = isExpression;
1527 column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0);
1528 column ["IsAliased"] = isAlias;
1529 column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1530 column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1531 #endif
1535 protected abstract void ProcessColumnInfo ();
1537 protected void ProcessColumnNames ()
1539 columnNames = new ArrayList ();
1541 int totalLength = comm.GetTdsShort ();
1542 int bytesRead = 0;
1543 int i = 0;
1545 while (bytesRead < totalLength) {
1546 int columnNameLength = comm.GetByte ();
1547 string columnName = comm.GetString (columnNameLength);
1548 bytesRead = bytesRead + 1 + columnNameLength;
1549 columnNames.Add (columnName);
1550 i += 1;
1554 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1555 protected void ProcessEndToken (TdsPacketSubType type)
1557 byte status = Comm.GetByte ();
1558 Comm.Skip (1);
1559 byte op = comm.GetByte ();
1560 Comm.Skip (1);
1562 int rowCount = comm.GetTdsInt ();
1563 bool validRowCount = IsValidRowCount (status,op);
1564 moreResults = ((status & 0x01) != 0);
1565 bool cancelled = ((status & 0x20) != 0);
1567 switch (type) {
1568 case TdsPacketSubType.DoneProc:
1569 doneProc = true;
1570 goto case TdsPacketSubType.Done;
1571 case TdsPacketSubType.Done:
1572 case TdsPacketSubType.DoneInProc:
1573 if (validRowCount) {
1574 if (recordsAffected == -1)
1575 recordsAffected = rowCount;
1576 else
1577 recordsAffected += rowCount;
1579 break;
1582 if (moreResults)
1583 queryInProgress = false;
1584 if (cancelled)
1585 cancelsProcessed += 1;
1586 if (messages.Count > 0 && !moreResults)
1587 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1590 protected void ProcessEnvironmentChange ()
1592 // VARADHAN: TDS 8 Debugging
1593 //Console.WriteLine ("In ProcessEnvironmentChange... entry");
1594 int len = GetSubPacketLength ();
1595 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1596 int cLen;
1598 switch (type) {
1599 case TdsEnvPacketSubType.BlockSize :
1600 string blockSize;
1601 cLen = comm.GetByte ();
1602 blockSize = comm.GetString (cLen);
1604 if (tdsVersion >= TdsVersion.tds70)
1605 comm.Skip (len - 2 - cLen * 2);
1606 else
1607 comm.Skip (len - 2 - cLen);
1609 packetSize = Int32.Parse (blockSize);
1610 comm.ResizeOutBuf (packetSize);
1611 break;
1612 case TdsEnvPacketSubType.CharSet :
1613 cLen = comm.GetByte ();
1614 if (tdsVersion == TdsVersion.tds70) {
1615 SetCharset (comm.GetString (cLen));
1616 comm.Skip (len - 2 - cLen * 2);
1618 else {
1619 SetCharset (comm.GetString (cLen));
1620 comm.Skip (len - 2 - cLen);
1623 break;
1624 case TdsEnvPacketSubType.Locale :
1625 cLen = comm.GetByte ();
1626 int lcid = 0;
1627 if (tdsVersion >= TdsVersion.tds70) {
1628 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1629 comm.Skip (len - 2 - cLen * 2);
1631 else {
1632 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1633 comm.Skip (len - 2 - cLen);
1635 locale = new CultureInfo (lcid);
1636 break;
1637 case TdsEnvPacketSubType.Database :
1638 cLen = comm.GetByte ();
1639 string newDB = comm.GetString (cLen);
1640 cLen = comm.GetByte () & 0xff;
1641 comm.GetString (cLen);
1642 if (originalDatabase == string.Empty)
1643 originalDatabase = newDB;
1644 database = newDB;
1645 break;
1646 case TdsEnvPacketSubType.CollationInfo:
1647 //Console.WriteLine ("ProcessEnvironmentChange::Got collation info");
1648 cLen = comm.GetByte ();
1649 collation = comm.GetBytes (cLen, true);
1650 lcid = TdsCollation.LCID (collation);
1651 locale = new CultureInfo (lcid);
1652 SetCharset (TdsCharset.GetEncoding (collation));
1653 break;
1655 default:
1656 comm.Skip (len - 1);
1657 break;
1659 // VARADHAN: TDS 8 Debugging
1660 //Console.WriteLine ("In ProcessEnvironmentChange... exit");
1663 protected void ProcessLoginAck ()
1665 uint srvVersion = 0;
1666 GetSubPacketLength ();
1668 //Console.WriteLine ("ProcessLoginAck: B4 tdsVersion:{0}", tdsVersion);
1669 // Valid only for a Login7 request
1670 if (tdsVersion >= TdsVersion.tds70) {
1671 comm.Skip (1);
1672 srvVersion = (uint)comm.GetTdsInt ();
1674 //Console.WriteLine ("srvVersion: {0}", srvVersion);
1675 switch (srvVersion) {
1676 case 0x00000007:
1677 tdsVersion = TdsVersion.tds70;
1678 break;
1679 case 0x00000107:
1680 tdsVersion = TdsVersion.tds80;
1681 break;
1682 case 0x01000071:
1683 tdsVersion = TdsVersion.tds81;
1684 break;
1685 case 0x02000972:
1686 tdsVersion = TdsVersion.tds90;
1687 break;
1689 //Console.WriteLine ("ProcessLoginAck: after tdsVersion:{0}", tdsVersion);
1692 if (tdsVersion >= TdsVersion.tds70) {
1693 int nameLength = comm.GetByte ();
1694 databaseProductName = comm.GetString (nameLength);
1695 databaseMajorVersion = comm.GetByte ();
1696 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1697 comm.GetByte ().ToString("00"),
1698 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1699 } else {
1700 comm.Skip (5);
1701 short nameLength = comm.GetByte ();
1702 databaseProductName = comm.GetString (nameLength);
1703 comm.Skip (1);
1704 databaseMajorVersion = comm.GetByte ();
1705 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1706 comm.Skip (1);
1709 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1710 int last = databaseProductName.IndexOf ('\0');
1711 databaseProductName = databaseProductName.Substring (0, last);
1714 connected = true;
1715 //Console.WriteLine ("databaseProductVersion:{0}", databaseProductVersion);
1718 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1720 if (TdsErrorMessage != null)
1721 TdsErrorMessage (this, e);
1724 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1726 if (TdsInfoMessage != null)
1727 TdsInfoMessage (this, e);
1728 messages.Clear ();
1731 protected void ProcessMessage (TdsPacketSubType subType)
1733 GetSubPacketLength ();
1735 int number = comm.GetTdsInt ();
1736 byte state = comm.GetByte ();
1737 byte theClass = comm.GetByte ();
1738 string message;
1739 string server;
1740 string procedure;
1741 byte lineNumber;
1742 string source;
1743 bool isError = false;
1745 if (subType == TdsPacketSubType.EED) {
1746 isError = (theClass > 10);
1747 comm.Skip (comm.GetByte ()); // SQL State
1748 comm.Skip (1); // Status
1749 comm.Skip (2); // TranState
1750 } else
1751 isError = (subType == TdsPacketSubType.Error);
1753 message = comm.GetString (comm.GetTdsShort ());
1754 server = comm.GetString (comm.GetByte ());
1755 procedure = comm.GetString (comm.GetByte ());
1756 lineNumber = comm.GetByte ();
1757 comm.Skip (1);
1758 source = String.Empty; // FIXME
1760 if (isError)
1761 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1762 else
1763 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1766 protected virtual void ProcessOutputParam ()
1768 GetSubPacketLength ();
1769 /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1770 comm.Skip (5);
1772 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1773 object value = GetColumnValue (colType, true);
1774 outputParameters.Add (value);
1777 protected void ProcessDynamic ()
1779 Comm.Skip (2);
1780 /*byte type =*/ Comm.GetByte ();
1781 /*byte status =*/ Comm.GetByte ();
1782 /*string id =*/ Comm.GetString (Comm.GetByte ());
1785 protected virtual TdsPacketSubType ProcessSubPacket ()
1787 // VARADHAN: TDS 8 Debugging
1788 // Console.WriteLine ("In ProcessSubPacket... entry");
1790 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1792 // VARADHAN: TDS 8 Debugging
1793 //Console.WriteLine ("Subpacket type: {0}", subType);
1794 switch (subType) {
1795 case TdsPacketSubType.Dynamic2:
1796 comm.Skip (comm.GetTdsInt ());
1797 break;
1798 case TdsPacketSubType.AltName:
1799 case TdsPacketSubType.AltFormat:
1800 case TdsPacketSubType.Capability:
1801 case TdsPacketSubType.ParamFormat:
1802 comm.Skip (comm.GetTdsShort ());
1803 break;
1804 case TdsPacketSubType.Dynamic:
1805 ProcessDynamic ();
1806 break;
1807 case TdsPacketSubType.EnvironmentChange:
1808 ProcessEnvironmentChange ();
1809 break;
1810 case TdsPacketSubType.Info: // TDS 4.2/7.0
1811 case TdsPacketSubType.EED: // TDS 5.0
1812 case TdsPacketSubType.Error: // TDS 4.2/7.0
1813 ProcessMessage (subType);
1814 break;
1815 case TdsPacketSubType.Param:
1816 ProcessOutputParam ();
1817 break;
1818 case TdsPacketSubType.LoginAck:
1819 ProcessLoginAck ();
1820 break;
1821 case TdsPacketSubType.Authentication: // TDS 7.0
1822 ProcessAuthentication ();
1823 break;
1824 case TdsPacketSubType.ReturnStatus :
1825 ProcessReturnStatus ();
1826 break;
1827 case TdsPacketSubType.ProcId:
1828 Comm.Skip (8);
1829 break;
1830 case TdsPacketSubType.Done:
1831 case TdsPacketSubType.DoneProc:
1832 case TdsPacketSubType.DoneInProc:
1833 ProcessEndToken (subType);
1834 break;
1835 case TdsPacketSubType.ColumnName:
1836 Comm.Skip (8);
1837 ProcessColumnNames ();
1838 break;
1839 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1840 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1841 case TdsPacketSubType.RowFormat: // TDS 5.0
1842 Columns.Clear ();
1843 ProcessColumnInfo ();
1844 break;
1845 case TdsPacketSubType.ColumnDetail:
1846 ProcessColumnDetail ();
1847 break;
1848 case TdsPacketSubType.TableName:
1849 ProcessTableName ();
1850 break;
1851 case TdsPacketSubType.ColumnOrder:
1852 comm.Skip (comm.GetTdsShort ());
1853 break;
1854 case TdsPacketSubType.Control:
1855 comm.Skip (comm.GetTdsShort ());
1856 break;
1857 case TdsPacketSubType.Row:
1858 LoadRow ();
1859 break;
1862 // VARADHAN: TDS 8 Debugging
1863 //Console.WriteLine ("In ProcessSubPacket... exit");
1864 return subType;
1867 protected void ProcessTableName ()
1869 tableNames = new ArrayList ();
1870 int totalLength = comm.GetTdsShort ();
1871 int position = 0;
1872 int len;
1874 while (position < totalLength) {
1875 if (tdsVersion >= TdsVersion.tds70) {
1876 len = comm.GetTdsShort ();
1877 position += 2 * (len + 1);
1879 else {
1880 len = comm.GetByte ();
1881 position += len + 1;
1883 tableNames.Add (comm.GetString (len));
1887 protected void SetCharset (Encoding encoder)
1889 comm.Encoder = encoder;
1892 protected void SetCharset (string charset)
1894 if (charset == null || charset.Length > 30)
1895 charset = "iso_1";
1897 if (this.charset != null && this.charset == charset)
1898 return;
1900 if (charset.StartsWith ("cp")) {
1901 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1902 this.charset = charset;
1904 else {
1905 encoder = Encoding.GetEncoding ("iso-8859-1");
1906 this.charset = "iso_1";
1908 SetCharset (encoder);
1911 protected void SetLanguage (string language)
1913 if (language == null || language.Length > 30)
1914 language = "us_english";
1916 this.language = language;
1919 protected virtual void ProcessReturnStatus ()
1921 comm.Skip(4);
1924 public static string GetPlainPassword(SecureString secPass)
1926 IntPtr plainString = IntPtr.Zero;
1929 plainString = Marshal.SecureStringToGlobalAllocUnicode(secPass);
1930 return Marshal.PtrToStringUni(plainString);
1932 finally
1934 Marshal.ZeroFreeGlobalAllocUnicode(plainString);
1938 #endregion // Private Methods
1940 #if NET_2_0
1941 #region asynchronous methods
1942 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1943 AsyncCallback callback, object state)
1945 InitExec ();
1947 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1948 ar.TdsAsyncState.WantResults = wantResults;
1950 Comm.StartPacket (TdsPacketType.Query);
1951 Comm.Append (sql);
1952 try {
1953 Comm.SendPacket ();
1954 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1955 ar);
1956 } catch (IOException ex) {
1957 connected = false;
1958 throw new TdsInternalException ("Server closed the connection.", ex);
1961 return ar;
1964 protected void EndExecuteQueryInternal (IAsyncResult ar)
1966 if (!ar.IsCompleted)
1967 ar.AsyncWaitHandle.WaitOne ();
1968 TdsAsyncResult result = (TdsAsyncResult) ar;
1969 if (result.IsCompletedWithException)
1970 throw result.Exception;
1973 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1975 TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1976 TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1978 try {
1979 Comm.EndReadPacket (ar);
1980 if (!tdsState.WantResults)
1981 SkipToEnd ();
1982 } catch (Exception e) {
1983 result.MarkComplete (e);
1984 return;
1986 result.MarkComplete ();
1990 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1991 TdsMetaParameterCollection parameters,
1992 AsyncCallback callback,
1993 object state)
1995 // abstract, kept to be backward compatiable.
1996 throw new NotImplementedException ("should not be called!");
1999 public virtual void EndExecuteNonQuery (IAsyncResult ar)
2001 // abstract method
2002 throw new NotImplementedException ("should not be called!");
2005 public virtual IAsyncResult BeginExecuteQuery (string sql,
2006 TdsMetaParameterCollection parameters,
2007 AsyncCallback callback,
2008 object state)
2010 // abstract, kept to be backward compatiable.
2011 throw new NotImplementedException ("should not be called!");
2014 public virtual void EndExecuteQuery (IAsyncResult ar)
2016 // abstract method
2017 throw new NotImplementedException ("should not be called!");
2020 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
2021 string epilog,
2022 string cmdText,
2023 bool IsNonQuery,
2024 TdsMetaParameterCollection parameters,
2025 AsyncCallback callback,
2026 object state)
2028 throw new NotImplementedException ("should not be called!");
2031 public virtual void EndExecuteProcedure (IAsyncResult ar)
2033 // abstract method
2034 throw new NotImplementedException ("should not be called!");
2037 public void WaitFor (IAsyncResult ar)
2039 if (! ar.IsCompleted)
2040 ar.AsyncWaitHandle.WaitOne ();
2043 public void CheckAndThrowException (IAsyncResult ar)
2045 TdsAsyncResult result = (TdsAsyncResult) ar;
2046 if (result.IsCompleted && result.IsCompletedWithException)
2047 throw result.Exception;
2050 #endregion // asynchronous methods
2051 #endif // NET_2_0