2 // Mono.Data.Tds.Protocol.Tds.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Sebastien Pouliot (spouliot@motus.com)
7 // Daniel Morgan (danielmorgan@verizon.net)
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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
;
37 using System
.Collections
;
38 using System
.ComponentModel
;
39 using System
.Diagnostics
;
40 using System
.Net
.Sockets
;
43 namespace Mono
.Data
.Tds
.Protocol
{
44 public abstract class Tds
: Component
, ITds
49 TdsVersion tdsVersion
;
51 protected internal TdsConnectionParameters connectionParms
;
52 protected readonly byte[] NTLMSSP_ID
= new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00}
;
57 string databaseProductName
;
58 string databaseProductVersion
;
59 int databaseMajorVersion
;
64 bool connected
= false;
71 TdsDataRow currentRow
= null;
72 TdsDataColumnCollection columns
;
75 ArrayList columnNames
;
77 TdsMetaParameterCollection parameters
;
86 ArrayList outputParameters
= new ArrayList ();
87 protected TdsInternalErrorCollection messages
= new TdsInternalErrorCollection ();
89 int recordsAffected
= 0;
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
{
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
172 public event TdsInternalErrorMessageEventHandler TdsErrorMessage
;
173 public event TdsInternalInfoMessageEventHandler TdsInfoMessage
;
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
);
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);
220 public virtual bool Reset ()
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
)
255 outputParameters
.Clear ();
257 Comm
.StartPacket (TdsPacketType
.Query
);
261 CheckForData (timeout
);
266 public bool NextResult ()
271 TdsPacketSubType subType
;
274 bool outputParams
= false;
277 subType
= ProcessSubPacket ();
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
) {
295 case TdsPacketSubType
.TableName
:
298 done
= (peek
!= (byte) TdsPacketSubType
.ColumnDetail
);
301 case TdsPacketSubType
.ColumnDetail
:
313 public bool NextRow ()
315 TdsPacketSubType subType
;
320 subType
= ProcessSubPacket ();
322 case TdsPacketSubType
.Row
:
326 case TdsPacketSubType
.Done
:
327 case TdsPacketSubType
.DoneProc
:
328 case TdsPacketSubType
.DoneInProc
:
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
)) {
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
)
384 object element
= null;
387 case TdsColumnType
.IntN
:
390 element
= GetIntValue (colType
);
392 case TdsColumnType
.Int1
:
393 case TdsColumnType
.Int2
:
394 case TdsColumnType
.Int4
:
395 element
= GetIntValue (colType
);
397 case TdsColumnType
.Image
:
400 element
= GetImageValue ();
402 case TdsColumnType
.Text
:
405 element
= GetTextValue (false);
407 case TdsColumnType
.NText
:
410 element
= GetTextValue (true);
412 case TdsColumnType
.Char
:
413 case TdsColumnType
.VarChar
:
416 element
= GetStringValue (false, false);
418 case TdsColumnType
.BigVarBinary
:
420 len
= comm
.GetTdsShort ();
421 element
= comm
.GetBytes (len
, true);
423 case TdsColumnType
.BigVarChar
:
425 element
= GetStringValue (false, false);
427 case TdsColumnType
.NChar
:
428 case TdsColumnType
.NVarChar
:
431 element
= GetStringValue (true, false);
433 case TdsColumnType
.Real
:
434 case TdsColumnType
.Float8
:
435 element
= GetFloatValue (colType
);
437 case TdsColumnType
.FloatN
:
440 element
= GetFloatValue (colType
);
442 case TdsColumnType
.SmallMoney
:
443 case TdsColumnType
.Money
:
444 element
= GetMoneyValue (colType
);
446 case TdsColumnType
.MoneyN
:
449 element
= GetMoneyValue (colType
);
451 case TdsColumnType
.Numeric
:
452 case TdsColumnType
.Decimal
:
457 precision
= comm
.GetByte ();
458 scale
= comm
.GetByte ();
461 precision
= (byte) columns
[ordinal
]["NumericPrecision"];
462 scale
= (byte) columns
[ordinal
]["NumericScale"];
465 element
= GetDecimalValue (precision
, scale
);
467 case TdsColumnType
.DateTimeN
:
470 element
= GetDateTimeValue (colType
);
472 case TdsColumnType
.DateTime4
:
473 case TdsColumnType
.DateTime
:
474 element
= GetDateTimeValue (colType
);
476 case TdsColumnType
.VarBinary
:
477 case TdsColumnType
.Binary
:
480 element
= GetBinaryValue ();
482 case TdsColumnType
.BitN
:
485 if (comm
.GetByte () == 0)
486 element
= DBNull
.Value
;
488 element
= (comm
.GetByte() != 0);
490 case TdsColumnType
.Bit
:
491 int columnSize
= comm
.GetByte ();
492 element
= (columnSize
!= 0);
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
;
500 len
= comm
.GetByte () & 0xff;
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
);
525 private object GetBinaryValue ()
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);
535 len
= (comm
.GetByte () & 0xff);
537 result
= comm
.GetBytes (len
, true);
542 private object GetDateTimeValue (TdsColumnType type
)
548 case TdsColumnType
.DateTime4
:
551 case TdsColumnType
.DateTime
:
554 case TdsColumnType
.DateTimeN
:
555 byte tmp
= comm
.Peek ();
556 if (tmp
!= 0 && tmp
!= 4 && tmp
!= 8)
558 len
= comm
.GetByte ();
562 DateTime epoch
= new DateTime (1900, 1, 1);
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
);
575 result
= epoch
.AddDays ((int) comm
.GetTdsShort ());
576 short minutes
= comm
.GetTdsShort ();
578 result
= ((DateTime
) result
).AddMinutes ((int) minutes
);
581 result
= DBNull
.Value
;
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;
596 bool positive
= (comm
.GetByte () == 1);
599 throw new OverflowException ();
601 for (int i
= 0, index
= 0; i
< len
&& i
< 16; i
+= 4, index
+= 1)
602 bits
[index
] = comm
.GetTdsInt ();
605 return new TdsBigDecimal (precision
, scale
, !positive
, bits
);
607 return new Decimal (bits
[0], bits
[1], bits
[2], !positive
, scale
);
610 private object GetFloatValue (TdsColumnType columnType
)
614 switch (columnType
) {
615 case TdsColumnType
.Real
:
618 case TdsColumnType
.Float8
:
621 case TdsColumnType
.FloatN
:
622 columnSize
= comm
.GetByte ();
626 switch (columnSize
) {
628 return BitConverter
.Int64BitsToDouble (comm
.GetTdsInt64 ());
630 return BitConverter
.ToSingle (BitConverter
.GetBytes (comm
.GetTdsInt ()), 0);
636 private object GetImageValue ()
638 byte hasValue
= comm
.GetByte ();
644 int len
= comm
.GetTdsInt ();
649 return (comm
.GetBytes (len
, true));
652 private object GetIntValue (TdsColumnType type
)
657 case TdsColumnType
.IntN
:
658 len
= comm
.GetByte ();
660 case TdsColumnType
.Int4
:
663 case TdsColumnType
.Int2
:
666 case TdsColumnType
.Int1
:
675 return (comm
.GetTdsInt ());
677 return (comm
.GetTdsShort ());
679 return (comm
.GetByte ());
686 private object GetMoneyValue (TdsColumnType type
)
689 object result
= null;
692 case TdsColumnType
.SmallMoney
:
693 case TdsColumnType
.Money4
:
696 case TdsColumnType
.Money
:
699 case TdsColumnType
.MoneyN
:
700 len
= comm
.GetByte ();
710 rawValue
= comm
.GetTdsInt ();
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);
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)
743 result
= comm
.GetString (len
/ 2);
745 result
= comm
.GetString (len
, false);
746 if (tdsVersion
< TdsVersion
.tds70
&& ((string) result
).Equals (" "))
754 protected int GetSubPacketLength ()
756 return comm
.GetTdsShort ();
759 private object GetTextValue (bool wideChars
)
761 string result
= null;
762 byte hasValue
= comm
.GetByte ();
767 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
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
779 result
= comm
.GetString (len
/ 2);
781 result
= comm
.GetString (len
, false);
784 if ((byte) tdsVersion
< (byte) TdsVersion
.tds70
&& 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
:
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
:
827 protected void LoadRow ()
829 currentRow
= new TdsDataRow ();
832 foreach (TdsDataColumn column
in columns
) {
833 object o
= GetColumnValue ((TdsColumnType
) column
["ColumnType"], false, i
);
836 outputParameters
.Add (o
);
838 if (o
is TdsBigDecimal
&& currentRow
.BigDecimalIndex
< 0)
839 currentRow
.BigDecimalIndex
= i
;
844 internal static int LookupBufferSize (TdsColumnType columnType
)
846 switch (columnType
) {
847 case TdsColumnType
.Int1
:
848 case TdsColumnType
.Bit
:
850 case TdsColumnType
.Int2
:
852 case TdsColumnType
.Int4
:
853 case TdsColumnType
.Real
:
854 case TdsColumnType
.DateTime4
:
855 case TdsColumnType
.Money4
:
856 case TdsColumnType
.SmallMoney
:
858 case TdsColumnType
.Float8
:
859 case TdsColumnType
.DateTime
:
860 case TdsColumnType
.Money
:
867 private int LookupDisplaySize (TdsColumnType columnType
)
869 switch (columnType
) {
870 case TdsColumnType
.Int1
:
872 case TdsColumnType
.Int2
:
874 case TdsColumnType
.Int4
:
876 case TdsColumnType
.Real
:
878 case TdsColumnType
.Float8
:
880 case TdsColumnType
.DateTime
:
882 case TdsColumnType
.DateTime4
:
884 case TdsColumnType
.Bit
:
886 case TdsColumnType
.Money
:
888 case TdsColumnType
.Money4
:
889 case TdsColumnType
.SmallMoney
:
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 ());
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
;
928 while (position
< len
) {
929 for (int j
= 0; j
< 3; j
+= 1)
930 values
[j
] = comm
.GetByte ();
933 if ((values
[2] & (byte) TdsColumnStatus
.Rename
) != 0) {
934 if (tdsVersion
== TdsVersion
.tds70
) {
935 columnNameLength
= comm
.GetByte ();
936 position
+= 2 * len
+ 1;
939 columnNameLength
= comm
.GetByte ();
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 ();
969 while (bytesRead
< totalLength
) {
970 int columnNameLength
= comm
.GetByte ();
971 string columnName
= comm
.GetString (columnNameLength
);
972 bytesRead
= bytesRead
+ 1 + columnNameLength
;
973 columnNames
.Add (columnName
);
978 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
979 protected void ProcessEndToken (TdsPacketSubType type
)
981 byte status
= Comm
.GetByte ();
983 byte op
= comm
.GetByte ();
986 int rowCount
= comm
.GetTdsInt ();
988 if (op
== (byte) 0xc1)
990 if (type
== TdsPacketSubType
.DoneInProc
)
993 moreResults
= ((status
& 0x01) != 0);
994 bool cancelled
= ((status
& 0x20) != 0);
997 case TdsPacketSubType
.DoneProc
:
999 goto case TdsPacketSubType
.Done
;
1001 case TdsPacketSubType
.Done
:
1003 recordsAffected
+= rowCount
;
1008 queryInProgress
= false;
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 ();
1022 case TdsEnvPacketSubType
.BlockSize
:
1024 cLen
= comm
.GetByte () & 0xff;
1025 blockSize
= comm
.GetString (cLen
);
1027 if (tdsVersion
== TdsVersion
.tds70
)
1028 comm
.Skip (len
- 2 - cLen
* 2);
1030 comm
.Skip (len
- 2 - cLen
);
1032 comm
.ResizeOutBuf (Int32
.Parse (blockSize
));
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);
1042 SetCharset (comm
.GetString (cLen
));
1043 comm
.Skip (len
- 2 - cLen
);
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
);
1055 comm
.Skip (len
- 1);
1060 protected void ProcessLoginAck ()
1062 GetSubPacketLength ();
1064 if (tdsVersion
== TdsVersion
.tds70
) {
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 ()));
1073 short nameLength
= comm
.GetByte ();
1074 databaseProductName
= comm
.GetString (nameLength
);
1076 databaseMajorVersion
= comm
.GetByte ();
1077 databaseProductVersion
= String
.Format ("{0}.{1}", databaseMajorVersion
, comm
.GetByte ());
1081 if (databaseProductName
.Length
> 1 && -1 != databaseProductName
.IndexOf ('\0')) {
1082 int last
= databaseProductName
.IndexOf ('\0');
1083 databaseProductName
= databaseProductName
.Substring (0, last
);
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
);
1102 protected void ProcessMessage (TdsPacketSubType subType
)
1104 GetSubPacketLength ();
1106 int number
= comm
.GetTdsInt ();
1107 byte state
= comm
.GetByte ();
1108 byte theClass
= comm
.GetByte ();
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
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 ();
1129 source
= String
.Empty
; // FIXME
1132 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass
, lineNumber
, message
, number
, procedure
, server
, source
, state
));
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);
1143 TdsColumnType colType
= (TdsColumnType
) comm
.GetByte ();
1144 object value = GetColumnValue (colType
, true);
1146 outputParameters
.Add (value);
1149 protected void ProcessDynamic ()
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 ();
1162 case TdsPacketSubType
.Dynamic2
:
1163 comm
.Skip (comm
.GetTdsInt ());
1165 case TdsPacketSubType
.AltName
:
1166 case TdsPacketSubType
.AltFormat
:
1167 case TdsPacketSubType
.Capability
:
1168 case TdsPacketSubType
.ParamFormat
:
1169 comm
.Skip (comm
.GetTdsShort ());
1171 case TdsPacketSubType
.Dynamic
:
1174 case TdsPacketSubType
.EnvironmentChange
:
1175 ProcessEnvironmentChange ();
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
);
1182 case TdsPacketSubType
.Param
:
1183 ProcessOutputParam ();
1185 case TdsPacketSubType
.LoginAck
:
1188 case TdsPacketSubType
.Authentication
: // TDS 7.0
1189 ProcessAuthentication ();
1191 case TdsPacketSubType
.ReturnStatus
:
1194 case TdsPacketSubType
.ProcId
:
1197 case TdsPacketSubType
.Done
:
1198 case TdsPacketSubType
.DoneProc
:
1199 case TdsPacketSubType
.DoneInProc
:
1200 ProcessEndToken (subType
);
1202 case TdsPacketSubType
.ColumnName
:
1204 ProcessColumnNames ();
1206 case TdsPacketSubType
.ColumnInfo
: // TDS 4.2
1207 case TdsPacketSubType
.ColumnMetadata
: // TDS 7.0
1208 case TdsPacketSubType
.RowFormat
: // TDS 5.0
1209 columns
= ProcessColumnInfo ();
1211 case TdsPacketSubType
.ColumnDetail
:
1212 ProcessColumnDetail ();
1214 case TdsPacketSubType
.TableName
:
1215 ProcessTableName ();
1217 case TdsPacketSubType
.ColumnOrder
:
1218 comm
.Skip (comm
.GetTdsShort ());
1220 case TdsPacketSubType
.Control
:
1221 comm
.Skip (comm
.GetTdsShort ());
1223 case TdsPacketSubType
.Row
:
1231 protected void ProcessTableName ()
1233 tableNames
= new ArrayList ();
1234 int totalLength
= comm
.GetTdsShort ();
1238 while (position
< totalLength
) {
1239 if (tdsVersion
== TdsVersion
.tds70
) {
1240 len
= comm
.GetTdsShort ();
1241 position
+= 2 * (len
+ 1);
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)
1256 if (this.charset
!= null && this.charset
!= charset
)
1259 if (charset
.StartsWith ("cp")) {
1260 encoder
= Encoding
.GetEncoding (Int32
.Parse (charset
.Substring (2)));
1261 this.charset
= charset
;
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