**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds.cs
bloba3090fefa49f5c15c268bc9f29bd1839e6bcbc99
1 //
2 // Mono.Data.Tds.Protocol.Tds.cs
3 //
4 // Author:
5 // Tim Coleman (tim@timcoleman.com)
6 // Sebastien Pouliot (spouliot@motus.com)
7 // Daniel Morgan (danielmorgan@verizon.net)
8 //
9 // Copyright (C) 2002 Tim Coleman
10 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
11 // Portions (C) 2003 Daniel Morgan
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using Mono.Security.Protocol.Ntlm;
36 using System;
37 using System.Collections;
38 using System.ComponentModel;
39 using System.Diagnostics;
40 using System.Net.Sockets;
41 using System.Text;
43 namespace Mono.Data.Tds.Protocol {
44 public abstract class Tds : Component, ITds
46 #region Fields
48 TdsComm comm;
49 TdsVersion tdsVersion;
51 protected internal TdsConnectionParameters connectionParms;
52 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
54 int packetSize;
55 string dataSource;
56 string database;
57 string databaseProductName;
58 string databaseProductVersion;
59 int databaseMajorVersion;
61 string charset;
62 string language;
64 bool connected = false;
65 bool moreResults;
67 Encoding encoder;
68 // bool autoCommit;
70 bool doneProc;
71 TdsDataRow currentRow = null;
72 TdsDataColumnCollection columns;
74 ArrayList tableNames;
75 ArrayList columnNames;
77 TdsMetaParameterCollection parameters;
79 bool queryInProgress;
80 int cancelsRequested;
81 int cancelsProcessed;
83 // bool isDone;
84 // bool isDoneInProc;
86 ArrayList outputParameters = new ArrayList ();
87 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
89 int recordsAffected = 0;
91 #endregion // Fields
93 #region Properties
95 protected string Charset {
96 get { return charset; }
99 public bool DoneProc {
100 get { return doneProc; }
103 protected string Language {
104 get { return language; }
107 protected ArrayList ColumnNames {
108 get { return columnNames; }
111 public TdsDataRow ColumnValues {
112 get { return currentRow; }
115 internal TdsComm Comm {
116 get { return comm; }
119 public string Database {
120 get { return database; }
123 public string DataSource {
124 get { return dataSource; }
127 public bool IsConnected {
128 get { return connected; }
129 set { connected = value; }
132 public bool MoreResults {
133 get { return moreResults; }
134 set { moreResults = value; }
137 public int PacketSize {
138 get { return packetSize; }
141 public int RecordsAffected {
142 get { return recordsAffected; }
143 set { recordsAffected = value; }
146 public string ServerVersion {
147 get { return databaseProductVersion; }
150 public TdsDataColumnCollection Columns {
151 get { return columns; }
154 public TdsVersion TdsVersion {
155 get { return tdsVersion; }
158 public ArrayList OutputParameters {
159 get { return outputParameters; }
160 set { outputParameters = value; }
163 protected TdsMetaParameterCollection Parameters {
164 get { return parameters; }
165 set { parameters = value; }
168 #endregion // Properties
170 #region Events
172 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
173 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
175 #endregion // Events
177 #region Constructors
179 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
181 this.tdsVersion = tdsVersion;
182 this.packetSize = packetSize;
183 this.dataSource = dataSource;
185 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
188 #endregion // Constructors
190 #region Public Methods
192 public void Cancel ()
194 if (queryInProgress) {
195 if (cancelsRequested == cancelsProcessed) {
196 comm.StartPacket (TdsPacketType.Cancel);
197 comm.SendPacket ();
198 cancelsRequested += 1;
203 public abstract bool Connect (TdsConnectionParameters connectionParameters);
205 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
207 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
208 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
211 public void Disconnect ()
213 comm.StartPacket (TdsPacketType.Logoff);
214 comm.Append ((byte) 0);
215 comm.SendPacket ();
216 comm.Close ();
217 connected = false;
220 public virtual bool Reset ()
222 return true;
225 public void Execute (string sql)
227 Execute (sql, null, 0, false);
230 public void ExecProc (string sql)
232 ExecProc (sql, null, 0, false);
235 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
237 ExecuteQuery (sql, timeout, wantResults);
240 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
242 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
245 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
247 throw new NotSupportedException ();
250 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
252 moreResults = true;
253 doneProc = false;
254 messages.Clear ();
255 outputParameters.Clear ();
257 Comm.StartPacket (TdsPacketType.Query);
258 Comm.Append (sql);
259 Comm.SendPacket ();
261 CheckForData (timeout);
262 if (!wantResults)
263 SkipToEnd ();
266 public bool NextResult ()
268 if (!moreResults)
269 return false;
271 TdsPacketSubType subType;
273 bool done = false;
274 bool outputParams = false;
276 while (!done) {
277 subType = ProcessSubPacket ();
278 if (outputParams) {
279 moreResults = false;
280 break;
283 switch (subType) {
284 case TdsPacketSubType.ColumnInfo:
285 case TdsPacketSubType.ColumnMetadata:
286 case TdsPacketSubType.RowFormat:
287 byte peek = Comm.Peek ();
288 done = (peek != (byte) TdsPacketSubType.TableName);
289 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
290 outputParams = true;
291 done = false;
294 break;
295 case TdsPacketSubType.TableName:
296 // done = true;
297 peek = Comm.Peek ();
298 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
300 break;
301 case TdsPacketSubType.ColumnDetail:
302 done = true;
303 break;
304 default:
305 done = !moreResults;
306 break;
310 return moreResults;
313 public bool NextRow ()
315 TdsPacketSubType subType;
316 bool done = false;
317 bool result = false;
319 do {
320 subType = ProcessSubPacket ();
321 switch (subType) {
322 case TdsPacketSubType.Row:
323 result = true;
324 done = true;
325 break;
326 case TdsPacketSubType.Done:
327 case TdsPacketSubType.DoneProc:
328 case TdsPacketSubType.DoneInProc:
329 result = false;
330 done = true;
331 break;
333 } while (!done);
335 return result;
338 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
340 throw new NotSupportedException ();
343 public void SkipToEnd ()
345 while (NextResult ()) { /* DO NOTHING */ }
348 public virtual void Unprepare (string statementId)
350 throw new NotSupportedException ();
353 #endregion // Public Methods
355 #region // Private Methods
357 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
358 protected void CheckForData (int timeout)
360 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
361 Cancel ();
362 throw CreateTimeoutException (dataSource, "CheckForData()");
366 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
368 return new TdsInternalInfoMessageEventArgs (errors);
371 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
373 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
376 private object GetColumnValue (TdsColumnType colType, bool outParam)
378 return GetColumnValue (colType, outParam, -1);
381 private object GetColumnValue (TdsColumnType colType, bool outParam, int ordinal)
383 int len;
384 object element = null;
386 switch (colType) {
387 case TdsColumnType.IntN :
388 if (outParam)
389 comm.Skip (1);
390 element = GetIntValue (colType);
391 break;
392 case TdsColumnType.Int1 :
393 case TdsColumnType.Int2 :
394 case TdsColumnType.Int4 :
395 element = GetIntValue (colType);
396 break;
397 case TdsColumnType.Image :
398 if (outParam)
399 comm.Skip (1);
400 element = GetImageValue ();
401 break;
402 case TdsColumnType.Text :
403 if (outParam)
404 comm.Skip (1);
405 element = GetTextValue (false);
406 break;
407 case TdsColumnType.NText :
408 if (outParam)
409 comm.Skip (1);
410 element = GetTextValue (true);
411 break;
412 case TdsColumnType.Char :
413 case TdsColumnType.VarChar :
414 if (outParam)
415 comm.Skip (1);
416 element = GetStringValue (false, false);
417 break;
418 case TdsColumnType.BigVarBinary :
419 comm.GetTdsShort ();
420 len = comm.GetTdsShort ();
421 element = comm.GetBytes (len, true);
422 break;
423 case TdsColumnType.BigVarChar :
424 comm.Skip (2);
425 element = GetStringValue (false, false);
426 break;
427 case TdsColumnType.NChar :
428 case TdsColumnType.NVarChar :
429 if (outParam)
430 comm.Skip (1);
431 element = GetStringValue (true, false);
432 break;
433 case TdsColumnType.Real :
434 case TdsColumnType.Float8 :
435 element = GetFloatValue (colType);
436 break;
437 case TdsColumnType.FloatN :
438 if (outParam)
439 comm.Skip (1);
440 element = GetFloatValue (colType);
441 break;
442 case TdsColumnType.SmallMoney :
443 case TdsColumnType.Money :
444 element = GetMoneyValue (colType);
445 break;
446 case TdsColumnType.MoneyN :
447 if (outParam)
448 comm.Skip (1);
449 element = GetMoneyValue (colType);
450 break;
451 case TdsColumnType.Numeric :
452 case TdsColumnType.Decimal :
453 byte precision;
454 byte scale;
455 if (outParam) {
456 comm.Skip (1);
457 precision = comm.GetByte ();
458 scale = comm.GetByte ();
460 else {
461 precision = (byte) columns[ordinal]["NumericPrecision"];
462 scale = (byte) columns[ordinal]["NumericScale"];
465 element = GetDecimalValue (precision, scale);
466 break;
467 case TdsColumnType.DateTimeN :
468 if (outParam)
469 comm.Skip (1);
470 element = GetDateTimeValue (colType);
471 break;
472 case TdsColumnType.DateTime4 :
473 case TdsColumnType.DateTime :
474 element = GetDateTimeValue (colType);
475 break;
476 case TdsColumnType.VarBinary :
477 case TdsColumnType.Binary :
478 if (outParam)
479 comm.Skip (1);
480 element = GetBinaryValue ();
481 break;
482 case TdsColumnType.BitN :
483 if (outParam)
484 comm.Skip (1);
485 if (comm.GetByte () == 0)
486 element = DBNull.Value;
487 else
488 element = (comm.GetByte() != 0);
489 break;
490 case TdsColumnType.Bit :
491 int columnSize = comm.GetByte ();
492 element = (columnSize != 0);
493 break;
494 case TdsColumnType.UniqueIdentifier :
495 if (comm.Peek () != 16) { // If it's null, then what to do?
496 byte swallowed = comm.GetByte();
497 element = DBNull.Value;
498 break;
500 len = comm.GetByte () & 0xff;
501 if (len > 0) {
502 byte[] guidBytes = comm.GetBytes (len, true);
503 if (!BitConverter.IsLittleEndian) {
504 byte[] swappedguidBytes = new byte[len];
505 for (int i = 0; i < 4; i++)
506 swappedguidBytes[i] = guidBytes[4-i-1];
507 for (int i = 4; i < 6; i++)
508 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
509 for (int i = 6; i < 8; i++)
510 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
511 for (int i = 8; i < 16; i++)
512 swappedguidBytes[i] = guidBytes[i];
513 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
515 element = new Guid (guidBytes);
517 break;
518 default :
519 return DBNull.Value;
522 return element;
525 private object GetBinaryValue ()
527 int len;
528 object result = DBNull.Value;
529 if (tdsVersion == TdsVersion.tds70) {
530 len = comm.GetTdsShort ();
531 if (len != 0xffff && len > 0)
532 result = comm.GetBytes (len, true);
534 else {
535 len = (comm.GetByte () & 0xff);
536 if (len != 0)
537 result = comm.GetBytes (len, true);
539 return result;
542 private object GetDateTimeValue (TdsColumnType type)
544 int len = 0;
545 object result;
547 switch (type) {
548 case TdsColumnType.DateTime4:
549 len = 4;
550 break;
551 case TdsColumnType.DateTime:
552 len = 8;
553 break;
554 case TdsColumnType.DateTimeN:
555 byte tmp = comm.Peek ();
556 if (tmp != 0 && tmp != 4 && tmp != 8)
557 break;
558 len = comm.GetByte ();
559 break;
562 DateTime epoch = new DateTime (1900, 1, 1);
564 switch (len) {
565 case 8 :
566 result = epoch.AddDays (comm.GetTdsInt ());
567 int seconds = comm.GetTdsInt ();
568 long millis = ((((long) seconds) % 300L) * 1000L) / 300L;
569 if (seconds != 0 || millis != 0) {
570 result = ((DateTime) result).AddSeconds (seconds / 300);
571 result = ((DateTime) result).AddMilliseconds (millis);
573 break;
574 case 4 :
575 result = epoch.AddDays ((int) comm.GetTdsShort ());
576 short minutes = comm.GetTdsShort ();
577 if (minutes != 0)
578 result = ((DateTime) result).AddMinutes ((int) minutes);
579 break;
580 default:
581 result = DBNull.Value;
582 break;
585 return result;
588 private object GetDecimalValue (byte precision, byte scale)
590 int[] bits = new int[4] {0,0,0,0};
592 int len = (comm.GetByte() & 0xff) - 1;
593 if (len < 0)
594 return DBNull.Value;
596 bool positive = (comm.GetByte () == 1);
598 if (len > 16)
599 throw new OverflowException ();
601 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
602 bits[index] = comm.GetTdsInt ();
604 if (bits [3] != 0)
605 return new TdsBigDecimal (precision, scale, !positive, bits);
606 else
607 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
610 private object GetFloatValue (TdsColumnType columnType)
612 int columnSize = 0;
614 switch (columnType) {
615 case TdsColumnType.Real:
616 columnSize = 4;
617 break;
618 case TdsColumnType.Float8:
619 columnSize = 8;
620 break;
621 case TdsColumnType.FloatN:
622 columnSize = comm.GetByte ();
623 break;
626 switch (columnSize) {
627 case 8 :
628 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
629 case 4 :
630 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
631 default :
632 return DBNull.Value;
636 private object GetImageValue ()
638 byte hasValue = comm.GetByte ();
640 if (hasValue == 0)
641 return DBNull.Value;
643 comm.Skip (24);
644 int len = comm.GetTdsInt ();
646 if (len < 0)
647 return DBNull.Value;
649 return (comm.GetBytes (len, true));
652 private object GetIntValue (TdsColumnType type)
654 int len;
656 switch (type) {
657 case TdsColumnType.IntN :
658 len = comm.GetByte ();
659 break;
660 case TdsColumnType.Int4 :
661 len = 4;
662 break;
663 case TdsColumnType.Int2 :
664 len = 2;
665 break;
666 case TdsColumnType.Int1 :
667 len = 1;
668 break;
669 default:
670 return DBNull.Value;
673 switch (len) {
674 case 4 :
675 return (comm.GetTdsInt ());
676 case 2 :
677 return (comm.GetTdsShort ());
678 case 1 :
679 return (comm.GetByte ());
680 default:
681 return DBNull.Value;
685 [MonoTODO]
686 private object GetMoneyValue (TdsColumnType type)
688 int len;
689 object result = null;
691 switch (type) {
692 case TdsColumnType.SmallMoney :
693 case TdsColumnType.Money4 :
694 len = 4;
695 break;
696 case TdsColumnType.Money :
697 len = 8;
698 break;
699 case TdsColumnType.MoneyN :
700 len = comm.GetByte ();
701 break;
702 default:
703 return DBNull.Value;
706 long rawValue = 0;
708 switch (len) {
709 case 4:
710 rawValue = comm.GetTdsInt ();
711 break;
712 case 8:
713 byte[] bits = new byte[8];
714 bits[4] = comm.GetByte ();
715 bits[5] = comm.GetByte ();
716 bits[6] = comm.GetByte ();
717 bits[7] = comm.GetByte ();
718 bits[0] = comm.GetByte ();
719 bits[1] = comm.GetByte ();
720 bits[2] = comm.GetByte ();
721 bits[3] = comm.GetByte ();
722 rawValue = BitConverter.ToInt64 (bits, 0);
723 break;
724 default:
725 return DBNull.Value;
728 result = new Decimal (rawValue);
730 return (((decimal) result) / 10000);
733 private object GetStringValue (bool wideChars, bool outputParam)
735 bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam);
736 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
738 if (tdsVersion < TdsVersion.tds70 && len == 0)
739 return DBNull.Value;
740 else if (len >= 0) {
741 object result;
742 if (wideChars)
743 result = comm.GetString (len / 2);
744 else
745 result = comm.GetString (len, false);
746 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
747 result = "";
748 return result;
750 else
751 return DBNull.Value;
754 protected int GetSubPacketLength ()
756 return comm.GetTdsShort ();
759 private object GetTextValue (bool wideChars)
761 string result = null;
762 byte hasValue = comm.GetByte ();
764 if (hasValue != 16)
765 return DBNull.Value;
767 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
768 comm.Skip (24);
770 int len = comm.GetTdsInt ();
772 //if the len is 0 , then the string can be a '' string
773 // this method is called only for Text and NText. Hence will
774 // return a empty string
775 if (len == 0)
776 return "";
778 if (wideChars)
779 result = comm.GetString (len / 2);
780 else
781 result = comm.GetString (len, false);
782 len /= 2;
784 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
785 result = "";
787 return result;
790 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
792 switch (columnType) {
793 case TdsColumnType.Int1 :
794 case TdsColumnType.Int2 :
795 case TdsColumnType.Int4 :
796 case TdsColumnType.Float8 :
797 case TdsColumnType.DateTime :
798 case TdsColumnType.Bit :
799 case TdsColumnType.Money :
800 case TdsColumnType.Money4 :
801 case TdsColumnType.SmallMoney :
802 case TdsColumnType.Real :
803 case TdsColumnType.DateTime4 :
804 return true;
805 case TdsColumnType.IntN :
806 case TdsColumnType.MoneyN :
807 case TdsColumnType.VarChar :
808 case TdsColumnType.NVarChar :
809 case TdsColumnType.DateTimeN :
810 case TdsColumnType.FloatN :
811 case TdsColumnType.Char :
812 case TdsColumnType.NChar :
813 case TdsColumnType.NText :
814 case TdsColumnType.Image :
815 case TdsColumnType.VarBinary :
816 case TdsColumnType.Binary :
817 case TdsColumnType.Decimal :
818 case TdsColumnType.Numeric :
819 case TdsColumnType.BitN :
820 case TdsColumnType.UniqueIdentifier :
821 return false;
822 default :
823 return false;
827 protected void LoadRow ()
829 currentRow = new TdsDataRow ();
831 int i = 0;
832 foreach (TdsDataColumn column in columns) {
833 object o = GetColumnValue ((TdsColumnType) column["ColumnType"], false, i);
834 currentRow.Add (o);
835 if (doneProc)
836 outputParameters.Add (o);
838 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
839 currentRow.BigDecimalIndex = i;
840 i += 1;
844 internal static int LookupBufferSize (TdsColumnType columnType)
846 switch (columnType) {
847 case TdsColumnType.Int1 :
848 case TdsColumnType.Bit :
849 return 1;
850 case TdsColumnType.Int2 :
851 return 2;
852 case TdsColumnType.Int4 :
853 case TdsColumnType.Real :
854 case TdsColumnType.DateTime4 :
855 case TdsColumnType.Money4 :
856 case TdsColumnType.SmallMoney :
857 return 4;
858 case TdsColumnType.Float8 :
859 case TdsColumnType.DateTime :
860 case TdsColumnType.Money :
861 return 8;
862 default :
863 return 0;
867 private int LookupDisplaySize (TdsColumnType columnType)
869 switch (columnType) {
870 case TdsColumnType.Int1 :
871 return 3;
872 case TdsColumnType.Int2 :
873 return 6;
874 case TdsColumnType.Int4 :
875 return 11;
876 case TdsColumnType.Real :
877 return 14;
878 case TdsColumnType.Float8 :
879 return 24;
880 case TdsColumnType.DateTime :
881 return 23;
882 case TdsColumnType.DateTime4 :
883 return 16;
884 case TdsColumnType.Bit :
885 return 1;
886 case TdsColumnType.Money :
887 return 21;
888 case TdsColumnType.Money4 :
889 case TdsColumnType.SmallMoney :
890 return 12;
891 default:
892 return 0;
896 protected internal int ProcessAuthentication ()
898 int pdu_size = Comm.GetTdsShort ();
899 byte[] msg2 = Comm.GetBytes (pdu_size, true);
901 Type2Message t2 = new Type2Message (msg2);
902 // 0x0001 Negotiate Unicode
903 // 0x0200 Negotiate NTLM
904 // 0x8000 Negotiate Always Sign
906 Type3Message t3 = new Type3Message ();
907 t3.Challenge = t2.Nonce;
909 t3.Domain = this.connectionParms.DefaultDomain;
910 t3.Host = this.connectionParms.Hostname;
911 t3.Username = this.connectionParms.User;
912 t3.Password = this.connectionParms.Password;
914 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
915 Comm.Append (t3.GetBytes ());
916 Comm.SendPacket ();
917 return 1; // TDS_SUCCEED
920 protected void ProcessColumnDetail ()
922 int len = GetSubPacketLength ();
923 byte[] values = new byte[3];
924 int columnNameLength;
925 string baseColumnName = String.Empty;
926 int position = 0;
928 while (position < len) {
929 for (int j = 0; j < 3; j += 1)
930 values[j] = comm.GetByte ();
931 position += 3;
933 if ((values[2] & (byte) TdsColumnStatus.Rename) != 0) {
934 if (tdsVersion == TdsVersion.tds70) {
935 columnNameLength = comm.GetByte ();
936 position += 2 * len + 1;
938 else {
939 columnNameLength = comm.GetByte ();
940 position += len + 1;
942 baseColumnName = comm.GetString (columnNameLength);
945 if ((values[2] & (byte) TdsColumnStatus.Hidden) == 0) {
946 byte index = (byte) (values[0] - (byte) 1);
947 byte tableIndex = (byte) (values[1] - (byte) 1);
949 columns [index]["IsExpression"] = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
950 columns [index]["IsKey"] = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
952 if ((values[2] & (byte) TdsColumnStatus.Rename) != 0)
953 columns [index]["BaseColumnName"] = baseColumnName;
954 columns [index]["BaseTableName"] = tableNames [tableIndex];
959 protected abstract TdsDataColumnCollection ProcessColumnInfo ();
961 protected void ProcessColumnNames ()
963 columnNames = new ArrayList ();
965 int totalLength = comm.GetTdsShort ();
966 int bytesRead = 0;
967 int i = 0;
969 while (bytesRead < totalLength) {
970 int columnNameLength = comm.GetByte ();
971 string columnName = comm.GetString (columnNameLength);
972 bytesRead = bytesRead + 1 + columnNameLength;
973 columnNames.Add (columnName);
974 i += 1;
978 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
979 protected void ProcessEndToken (TdsPacketSubType type)
981 byte status = Comm.GetByte ();
982 Comm.Skip (1);
983 byte op = comm.GetByte ();
984 Comm.Skip (1);
986 int rowCount = comm.GetTdsInt ();
988 if (op == (byte) 0xc1)
989 rowCount = 0;
990 if (type == TdsPacketSubType.DoneInProc)
991 rowCount = -1;
993 moreResults = ((status & 0x01) != 0);
994 bool cancelled = ((status & 0x20) != 0);
996 switch (type) {
997 case TdsPacketSubType.DoneProc:
998 doneProc = true;
999 goto case TdsPacketSubType.Done;
1001 case TdsPacketSubType.Done:
1002 if (rowCount > 0)
1003 recordsAffected += rowCount;
1004 break;
1007 if (moreResults)
1008 queryInProgress = false;
1009 if (cancelled)
1010 cancelsProcessed += 1;
1011 if (messages.Count > 0 && !moreResults)
1012 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1015 protected void ProcessEnvironmentChange ()
1017 int len = GetSubPacketLength ();
1018 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1019 int cLen;
1021 switch (type) {
1022 case TdsEnvPacketSubType.BlockSize :
1023 string blockSize;
1024 cLen = comm.GetByte () & 0xff;
1025 blockSize = comm.GetString (cLen);
1027 if (tdsVersion == TdsVersion.tds70)
1028 comm.Skip (len - 2 - cLen * 2);
1029 else
1030 comm.Skip (len - 2 - cLen);
1032 comm.ResizeOutBuf (Int32.Parse (blockSize));
1033 break;
1034 case TdsEnvPacketSubType.CharSet :
1035 cLen = comm.GetByte () & 0xff;
1036 if (tdsVersion == TdsVersion.tds70) {
1037 //this.language = comm.GetString (cLen); // FIXME
1038 comm.GetString (cLen);
1039 comm.Skip (len - 2 - cLen * 2);
1041 else {
1042 SetCharset (comm.GetString (cLen));
1043 comm.Skip (len - 2 - cLen);
1046 break;
1047 case TdsEnvPacketSubType.Database :
1048 cLen = comm.GetByte () & 0xff;
1049 string newDB = comm.GetString (cLen);
1050 cLen = comm.GetByte () & 0xff;
1051 string oldDB = comm.GetString (cLen);
1052 database = newDB;
1053 break;
1054 default:
1055 comm.Skip (len - 1);
1056 break;
1060 protected void ProcessLoginAck ()
1062 GetSubPacketLength ();
1064 if (tdsVersion == TdsVersion.tds70) {
1065 comm.Skip (5);
1066 int nameLength = comm.GetByte ();
1067 databaseProductName = comm.GetString (nameLength);
1068 databaseMajorVersion = comm.GetByte ();
1069 databaseProductVersion = String.Format ("0{0}.0{1}.0{2}", databaseMajorVersion, comm.GetByte (), ((256 * (comm.GetByte () + 1)) + comm.GetByte ()));
1071 else {
1072 comm.Skip (5);
1073 short nameLength = comm.GetByte ();
1074 databaseProductName = comm.GetString (nameLength);
1075 comm.Skip (1);
1076 databaseMajorVersion = comm.GetByte ();
1077 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1078 comm.Skip (1);
1081 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1082 int last = databaseProductName.IndexOf ('\0');
1083 databaseProductName = databaseProductName.Substring (0, last);
1086 connected = true;
1089 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1091 if (TdsErrorMessage != null)
1092 TdsErrorMessage (this, e);
1095 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1097 if (TdsInfoMessage != null)
1098 TdsInfoMessage (this, e);
1099 messages.Clear ();
1102 protected void ProcessMessage (TdsPacketSubType subType)
1104 GetSubPacketLength ();
1106 int number = comm.GetTdsInt ();
1107 byte state = comm.GetByte ();
1108 byte theClass = comm.GetByte ();
1109 string message;
1110 string server;
1111 string procedure;
1112 byte lineNumber;
1113 string source;
1114 bool isError = false;
1116 if (subType == TdsPacketSubType.EED) {
1117 isError = (theClass > 10);
1118 comm.Skip (comm.GetByte ()); // SQL State
1119 comm.Skip (1); // Status
1120 comm.Skip (2); // TranState
1121 } else
1122 isError = (subType == TdsPacketSubType.Error);
1124 message = comm.GetString (comm.GetTdsShort ());
1125 server = comm.GetString (comm.GetByte ());
1126 procedure = comm.GetString (comm.GetByte ());
1127 lineNumber = comm.GetByte ();
1128 comm.Skip (1);
1129 source = String.Empty; // FIXME
1131 if (isError)
1132 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1133 else
1134 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1137 protected void ProcessOutputParam ()
1139 GetSubPacketLength ();
1140 comm.GetString (comm.GetByte () & 0xff);
1141 comm.Skip (5);
1143 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1144 object value = GetColumnValue (colType, true);
1146 outputParameters.Add (value);
1149 protected void ProcessDynamic ()
1151 Comm.Skip (2);
1152 byte type = Comm.GetByte ();
1153 byte status = Comm.GetByte ();
1154 string id = Comm.GetString (Comm.GetByte ());
1157 protected virtual TdsPacketSubType ProcessSubPacket ()
1159 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1161 switch (subType) {
1162 case TdsPacketSubType.Dynamic2:
1163 comm.Skip (comm.GetTdsInt ());
1164 break;
1165 case TdsPacketSubType.AltName:
1166 case TdsPacketSubType.AltFormat:
1167 case TdsPacketSubType.Capability:
1168 case TdsPacketSubType.ParamFormat:
1169 comm.Skip (comm.GetTdsShort ());
1170 break;
1171 case TdsPacketSubType.Dynamic:
1172 ProcessDynamic ();
1173 break;
1174 case TdsPacketSubType.EnvironmentChange:
1175 ProcessEnvironmentChange ();
1176 break;
1177 case TdsPacketSubType.Info: // TDS 4.2/7.0
1178 case TdsPacketSubType.EED: // TDS 5.0
1179 case TdsPacketSubType.Error: // TDS 4.2/7.0
1180 ProcessMessage (subType);
1181 break;
1182 case TdsPacketSubType.Param:
1183 ProcessOutputParam ();
1184 break;
1185 case TdsPacketSubType.LoginAck:
1186 ProcessLoginAck ();
1187 break;
1188 case TdsPacketSubType.Authentication: // TDS 7.0
1189 ProcessAuthentication ();
1190 break;
1191 case TdsPacketSubType.ReturnStatus :
1192 Comm.Skip (4);
1193 break;
1194 case TdsPacketSubType.ProcId:
1195 Comm.Skip (8);
1196 break;
1197 case TdsPacketSubType.Done:
1198 case TdsPacketSubType.DoneProc:
1199 case TdsPacketSubType.DoneInProc:
1200 ProcessEndToken (subType);
1201 break;
1202 case TdsPacketSubType.ColumnName:
1203 Comm.Skip (8);
1204 ProcessColumnNames ();
1205 break;
1206 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1207 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1208 case TdsPacketSubType.RowFormat: // TDS 5.0
1209 columns = ProcessColumnInfo ();
1210 break;
1211 case TdsPacketSubType.ColumnDetail:
1212 ProcessColumnDetail ();
1213 break;
1214 case TdsPacketSubType.TableName:
1215 ProcessTableName ();
1216 break;
1217 case TdsPacketSubType.ColumnOrder:
1218 comm.Skip (comm.GetTdsShort ());
1219 break;
1220 case TdsPacketSubType.Control:
1221 comm.Skip (comm.GetTdsShort ());
1222 break;
1223 case TdsPacketSubType.Row:
1224 LoadRow ();
1225 break;
1228 return subType;
1231 protected void ProcessTableName ()
1233 tableNames = new ArrayList ();
1234 int totalLength = comm.GetTdsShort ();
1235 int position = 0;
1236 int len;
1238 while (position < totalLength) {
1239 if (tdsVersion == TdsVersion.tds70) {
1240 len = comm.GetTdsShort ();
1241 position += 2 * (len + 1);
1243 else {
1244 len = comm.GetByte ();
1245 position += len + 1;
1247 tableNames.Add (comm.GetString (len));
1251 protected void SetCharset (string charset)
1253 if (charset == null || charset.Length > 30)
1254 charset = "iso_1";
1256 if (this.charset != null && this.charset != charset)
1257 return;
1259 if (charset.StartsWith ("cp")) {
1260 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1261 this.charset = charset;
1263 else {
1264 encoder = Encoding.GetEncoding ("iso-8859-1");
1265 this.charset = "iso_1";
1267 comm.Encoder = encoder;
1270 protected void SetLanguage (string language)
1272 if (language == null || language.Length > 30)
1273 language = "us_english";
1275 this.language = language;
1278 #endregion // Private Methods