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,2005 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 originalDatabase = string.Empty;
58 string databaseProductName;
59 string databaseProductVersion;
60 int databaseMajorVersion;
65 bool connected = false;
72 TdsDataRow currentRow = null;
73 TdsDataColumnCollection columns;
76 ArrayList columnNames;
78 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
87 ArrayList outputParameters = new ArrayList ();
88 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
90 int recordsAffected = -1;
92 long StreamLength = 0;
94 int StreamColumnIndex = 0;
96 bool sequentialAccess = false;
97 bool isRowRead = false;
98 bool isResultRead = false;
99 bool LoadInProgress = false;
105 protected string Charset {
106 get { return charset; }
109 public bool DoneProc {
110 get { return doneProc; }
113 protected string Language {
114 get { return language; }
117 protected ArrayList ColumnNames {
118 get { return columnNames; }
121 public TdsDataRow ColumnValues {
122 get { return currentRow; }
125 internal TdsComm Comm {
129 public string Database {
130 get { return database; }
133 public string DataSource {
134 get { return dataSource; }
137 public bool IsConnected {
138 get { return connected; }
139 set { connected = value; }
142 public bool MoreResults {
143 get { return moreResults; }
144 set { moreResults = value; }
147 public int PacketSize {
148 get { return packetSize; }
151 public int RecordsAffected {
152 get { return recordsAffected; }
153 set { recordsAffected = value; }
156 public string ServerVersion {
157 get { return databaseProductVersion; }
160 public TdsDataColumnCollection Columns {
161 get { return columns; }
164 public TdsVersion TdsVersion {
165 get { return tdsVersion; }
168 public ArrayList OutputParameters {
169 get { return outputParameters; }
170 set { outputParameters = value; }
173 protected TdsMetaParameterCollection Parameters {
174 get { return parameters; }
175 set { parameters = value; }
178 public bool SequentialAccess {
179 get { return sequentialAccess; }
180 set { sequentialAccess = value; }
183 private void SkipRow ()
185 SkipToColumnIndex (Columns.Count);
188 StreamColumnIndex = 0;
190 LoadInProgress = false;
193 private void SkipToColumnIndex (int colIndex)
198 if (colIndex < StreamColumnIndex)
199 throw new Exception ("Cannot Skip to a colindex less than the curr index");
201 while (colIndex != StreamColumnIndex) {
202 TdsColumnType colType = (TdsColumnType)Columns[StreamColumnIndex]["ColumnType"];
203 if (!(colType == TdsColumnType.Image ||
204 colType == TdsColumnType.Text ||
205 colType == TdsColumnType.NText)) {
206 GetColumnValue (colType, false, StreamColumnIndex);
207 StreamColumnIndex ++;
211 Comm.Skip (StreamLength);
218 public object GetSequentialColumnValue (int colIndex)
220 if (colIndex < StreamColumnIndex)
221 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
226 if (colIndex != StreamColumnIndex)
227 SkipToColumnIndex (colIndex);
229 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
234 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
236 if (colIndex < StreamColumnIndex)
237 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
239 if (colIndex != StreamColumnIndex)
240 SkipToColumnIndex (colIndex);
243 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
245 if (buffer == null) {
248 return LoadData (fieldIndex, buffer, bufferIndex, size);
251 private void BeginLoad(TdsColumnType colType)
259 case TdsColumnType.Text :
260 case TdsColumnType.NText:
261 case TdsColumnType.Image:
262 if (Comm.GetByte () != 0) {
264 StreamLength = Comm.GetTdsInt ();
267 case TdsColumnType.BigVarChar:
268 case TdsColumnType.BigChar:
269 case TdsColumnType.BigBinary:
270 case TdsColumnType.BigVarBinary:
272 StreamLength = Comm.GetTdsShort ();
274 case TdsColumnType.VarChar :
275 case TdsColumnType.NVarChar :
276 case TdsColumnType.Char:
277 case TdsColumnType.NChar:
278 case TdsColumnType.Binary:
279 case TdsColumnType.VarBinary:
280 StreamLength = Comm.GetTdsShort ();
288 LoadInProgress = true;
291 private void EndLoad()
293 if (StreamLength > 0)
294 Comm.Skip (StreamLength);
298 LoadInProgress = false;
301 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
303 if (StreamLength <= 0)
306 if (fieldIndex < StreamIndex)
307 throw new InvalidOperationException ("field index less than stream pos");
309 if (fieldIndex >= (StreamLength + StreamIndex))
313 Comm.Skip ((int) (fieldIndex - StreamIndex));
314 StreamIndex += (fieldIndex - StreamIndex);
316 // Load the reqd amt of bytes
317 int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
318 byte[] arr = Comm.GetBytes (loadlen, true);
320 // update the index and stream length
321 StreamIndex += loadlen + (fieldIndex - StreamIndex);
322 StreamLength -= loadlen;
323 arr.CopyTo (buffer, bufferIndex);
328 #endregion // Properties
332 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
333 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
339 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
341 this.tdsVersion = tdsVersion;
342 this.packetSize = packetSize;
343 this.dataSource = dataSource;
345 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
348 #endregion // Constructors
350 #region Public Methods
352 internal protected void InitExec ()
358 outputParameters.Clear ();
361 public void Cancel ()
363 if (queryInProgress) {
364 if (cancelsRequested == cancelsProcessed) {
365 comm.StartPacket (TdsPacketType.Cancel);
367 cancelsRequested += 1;
372 public abstract bool Connect (TdsConnectionParameters connectionParameters);
374 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
376 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
377 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
380 public void Disconnect ()
382 comm.StartPacket (TdsPacketType.Logoff);
383 comm.Append ((byte) 0);
389 public virtual bool Reset ()
391 database = originalDatabase;
395 protected virtual bool IsValidRowCount (byte status, byte op)
397 return ((status & (0x10)) != 0) ;
400 public void Execute (string sql)
402 Execute (sql, null, 0, false);
405 public void ExecProc (string sql)
407 ExecProc (sql, null, 0, false);
410 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
412 ExecuteQuery (sql, timeout, wantResults);
415 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
417 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
420 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
422 throw new NotSupportedException ();
425 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
430 CheckForData (timeout);
435 internal void ExecBulkCopy (int timeout, bool wantResults)
440 CheckForData (timeout);
445 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
449 Comm.StartPacket (TdsPacketType.Query);
453 CheckForData (timeout);
458 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
459 int timeout, bool wantResults)
461 Comm.StartPacket (TdsPacketType.DBRPC);
463 byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
464 byte rpcNameLength = (byte) rpcNameBytes.Length;
465 ushort mask = 0x0000;
466 ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
469 Comm.Append (packetLength);
470 Comm.Append (rpcNameLength);
471 Comm.Append (rpcNameBytes);
475 CheckForData (timeout);
480 public bool NextResult ()
482 if (SequentialAccess) {
484 while (NextRow ()) {}
486 isResultRead = false;
492 TdsPacketSubType subType;
495 bool outputParams = false;
498 subType = ProcessSubPacket ();
505 case TdsPacketSubType.ColumnInfo:
506 case TdsPacketSubType.ColumnMetadata:
507 case TdsPacketSubType.RowFormat:
508 byte peek = Comm.Peek ();
509 done = (peek != (byte) TdsPacketSubType.TableName);
510 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
516 case TdsPacketSubType.TableName:
519 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
522 case TdsPacketSubType.ColumnDetail:
534 public bool NextRow ()
536 if (SequentialAccess) {
543 TdsPacketSubType subType;
548 subType = ProcessSubPacket ();
550 case TdsPacketSubType.Row:
554 case TdsPacketSubType.Done:
555 case TdsPacketSubType.DoneProc:
556 case TdsPacketSubType.DoneInProc:
566 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
568 throw new NotSupportedException ();
571 public void SkipToEnd ()
573 while (NextResult ()) { /* DO NOTHING */ }
576 public virtual void Unprepare (string statementId)
578 throw new NotSupportedException ();
581 #endregion // Public Methods
583 #region // Private Methods
585 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
586 protected void CheckForData (int timeout)
588 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
590 throw CreateTimeoutException (dataSource, "CheckForData()");
594 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
596 return new TdsInternalInfoMessageEventArgs (errors);
599 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
601 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
604 private object GetColumnValue (TdsColumnType colType, bool outParam)
606 return GetColumnValue (colType, outParam, -1);
609 private object GetColumnValue (TdsColumnType colType, bool outParam, int ordinal)
612 object element = null;
615 case TdsColumnType.IntN :
618 element = GetIntValue (colType);
620 case TdsColumnType.Int1 :
621 case TdsColumnType.Int2 :
622 case TdsColumnType.Int4 :
623 element = GetIntValue (colType);
625 case TdsColumnType.Image :
628 element = GetImageValue ();
630 case TdsColumnType.Text :
633 element = GetTextValue (false);
635 case TdsColumnType.NText :
638 element = GetTextValue (true);
640 case TdsColumnType.Char :
641 case TdsColumnType.VarChar :
644 element = GetStringValue (false, false);
646 case TdsColumnType.BigVarBinary :
649 len = comm.GetTdsShort ();
650 element = comm.GetBytes (len, true);
653 case TdsColumnType.BigBinary :
656 len = comm.GetTdsShort ();
657 element = comm.GetBytes (len, true);
660 case TdsColumnType.BigBinary :
663 element = GetBinaryValue ();
665 case TdsColumnType.BigChar :
666 case TdsColumnType.BigVarChar :
669 element = GetStringValue (false, false);
671 case TdsColumnType.NChar :
672 case TdsColumnType.BigNVarChar :
675 element = GetStringValue (true, false);
677 case TdsColumnType.NVarChar :
680 element = GetStringValue (true, false);
682 case TdsColumnType.Real :
683 case TdsColumnType.Float8 :
684 element = GetFloatValue (colType);
686 case TdsColumnType.FloatN :
689 element = GetFloatValue (colType);
691 case TdsColumnType.SmallMoney :
692 case TdsColumnType.Money :
693 element = GetMoneyValue (colType);
695 case TdsColumnType.MoneyN :
698 element = GetMoneyValue (colType);
700 case TdsColumnType.Numeric :
701 case TdsColumnType.Decimal :
706 precision = comm.GetByte ();
707 scale = comm.GetByte ();
710 precision = (byte) columns[ordinal]["NumericPrecision"];
711 scale = (byte) columns[ordinal]["NumericScale"];
714 element = GetDecimalValue (precision, scale);
716 case TdsColumnType.DateTimeN :
719 element = GetDateTimeValue (colType);
721 case TdsColumnType.DateTime4 :
722 case TdsColumnType.DateTime :
723 element = GetDateTimeValue (colType);
725 case TdsColumnType.VarBinary :
726 case TdsColumnType.Binary :
729 element = GetBinaryValue ();
731 case TdsColumnType.BitN :
734 if (comm.GetByte () == 0)
735 element = DBNull.Value;
737 element = (comm.GetByte() != 0);
739 case TdsColumnType.Bit :
740 int columnSize = comm.GetByte ();
741 element = (columnSize != 0);
743 case TdsColumnType.UniqueIdentifier :
744 if (comm.Peek () != 16) { // If it's null, then what to do?
745 /*byte swallowed =*/ comm.GetByte();
746 element = DBNull.Value;
752 len = comm.GetByte () & 0xff;
754 byte[] guidBytes = comm.GetBytes (len, true);
755 if (!BitConverter.IsLittleEndian) {
756 byte[] swappedguidBytes = new byte[len];
757 for (int i = 0; i < 4; i++)
758 swappedguidBytes[i] = guidBytes[4-i-1];
759 for (int i = 4; i < 6; i++)
760 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
761 for (int i = 6; i < 8; i++)
762 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
763 for (int i = 8; i < 16; i++)
764 swappedguidBytes[i] = guidBytes[i];
765 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
767 element = new Guid (guidBytes);
776 private object GetBinaryValue ()
779 object result = DBNull.Value;
781 if (tdsVersion == TdsVersion.tds70) {
782 len = comm.GetTdsShort ();
783 if (len != 0xffff && len > 0)
784 result = comm.GetBytes (len, true);
786 len = (comm.GetByte () & 0xff);
788 result = comm.GetBytes (len, true);
794 private object GetDateTimeValue (TdsColumnType type)
800 case TdsColumnType.DateTime4:
803 case TdsColumnType.DateTime:
806 case TdsColumnType.DateTimeN:
807 byte tmp = comm.Peek ();
808 if (tmp != 0 && tmp != 4 && tmp != 8)
810 len = comm.GetByte ();
814 DateTime epoch = new DateTime (1900, 1, 1);
818 result = epoch.AddDays (comm.GetTdsInt ());
819 int seconds = comm.GetTdsInt ();
820 long millis = ((((long) seconds) % 300L) * 1000L) / 300L;
821 if (seconds != 0 || millis != 0) {
822 result = ((DateTime) result).AddSeconds (seconds / 300);
823 result = ((DateTime) result).AddMilliseconds (millis);
827 // MSDN says small datetime is stored in 2 bytes as no of days
828 // *after* 1/1/1900. so, cast to unsigned short
829 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
830 short minutes = comm.GetTdsShort ();
832 result = ((DateTime) result).AddMinutes ((int) minutes);
835 result = DBNull.Value;
842 private object GetDecimalValue (byte precision, byte scale) {
843 if (tdsVersion < TdsVersion.tds70)
844 return GetDecimalValueTds50 (precision, scale);
846 return GetDecimalValueTds70 (precision, scale);
849 private object GetDecimalValueTds70 (byte precision, byte scale) {
850 int[] bits = new int[4] {0,0,0,0};
852 int len = (comm.GetByte() & 0xff) - 1;
856 bool positive = (comm.GetByte () == 1);
858 throw new OverflowException ();
860 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
861 bits[index] = comm.GetTdsInt ();
864 return new TdsBigDecimal (precision, scale, !positive, bits);
866 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
869 private object GetDecimalValueTds50 (byte precision, byte scale) {
870 int[] bits = new int[4] {0,0,0,0};
872 int len = (comm.GetByte() & 0xff);
876 byte[] dec_bytes=comm.GetBytes(len,false);
878 byte[] easy=new byte[4];
880 bool positive = dec_bytes[0]==1;
883 throw new OverflowException ();
885 for (int i = 1, index = 0; i < len && i < 16; i +=
887 for(int j=0; j<4; j++)
889 easy[j]=dec_bytes[len-
893 if(!BitConverter.IsLittleEndian)
894 easy=comm.Swap(easy);
895 bits[index] = BitConverter.ToInt32(easy,0);
898 return new TdsBigDecimal (precision,
899 scale, positive, bits);
901 return new Decimal(bits[0], bits[1], bits
902 [2], positive, scale);
906 private object GetFloatValue (TdsColumnType columnType)
910 switch (columnType) {
911 case TdsColumnType.Real:
914 case TdsColumnType.Float8:
917 case TdsColumnType.FloatN:
918 columnSize = comm.GetByte ();
922 switch (columnSize) {
924 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
926 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
932 private object GetImageValue ()
934 byte hasValue = comm.GetByte ();
940 int len = comm.GetTdsInt ();
945 return (comm.GetBytes (len, true));
948 private object GetIntValue (TdsColumnType type)
953 case TdsColumnType.IntN :
954 len = comm.GetByte ();
956 case TdsColumnType.Int4 :
959 case TdsColumnType.Int2 :
962 case TdsColumnType.Int1 :
971 return (comm.GetTdsInt ());
973 return (comm.GetTdsShort ());
975 return (comm.GetByte ());
981 private object GetMoneyValue (TdsColumnType type)
986 case TdsColumnType.SmallMoney :
987 case TdsColumnType.Money4 :
990 case TdsColumnType.Money :
993 case TdsColumnType.MoneyN :
994 len = comm.GetByte ();
1002 return new Decimal (Comm.GetTdsInt (), 0, 0, false, 4);
1004 int hi = Comm.GetTdsInt ();
1005 int lo = Comm.GetTdsInt ();
1006 return new Decimal (lo, hi, 0, false, 4);
1008 return DBNull.Value;
1012 private object GetStringValue (bool wideChars, bool outputParam)
1014 bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam);
1015 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1017 if (tdsVersion < TdsVersion.tds70 && len == 0)
1018 return DBNull.Value;
1019 else if (len >= 0) {
1022 result = comm.GetString (len / 2);
1024 result = comm.GetString (len, false);
1025 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1030 return DBNull.Value;
1033 protected int GetSubPacketLength ()
1035 return comm.GetTdsShort ();
1038 private object GetTextValue (bool wideChars)
1040 string result = null;
1041 byte hasValue = comm.GetByte ();
1044 return DBNull.Value;
1046 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1049 int len = comm.GetTdsInt ();
1051 //if the len is 0 , then the string can be a '' string
1052 // this method is called only for Text and NText. Hence will
1053 // return a empty string
1058 result = comm.GetString (len / 2);
1060 result = comm.GetString (len, false);
1063 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1069 internal bool IsBlobType (TdsColumnType columnType)
1071 return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1074 internal bool IsLargeType (TdsColumnType columnType)
1076 return ((byte) columnType > 128);
1079 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1081 switch (columnType) {
1082 case TdsColumnType.Int1 :
1083 case TdsColumnType.Int2 :
1084 case TdsColumnType.Int4 :
1085 case TdsColumnType.Float8 :
1086 case TdsColumnType.DateTime :
1087 case TdsColumnType.Bit :
1088 case TdsColumnType.Money :
1089 case TdsColumnType.Money4 :
1090 case TdsColumnType.SmallMoney :
1091 case TdsColumnType.Real :
1092 case TdsColumnType.DateTime4 :
1094 case TdsColumnType.Decimal:
1095 case TdsColumnType.Numeric:
1103 protected void LoadRow ()
1105 if (SequentialAccess) {
1109 isResultRead = true;
1113 currentRow = new TdsDataRow ();
1116 foreach (TdsDataColumn column in columns) {
1117 object o = GetColumnValue ((TdsColumnType) column["ColumnType"], false, i);
1120 outputParameters.Add (o);
1122 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1123 currentRow.BigDecimalIndex = i;
1128 internal static int LookupBufferSize (TdsColumnType columnType)
1130 switch (columnType) {
1131 case TdsColumnType.Int1 :
1132 case TdsColumnType.Bit :
1134 case TdsColumnType.Int2 :
1136 case TdsColumnType.Int4 :
1137 case TdsColumnType.Real :
1138 case TdsColumnType.DateTime4 :
1139 case TdsColumnType.Money4 :
1140 case TdsColumnType.SmallMoney :
1142 case TdsColumnType.Float8 :
1143 case TdsColumnType.DateTime :
1144 case TdsColumnType.Money :
1151 private int LookupDisplaySize (TdsColumnType columnType)
1153 switch (columnType) {
1154 case TdsColumnType.Int1 :
1156 case TdsColumnType.Int2 :
1158 case TdsColumnType.Int4 :
1160 case TdsColumnType.Real :
1162 case TdsColumnType.Float8 :
1164 case TdsColumnType.DateTime :
1166 case TdsColumnType.DateTime4 :
1168 case TdsColumnType.Bit :
1170 case TdsColumnType.Money :
1172 case TdsColumnType.Money4 :
1173 case TdsColumnType.SmallMoney :
1180 protected internal int ProcessAuthentication ()
1182 int pdu_size = Comm.GetTdsShort ();
1183 byte[] msg2 = Comm.GetBytes (pdu_size, true);
1185 Type2Message t2 = new Type2Message (msg2);
1186 // 0x0001 Negotiate Unicode
1187 // 0x0200 Negotiate NTLM
1188 // 0x8000 Negotiate Always Sign
1190 Type3Message t3 = new Type3Message ();
1191 t3.Challenge = t2.Nonce;
1193 t3.Domain = this.connectionParms.DefaultDomain;
1194 t3.Host = this.connectionParms.Hostname;
1195 t3.Username = this.connectionParms.User;
1196 t3.Password = this.connectionParms.Password;
1198 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1199 Comm.Append (t3.GetBytes ());
1201 return 1; // TDS_SUCCEED
1204 protected void ProcessColumnDetail ()
1206 int len = GetSubPacketLength ();
1207 byte[] values = new byte[3];
1208 int columnNameLength;
1209 string baseColumnName = String.Empty;
1212 while (position < len) {
1213 for (int j = 0; j < 3; j += 1)
1214 values[j] = comm.GetByte ();
1217 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1219 if (tdsVersion == TdsVersion.tds70) {
1220 columnNameLength = comm.GetByte ();
1221 position += 2 * columnNameLength + 1;
1224 columnNameLength = comm.GetByte ();
1225 position += columnNameLength + 1;
1227 baseColumnName = comm.GetString (columnNameLength);
1230 byte index = (byte) (values[0] - (byte) 1);
1231 byte tableIndex = (byte) (values[1] - (byte) 1);
1232 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1234 columns [index]["IsExpression"] = isExpression;
1235 columns [index]["IsKey"] = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1236 columns [index]["IsHidden"] = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1237 columns [index]["IsAliased"] = isAlias;
1239 columns [index]["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1240 columns [index]["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1244 protected abstract TdsDataColumnCollection ProcessColumnInfo ();
1246 protected void ProcessColumnNames ()
1248 columnNames = new ArrayList ();
1250 int totalLength = comm.GetTdsShort ();
1254 while (bytesRead < totalLength) {
1255 int columnNameLength = comm.GetByte ();
1256 string columnName = comm.GetString (columnNameLength);
1257 bytesRead = bytesRead + 1 + columnNameLength;
1258 columnNames.Add (columnName);
1263 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1264 protected void ProcessEndToken (TdsPacketSubType type)
1266 byte status = Comm.GetByte ();
1268 byte op = comm.GetByte ();
1271 int rowCount = comm.GetTdsInt ();
1272 bool validRowCount = IsValidRowCount (status,op);
1273 moreResults = ((status & 0x01) != 0);
1274 bool cancelled = ((status & 0x20) != 0);
1277 case TdsPacketSubType.DoneProc:
1279 goto case TdsPacketSubType.Done;
1280 case TdsPacketSubType.Done:
1281 case TdsPacketSubType.DoneInProc:
1282 if (validRowCount) {
1283 if (recordsAffected == -1)
1284 recordsAffected = rowCount;
1286 recordsAffected += rowCount;
1292 queryInProgress = false;
1294 cancelsProcessed += 1;
1295 if (messages.Count > 0 && !moreResults)
1296 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1299 protected void ProcessEnvironmentChange ()
1301 int len = GetSubPacketLength ();
1302 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1306 case TdsEnvPacketSubType.BlockSize :
1308 cLen = comm.GetByte ();
1309 blockSize = comm.GetString (cLen);
1311 if (tdsVersion == TdsVersion.tds70)
1312 comm.Skip (len - 2 - cLen * 2);
1314 comm.Skip (len - 2 - cLen);
1316 packetSize = Int32.Parse (blockSize);
1317 comm.ResizeOutBuf (packetSize);
1319 case TdsEnvPacketSubType.CharSet :
1320 cLen = comm.GetByte ();
1321 if (tdsVersion == TdsVersion.tds70) {
1322 SetCharset (comm.GetString (cLen));
1323 comm.Skip (len - 2 - cLen * 2);
1326 SetCharset (comm.GetString (cLen));
1327 comm.Skip (len - 2 - cLen);
1331 case TdsEnvPacketSubType.Database :
1332 cLen = comm.GetByte ();
1333 string newDB = comm.GetString (cLen);
1334 cLen = comm.GetByte () & 0xff;
1335 comm.GetString (cLen);
1336 if (originalDatabase == string.Empty)
1337 originalDatabase = newDB;
1341 comm.Skip (len - 1);
1346 protected void ProcessLoginAck ()
1348 GetSubPacketLength ();
1350 if (tdsVersion == TdsVersion.tds70) {
1352 int nameLength = comm.GetByte ();
1353 databaseProductName = comm.GetString (nameLength);
1354 databaseMajorVersion = comm.GetByte ();
1355 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1356 comm.GetByte ().ToString("00"),
1357 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1361 short nameLength = comm.GetByte ();
1362 databaseProductName = comm.GetString (nameLength);
1364 databaseMajorVersion = comm.GetByte ();
1365 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1369 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1370 int last = databaseProductName.IndexOf ('\0');
1371 databaseProductName = databaseProductName.Substring (0, last);
1377 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1379 if (TdsErrorMessage != null)
1380 TdsErrorMessage (this, e);
1383 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1385 if (TdsInfoMessage != null)
1386 TdsInfoMessage (this, e);
1390 protected void ProcessMessage (TdsPacketSubType subType)
1392 GetSubPacketLength ();
1394 int number = comm.GetTdsInt ();
1395 byte state = comm.GetByte ();
1396 byte theClass = comm.GetByte ();
1402 bool isError = false;
1404 if (subType == TdsPacketSubType.EED) {
1405 isError = (theClass > 10);
1406 comm.Skip (comm.GetByte ()); // SQL State
1407 comm.Skip (1); // Status
1408 comm.Skip (2); // TranState
1410 isError = (subType == TdsPacketSubType.Error);
1412 message = comm.GetString (comm.GetTdsShort ());
1413 server = comm.GetString (comm.GetByte ());
1414 procedure = comm.GetString (comm.GetByte ());
1415 lineNumber = comm.GetByte ();
1417 source = String.Empty; // FIXME
1420 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1422 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1425 protected void ProcessOutputParam ()
1427 GetSubPacketLength ();
1428 /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1431 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1432 object value = GetColumnValue (colType, true);
1433 outputParameters.Add (value);
1436 protected void ProcessDynamic ()
1439 /*byte type =*/ Comm.GetByte ();
1440 /*byte status =*/ Comm.GetByte ();
1441 /*string id =*/ Comm.GetString (Comm.GetByte ());
1444 protected virtual TdsPacketSubType ProcessSubPacket ()
1446 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1449 case TdsPacketSubType.Dynamic2:
1450 comm.Skip (comm.GetTdsInt ());
1452 case TdsPacketSubType.AltName:
1453 case TdsPacketSubType.AltFormat:
1454 case TdsPacketSubType.Capability:
1455 case TdsPacketSubType.ParamFormat:
1456 comm.Skip (comm.GetTdsShort ());
1458 case TdsPacketSubType.Dynamic:
1461 case TdsPacketSubType.EnvironmentChange:
1462 ProcessEnvironmentChange ();
1464 case TdsPacketSubType.Info: // TDS 4.2/7.0
1465 case TdsPacketSubType.EED: // TDS 5.0
1466 case TdsPacketSubType.Error: // TDS 4.2/7.0
1467 ProcessMessage (subType);
1469 case TdsPacketSubType.Param:
1470 ProcessOutputParam ();
1472 case TdsPacketSubType.LoginAck:
1475 case TdsPacketSubType.Authentication: // TDS 7.0
1476 ProcessAuthentication ();
1478 case TdsPacketSubType.ReturnStatus :
1479 ProcessReturnStatus ();
1481 case TdsPacketSubType.ProcId:
1484 case TdsPacketSubType.Done:
1485 case TdsPacketSubType.DoneProc:
1486 case TdsPacketSubType.DoneInProc:
1487 ProcessEndToken (subType);
1489 case TdsPacketSubType.ColumnName:
1491 ProcessColumnNames ();
1493 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1494 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1495 case TdsPacketSubType.RowFormat: // TDS 5.0
1496 columns = ProcessColumnInfo ();
1498 case TdsPacketSubType.ColumnDetail:
1499 ProcessColumnDetail ();
1501 case TdsPacketSubType.TableName:
1502 ProcessTableName ();
1504 case TdsPacketSubType.ColumnOrder:
1505 comm.Skip (comm.GetTdsShort ());
1507 case TdsPacketSubType.Control:
1508 comm.Skip (comm.GetTdsShort ());
1510 case TdsPacketSubType.Row:
1518 protected void ProcessTableName ()
1520 tableNames = new ArrayList ();
1521 int totalLength = comm.GetTdsShort ();
1525 while (position < totalLength) {
1526 if (tdsVersion == TdsVersion.tds70) {
1527 len = comm.GetTdsShort ();
1528 position += 2 * (len + 1);
1531 len = comm.GetByte ();
1532 position += len + 1;
1534 tableNames.Add (comm.GetString (len));
1538 protected void SetCharset (string charset)
1540 if (charset == null || charset.Length > 30)
1543 if (this.charset != null && this.charset == charset)
1546 if (charset.StartsWith ("cp")) {
1547 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1548 this.charset = charset;
1551 encoder = Encoding.GetEncoding ("iso-8859-1");
1552 this.charset = "iso_1";
1554 comm.Encoder = encoder;
1557 protected void SetLanguage (string language)
1559 if (language == null || language.Length > 30)
1560 language = "us_english";
1562 this.language = language;
1565 protected virtual void ProcessReturnStatus ()
1570 #endregion // Private Methods
1573 #region asynchronous methods
1574 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1575 AsyncCallback callback, object state)
1579 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1580 ar.TdsAsyncState.WantResults = wantResults;
1582 Comm.StartPacket (TdsPacketType.Query);
1586 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1591 protected void EndExecuteQueryInternal (IAsyncResult ar)
1593 if (!ar.IsCompleted)
1594 ar.AsyncWaitHandle.WaitOne ();
1595 TdsAsyncResult result = (TdsAsyncResult) ar;
1596 if (result.IsCompletedWithException)
1597 throw result.Exception;
1600 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1602 TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1603 TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1606 Comm.EndReadPacket (ar);
1607 if (!tdsState.WantResults)
1609 } catch (Exception e) {
1610 result.MarkComplete (e);
1613 result.MarkComplete ();
1617 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1618 TdsMetaParameterCollection parameters,
1619 AsyncCallback callback,
1622 // abstract, kept to be backward compatiable.
1623 throw new NotImplementedException ("should not be called!");
1626 public virtual void EndExecuteNonQuery (IAsyncResult ar)
1629 throw new NotImplementedException ("should not be called!");
1632 public virtual IAsyncResult BeginExecuteQuery (string sql,
1633 TdsMetaParameterCollection parameters,
1634 AsyncCallback callback,
1637 // abstract, kept to be backward compatiable.
1638 throw new NotImplementedException ("should not be called!");
1641 public virtual void EndExecuteQuery (IAsyncResult ar)
1644 throw new NotImplementedException ("should not be called!");
1647 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
1651 TdsMetaParameterCollection parameters,
1652 AsyncCallback callback,
1655 throw new NotImplementedException ("should not be called!");
1658 public virtual void EndExecuteProcedure (IAsyncResult ar)
1661 throw new NotImplementedException ("should not be called!");
1664 public void WaitFor (IAsyncResult ar)
1666 if (! ar.IsCompleted)
1667 ar.AsyncWaitHandle.WaitOne ();
1670 public void CheckAndThrowException (IAsyncResult ar)
1672 TdsAsyncResult result = (TdsAsyncResult) ar;
1673 if (result.IsCompleted && result.IsCompletedWithException)
1674 throw result.Exception;
1677 #endregion // asynchronous methods