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;
38 using System.Collections;
39 using System.ComponentModel;
40 using System.Diagnostics;
41 using System.Net.Sockets;
42 using System.Globalization;
45 namespace Mono.Data.Tds.Protocol
47 public abstract class Tds
52 TdsVersion tdsVersion;
54 protected internal TdsConnectionParameters connectionParms;
55 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
60 string originalDatabase = string.Empty;
61 string databaseProductName;
62 string databaseProductVersion;
63 int databaseMajorVersion;
64 CultureInfo locale = CultureInfo.InvariantCulture;
77 TdsDataRow currentRow;
78 TdsDataColumnCollection columns;
81 ArrayList columnNames;
83 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
92 ArrayList outputParameters = new ArrayList ();
93 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
95 int recordsAffected = -1;
99 int StreamColumnIndex;
101 bool sequentialAccess;
106 internal int poolStatus = 0;
112 protected string Charset {
113 get { return charset; }
116 protected CultureInfo Locale {
117 get { return locale; }
120 public bool DoneProc {
121 get { return doneProc; }
124 protected string Language {
125 get { return language; }
128 protected ArrayList ColumnNames {
129 get { return columnNames; }
132 public TdsDataRow ColumnValues {
133 get { return currentRow; }
136 internal TdsComm Comm {
140 public string Database {
141 get { return database; }
144 public string DataSource {
145 get { return dataSource; }
148 public bool IsConnected {
149 get { return connected; }
150 set { connected = value; }
153 public bool Pooling {
154 get { return pooling; }
155 set { pooling = value; }
158 public bool MoreResults {
159 get { return moreResults; }
160 set { moreResults = value; }
163 public int PacketSize {
164 get { return packetSize; }
167 public int RecordsAffected {
168 get { return recordsAffected; }
169 set { recordsAffected = value; }
172 public string ServerVersion {
173 get { return databaseProductVersion; }
176 public TdsDataColumnCollection Columns {
177 get { return columns; }
180 public TdsVersion TdsVersion {
181 get { return tdsVersion; }
184 public ArrayList OutputParameters {
185 get { return outputParameters; }
186 set { outputParameters = value; }
189 protected TdsMetaParameterCollection Parameters {
190 get { return parameters; }
191 set { parameters = value; }
194 public bool SequentialAccess {
195 get { return sequentialAccess; }
196 set { sequentialAccess = value; }
199 private void SkipRow ()
201 SkipToColumnIndex (Columns.Count);
204 StreamColumnIndex = 0;
206 LoadInProgress = false;
209 private void SkipToColumnIndex (int colIndex)
214 if (colIndex < StreamColumnIndex)
215 throw new Exception ("Cannot Skip to a colindex less than the curr index");
217 while (colIndex != StreamColumnIndex) {
219 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
221 throw new Exception ("Column type unset.");
223 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
225 if (!(colType == TdsColumnType.Image ||
226 colType == TdsColumnType.Text ||
227 colType == TdsColumnType.NText)) {
228 GetColumnValue (colType, false, StreamColumnIndex);
229 StreamColumnIndex ++;
233 Comm.Skip (StreamLength);
240 public object GetSequentialColumnValue (int colIndex)
242 if (colIndex < StreamColumnIndex)
243 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
248 if (colIndex != StreamColumnIndex)
249 SkipToColumnIndex (colIndex);
252 object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
254 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
260 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
262 if (colIndex < StreamColumnIndex)
263 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
265 if (colIndex != StreamColumnIndex)
266 SkipToColumnIndex (colIndex);
268 if (!LoadInProgress) {
270 BeginLoad (Columns[colIndex].ColumnType);
272 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
276 if (buffer == null) {
279 return LoadData (fieldIndex, buffer, bufferIndex, size);
280 } catch (IOException ex) {
282 throw new TdsInternalException ("Server closed the connection.", ex);
286 private void BeginLoad (
288 TdsColumnType? colType
290 TdsColumnType colType
301 throw new ArgumentNullException ("colType");
304 case TdsColumnType.Text :
305 case TdsColumnType.NText:
306 case TdsColumnType.Image:
307 if (Comm.GetByte () != 0) {
309 StreamLength = Comm.GetTdsInt ();
312 case TdsColumnType.BigVarChar:
313 case TdsColumnType.BigChar:
314 case TdsColumnType.BigBinary:
315 case TdsColumnType.BigVarBinary:
317 StreamLength = Comm.GetTdsShort ();
319 case TdsColumnType.VarChar :
320 case TdsColumnType.NVarChar :
321 case TdsColumnType.Char:
322 case TdsColumnType.NChar:
323 case TdsColumnType.Binary:
324 case TdsColumnType.VarBinary:
325 StreamLength = Comm.GetTdsShort ();
333 LoadInProgress = true;
336 private void EndLoad()
338 if (StreamLength > 0)
339 Comm.Skip (StreamLength);
343 LoadInProgress = false;
346 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
348 if (StreamLength <= 0)
351 if (fieldIndex < StreamIndex)
352 throw new InvalidOperationException ("field index less than stream pos");
354 if (fieldIndex >= (StreamLength + StreamIndex))
358 Comm.Skip ((int) (fieldIndex - StreamIndex));
359 StreamIndex += (fieldIndex - StreamIndex);
361 // Load the reqd amt of bytes
362 int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
363 byte[] arr = Comm.GetBytes (loadlen, true);
365 // update the index and stream length
366 StreamIndex += loadlen + (fieldIndex - StreamIndex);
367 StreamLength -= loadlen;
368 arr.CopyTo (buffer, bufferIndex);
373 #endregion // Properties
377 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
378 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
384 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
386 this.tdsVersion = tdsVersion;
387 this.packetSize = packetSize;
388 this.dataSource = dataSource;
390 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
393 #endregion // Constructors
395 #region Public Methods
397 internal protected void InitExec ()
403 outputParameters.Clear ();
406 public void Cancel ()
408 if (queryInProgress) {
409 if (cancelsRequested == cancelsProcessed) {
410 comm.StartPacket (TdsPacketType.Cancel);
413 } catch (IOException ex) {
415 throw new TdsInternalException ("Server closed the connection.", ex);
417 cancelsRequested += 1;
422 public abstract bool Connect (TdsConnectionParameters connectionParameters);
424 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
426 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
427 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
430 public void Disconnect ()
432 comm.StartPacket (TdsPacketType.Logoff);
433 comm.Append ((byte) 0);
439 public virtual bool Reset ()
441 database = originalDatabase;
445 protected virtual bool IsValidRowCount (byte status, byte op)
447 return ((status & (0x10)) != 0) ;
450 public void Execute (string sql)
452 Execute (sql, null, 0, false);
455 public void ExecProc (string sql)
457 ExecProc (sql, null, 0, false);
460 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
462 ExecuteQuery (sql, timeout, wantResults);
465 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
467 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
470 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
472 throw new NotSupportedException ();
475 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
480 CheckForData (timeout);
483 } catch (IOException ex) {
485 throw new TdsInternalException ("Server closed the connection.", ex);
489 internal void ExecBulkCopy (int timeout, bool wantResults)
494 CheckForData (timeout);
497 } catch (IOException ex) {
499 throw new TdsInternalException ("Server closed the connection.", ex);
503 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
507 Comm.StartPacket (TdsPacketType.Query);
511 CheckForData (timeout);
514 } catch (IOException ex) {
516 throw new TdsInternalException ("Server closed the connection.", ex);
520 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
521 int timeout, bool wantResults)
523 Comm.StartPacket (TdsPacketType.DBRPC);
525 byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
526 byte rpcNameLength = (byte) rpcNameBytes.Length;
527 ushort mask = 0x0000;
528 ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
531 Comm.Append (packetLength);
532 Comm.Append (rpcNameLength);
533 Comm.Append (rpcNameBytes);
538 CheckForData (timeout);
541 } catch (IOException ex) {
543 throw new TdsInternalException ("Server closed the connection.", ex);
547 public bool NextResult ()
549 if (SequentialAccess) {
551 while (NextRow ()) {}
553 isResultRead = false;
559 TdsPacketSubType subType;
562 bool outputParams = false;
565 subType = ProcessSubPacket ();
571 case TdsPacketSubType.ColumnInfo:
572 case TdsPacketSubType.ColumnMetadata:
573 case TdsPacketSubType.RowFormat:
574 byte peek = Comm.Peek ();
575 done = (peek != (byte) TdsPacketSubType.TableName);
576 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
581 case TdsPacketSubType.TableName:
584 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
586 case TdsPacketSubType.ColumnDetail:
598 public bool NextRow ()
600 if (SequentialAccess) {
607 TdsPacketSubType subType;
612 subType = ProcessSubPacket ();
614 case TdsPacketSubType.Row:
618 case TdsPacketSubType.Done:
619 case TdsPacketSubType.DoneProc:
620 case TdsPacketSubType.DoneInProc:
630 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
632 throw new NotSupportedException ();
635 public void SkipToEnd ()
638 while (NextResult ()) { /* DO NOTHING */ }
639 } catch (IOException ex) {
641 throw new TdsInternalException ("Server closed the connection.", ex);
645 public virtual void Unprepare (string statementId)
647 throw new NotSupportedException ();
650 #endregion // Public Methods
652 #region // Private Methods
654 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
655 protected void CheckForData (int timeout)
657 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
659 throw CreateTimeoutException (dataSource, "CheckForData()");
663 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
665 return new TdsInternalInfoMessageEventArgs (errors);
668 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
670 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
673 private object GetColumnValue (
675 TdsColumnType? colType,
677 TdsColumnType colType,
681 return GetColumnValue (colType, outParam, -1);
684 private object GetColumnValue (
686 TdsColumnType? colType,
688 TdsColumnType colType,
690 bool outParam, int ordinal)
693 object element = null;
697 throw new ArgumentNullException ("colType");
700 case TdsColumnType.IntN :
703 element = GetIntValue (colType);
705 case TdsColumnType.Int1 :
706 case TdsColumnType.Int2 :
707 case TdsColumnType.Int4 :
708 element = GetIntValue (colType);
710 case TdsColumnType.Image :
713 element = GetImageValue ();
715 case TdsColumnType.Text :
718 element = GetTextValue (false);
720 case TdsColumnType.NText :
723 element = GetTextValue (true);
725 case TdsColumnType.Char :
726 case TdsColumnType.VarChar :
729 element = GetStringValue (false, false);
731 case TdsColumnType.BigVarBinary :
734 len = comm.GetTdsShort ();
735 element = comm.GetBytes (len, true);
738 case TdsColumnType.BigBinary :
741 len = comm.GetTdsShort ();
742 element = comm.GetBytes (len, true);
745 case TdsColumnType.BigBinary :
748 element = GetBinaryValue ();
750 case TdsColumnType.BigChar :
751 case TdsColumnType.BigVarChar :
754 element = GetStringValue (false, false);
756 case TdsColumnType.NChar :
757 case TdsColumnType.BigNVarChar :
760 element = GetStringValue (true, false);
762 case TdsColumnType.NVarChar :
765 element = GetStringValue (true, false);
767 case TdsColumnType.Real :
768 case TdsColumnType.Float8 :
769 element = GetFloatValue (colType);
771 case TdsColumnType.FloatN :
774 element = GetFloatValue (colType);
776 case TdsColumnType.SmallMoney :
777 case TdsColumnType.Money :
778 element = GetMoneyValue (colType);
780 case TdsColumnType.MoneyN :
783 element = GetMoneyValue (colType);
785 case TdsColumnType.Numeric :
786 case TdsColumnType.Decimal :
791 precision = comm.GetByte ();
792 scale = comm.GetByte ();
796 precision = (byte) columns[ordinal].NumericPrecision;
797 scale = (byte) columns[ordinal].NumericScale;
799 precision = (byte) columns[ordinal]["NumericPrecision"];
800 scale = (byte) columns[ordinal]["NumericScale"];
804 element = GetDecimalValue (precision, scale);
806 // workaround for fact that TDS 7.0 returns
807 // bigint as decimal (19,0), and client code
808 // expects it to be returned as a long
809 if (scale == 0 && precision <= 19) {
810 if (!(element is System.DBNull))
811 element = Convert.ToInt64 (element);
814 case TdsColumnType.DateTimeN :
817 element = GetDateTimeValue (colType);
819 case TdsColumnType.DateTime4 :
820 case TdsColumnType.DateTime :
821 element = GetDateTimeValue (colType);
823 case TdsColumnType.VarBinary :
824 case TdsColumnType.Binary :
827 element = GetBinaryValue ();
829 case TdsColumnType.BitN :
832 if (comm.GetByte () == 0)
833 element = DBNull.Value;
835 element = (comm.GetByte() != 0);
837 case TdsColumnType.Bit :
838 int columnSize = comm.GetByte ();
839 element = (columnSize != 0);
841 case TdsColumnType.UniqueIdentifier :
842 if (comm.Peek () != 16) { // If it's null, then what to do?
843 /*byte swallowed =*/ comm.GetByte();
844 element = DBNull.Value;
850 len = comm.GetByte () & 0xff;
852 byte[] guidBytes = comm.GetBytes (len, true);
853 if (!BitConverter.IsLittleEndian) {
854 byte[] swappedguidBytes = new byte[len];
855 for (int i = 0; i < 4; i++)
856 swappedguidBytes[i] = guidBytes[4-i-1];
857 for (int i = 4; i < 6; i++)
858 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
859 for (int i = 6; i < 8; i++)
860 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
861 for (int i = 8; i < 16; i++)
862 swappedguidBytes[i] = guidBytes[i];
863 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
865 element = new Guid (guidBytes);
874 private object GetBinaryValue ()
877 object result = DBNull.Value;
879 if (tdsVersion == TdsVersion.tds70) {
880 len = comm.GetTdsShort ();
881 if (len != 0xffff && len > 0)
882 result = comm.GetBytes (len, true);
884 len = (comm.GetByte () & 0xff);
886 result = comm.GetBytes (len, true);
892 private object GetDateTimeValue (
905 throw new ArgumentNullException ("type");
908 case TdsColumnType.DateTime4:
911 case TdsColumnType.DateTime:
914 case TdsColumnType.DateTimeN:
915 byte tmp = comm.Peek ();
916 if (tmp != 0 && tmp != 4 && tmp != 8)
918 len = comm.GetByte ();
922 DateTime epoch = new DateTime (1900, 1, 1);
926 result = epoch.AddDays (comm.GetTdsInt ());
927 int seconds = comm.GetTdsInt ();
928 long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f);
929 if (seconds != 0 || millis != 0) {
930 result = ((DateTime) result).AddSeconds (seconds / 300);
931 result = ((DateTime) result).AddMilliseconds (millis);
935 // MSDN says small datetime is stored in 2 bytes as no of days
936 // *after* 1/1/1900. so, cast to unsigned short
937 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
938 short minutes = comm.GetTdsShort ();
940 result = ((DateTime) result).AddMinutes ((int) minutes);
943 result = DBNull.Value;
950 private object GetDecimalValue (byte precision, byte scale)
952 if (tdsVersion < TdsVersion.tds70)
953 return GetDecimalValueTds50 (precision, scale);
955 return GetDecimalValueTds70 (precision, scale);
958 private object GetDecimalValueTds70 (byte precision, byte scale)
960 int[] bits = new int[4] {0,0,0,0};
962 int len = (comm.GetByte() & 0xff) - 1;
966 bool positive = (comm.GetByte () == 1);
968 throw new OverflowException ();
970 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
971 bits[index] = comm.GetTdsInt ();
974 return new TdsBigDecimal (precision, scale, !positive, bits);
976 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
979 private object GetDecimalValueTds50 (byte precision, byte scale)
981 int[] bits = new int[4] {0,0,0,0};
983 int len = (comm.GetByte() & 0xff);
987 byte[] dec_bytes=comm.GetBytes(len,false);
989 byte[] easy=new byte[4];
991 bool positive = dec_bytes[0]==1;
994 throw new OverflowException ();
996 for (int i = 1, index = 0; i < len && i < 16; i +=
998 for(int j=0; j<4; j++)
1000 easy[j]=dec_bytes[len-
1004 if(!BitConverter.IsLittleEndian)
1005 easy=comm.Swap(easy);
1006 bits[index] = BitConverter.ToInt32(easy,0);
1009 return new TdsBigDecimal (precision,
1010 scale, positive, bits);
1012 return new Decimal(bits[0], bits[1], bits
1013 [2], positive, scale);
1017 private object GetFloatValue (
1019 TdsColumnType? columnType
1021 TdsColumnType columnType
1026 if (columnType == null)
1027 throw new ArgumentNullException ("columnType");
1031 switch (columnType) {
1032 case TdsColumnType.Real:
1035 case TdsColumnType.Float8:
1038 case TdsColumnType.FloatN:
1039 columnSize = comm.GetByte ();
1043 switch (columnSize) {
1045 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1047 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1049 return DBNull.Value;
1053 private object GetImageValue ()
1055 byte hasValue = comm.GetByte ();
1058 return DBNull.Value;
1061 int len = comm.GetTdsInt ();
1064 return DBNull.Value;
1066 return (comm.GetBytes (len, true));
1069 private object GetIntValue (
1081 throw new ArgumentNullException ("type");
1084 case TdsColumnType.IntN :
1085 len = comm.GetByte ();
1087 case TdsColumnType.Int4 :
1090 case TdsColumnType.Int2 :
1093 case TdsColumnType.Int1 :
1097 return DBNull.Value;
1102 return (comm.GetTdsInt ());
1104 return (comm.GetTdsShort ());
1106 return (comm.GetByte ());
1108 return DBNull.Value;
1112 private object GetMoneyValue (
1124 throw new ArgumentNullException ("type");
1127 case TdsColumnType.SmallMoney :
1128 case TdsColumnType.Money4 :
1131 case TdsColumnType.Money :
1134 case TdsColumnType.MoneyN :
1135 len = comm.GetByte ();
1138 return DBNull.Value;
1143 return new Decimal (Comm.GetTdsInt (), 0, 0, false, 4);
1145 int hi = Comm.GetTdsInt ();
1146 int lo = Comm.GetTdsInt ();
1147 return new Decimal (lo, hi, 0, false, 4);
1149 return DBNull.Value;
1153 private object GetStringValue (bool wideChars, bool outputParam)
1155 bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam);
1156 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1158 if (tdsVersion < TdsVersion.tds70 && len == 0)
1159 return DBNull.Value;
1160 else if (len >= 0) {
1163 result = comm.GetString (len / 2);
1165 result = comm.GetString (len, false);
1166 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1167 result = string.Empty;
1171 return DBNull.Value;
1174 protected int GetSubPacketLength ()
1176 return comm.GetTdsShort ();
1179 private object GetTextValue (bool wideChars)
1181 string result = null;
1182 byte hasValue = comm.GetByte ();
1185 return DBNull.Value;
1187 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1190 int len = comm.GetTdsInt ();
1192 //if the len is 0 , then the string can be a '' string
1193 // this method is called only for Text and NText. Hence will
1194 // return a empty string
1196 return string.Empty;
1199 result = comm.GetString (len / 2);
1201 result = comm.GetString (len, false);
1204 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1205 result = string.Empty;
1210 internal bool IsBlobType (TdsColumnType columnType)
1212 return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1215 internal bool IsLargeType (TdsColumnType columnType)
1217 return ((byte) columnType > 128);
1220 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1222 switch (columnType) {
1223 case TdsColumnType.Int1 :
1224 case TdsColumnType.Int2 :
1225 case TdsColumnType.Int4 :
1226 case TdsColumnType.Float8 :
1227 case TdsColumnType.DateTime :
1228 case TdsColumnType.Bit :
1229 case TdsColumnType.Money :
1230 case TdsColumnType.Money4 :
1231 case TdsColumnType.SmallMoney :
1232 case TdsColumnType.Real :
1233 case TdsColumnType.DateTime4 :
1235 case TdsColumnType.Decimal:
1236 case TdsColumnType.Numeric:
1244 protected void LoadRow ()
1246 if (SequentialAccess) {
1250 isResultRead = true;
1254 currentRow = new TdsDataRow ();
1257 foreach (TdsDataColumn column in columns) {
1259 object o = GetColumnValue (column.ColumnType, false, i);
1261 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1265 outputParameters.Add (o);
1267 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1268 currentRow.BigDecimalIndex = i;
1273 internal static int LookupBufferSize (TdsColumnType columnType)
1275 switch (columnType) {
1276 case TdsColumnType.Int1 :
1277 case TdsColumnType.Bit :
1279 case TdsColumnType.Int2 :
1281 case TdsColumnType.Int4 :
1282 case TdsColumnType.Real :
1283 case TdsColumnType.DateTime4 :
1284 case TdsColumnType.Money4 :
1285 case TdsColumnType.SmallMoney :
1287 case TdsColumnType.Float8 :
1288 case TdsColumnType.DateTime :
1289 case TdsColumnType.Money :
1296 protected internal int ProcessAuthentication ()
1298 int pdu_size = Comm.GetTdsShort ();
1299 byte[] msg2 = Comm.GetBytes (pdu_size, true);
1301 Type2Message t2 = new Type2Message (msg2);
1302 // 0x0001 Negotiate Unicode
1303 // 0x0200 Negotiate NTLM
1304 // 0x8000 Negotiate Always Sign
1306 Type3Message t3 = new Type3Message ();
1307 t3.Challenge = t2.Nonce;
1309 t3.Domain = this.connectionParms.DefaultDomain;
1310 t3.Host = this.connectionParms.Hostname;
1311 t3.Username = this.connectionParms.User;
1312 t3.Password = this.connectionParms.Password;
1314 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1315 Comm.Append (t3.GetBytes ());
1318 } catch (IOException ex) {
1320 throw new TdsInternalException ("Server closed the connection.", ex);
1322 return 1; // TDS_SUCCEED
1325 protected void ProcessColumnDetail ()
1327 int len = GetSubPacketLength ();
1328 byte[] values = new byte[3];
1329 int columnNameLength;
1330 string baseColumnName = String.Empty;
1333 while (position < len) {
1334 for (int j = 0; j < 3; j += 1)
1335 values[j] = comm.GetByte ();
1338 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1340 if (tdsVersion == TdsVersion.tds70) {
1341 columnNameLength = comm.GetByte ();
1342 position += 2 * columnNameLength + 1;
1345 columnNameLength = comm.GetByte ();
1346 position += columnNameLength + 1;
1348 baseColumnName = comm.GetString (columnNameLength);
1351 byte index = (byte) (values[0] - (byte) 1);
1352 byte tableIndex = (byte) (values[1] - (byte) 1);
1353 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1355 TdsDataColumn column = columns [index];
1357 column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1358 column.IsExpression = isExpression;
1359 column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1360 column.IsAliased = isAlias;
1361 column.BaseColumnName = ((isAlias) ? baseColumnName : null);
1362 column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null);
1364 column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0);
1365 column ["IsExpression"] = isExpression;
1366 column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0);
1367 column ["IsAliased"] = isAlias;
1368 column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1369 column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1374 protected abstract TdsDataColumnCollection ProcessColumnInfo ();
1376 protected void ProcessColumnNames ()
1378 columnNames = new ArrayList ();
1380 int totalLength = comm.GetTdsShort ();
1384 while (bytesRead < totalLength) {
1385 int columnNameLength = comm.GetByte ();
1386 string columnName = comm.GetString (columnNameLength);
1387 bytesRead = bytesRead + 1 + columnNameLength;
1388 columnNames.Add (columnName);
1393 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1394 protected void ProcessEndToken (TdsPacketSubType type)
1396 byte status = Comm.GetByte ();
1398 byte op = comm.GetByte ();
1401 int rowCount = comm.GetTdsInt ();
1402 bool validRowCount = IsValidRowCount (status,op);
1403 moreResults = ((status & 0x01) != 0);
1404 bool cancelled = ((status & 0x20) != 0);
1407 case TdsPacketSubType.DoneProc:
1409 goto case TdsPacketSubType.Done;
1410 case TdsPacketSubType.Done:
1411 case TdsPacketSubType.DoneInProc:
1412 if (validRowCount) {
1413 if (recordsAffected == -1)
1414 recordsAffected = rowCount;
1416 recordsAffected += rowCount;
1422 queryInProgress = false;
1424 cancelsProcessed += 1;
1425 if (messages.Count > 0 && !moreResults)
1426 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1429 protected void ProcessEnvironmentChange ()
1431 int len = GetSubPacketLength ();
1432 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1436 case TdsEnvPacketSubType.BlockSize :
1438 cLen = comm.GetByte ();
1439 blockSize = comm.GetString (cLen);
1441 if (tdsVersion == TdsVersion.tds70)
1442 comm.Skip (len - 2 - cLen * 2);
1444 comm.Skip (len - 2 - cLen);
1446 packetSize = Int32.Parse (blockSize);
1447 comm.ResizeOutBuf (packetSize);
1449 case TdsEnvPacketSubType.CharSet :
1450 cLen = comm.GetByte ();
1451 if (tdsVersion == TdsVersion.tds70) {
1452 SetCharset (comm.GetString (cLen));
1453 comm.Skip (len - 2 - cLen * 2);
1456 SetCharset (comm.GetString (cLen));
1457 comm.Skip (len - 2 - cLen);
1461 case TdsEnvPacketSubType.Locale :
1462 cLen = comm.GetByte ();
1464 if (tdsVersion == TdsVersion.tds70) {
1465 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1466 comm.Skip (len - 2 - cLen * 2);
1469 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1470 comm.Skip (len - 2 - cLen);
1472 locale = new CultureInfo (lcid);
1474 case TdsEnvPacketSubType.Database :
1475 cLen = comm.GetByte ();
1476 string newDB = comm.GetString (cLen);
1477 cLen = comm.GetByte () & 0xff;
1478 comm.GetString (cLen);
1479 if (originalDatabase == string.Empty)
1480 originalDatabase = newDB;
1484 comm.Skip (len - 1);
1489 protected void ProcessLoginAck ()
1491 GetSubPacketLength ();
1493 if (tdsVersion == TdsVersion.tds70) {
1495 int nameLength = comm.GetByte ();
1496 databaseProductName = comm.GetString (nameLength);
1497 databaseMajorVersion = comm.GetByte ();
1498 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1499 comm.GetByte ().ToString("00"),
1500 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1504 short nameLength = comm.GetByte ();
1505 databaseProductName = comm.GetString (nameLength);
1507 databaseMajorVersion = comm.GetByte ();
1508 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1512 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1513 int last = databaseProductName.IndexOf ('\0');
1514 databaseProductName = databaseProductName.Substring (0, last);
1520 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1522 if (TdsErrorMessage != null)
1523 TdsErrorMessage (this, e);
1526 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1528 if (TdsInfoMessage != null)
1529 TdsInfoMessage (this, e);
1533 protected void ProcessMessage (TdsPacketSubType subType)
1535 GetSubPacketLength ();
1537 int number = comm.GetTdsInt ();
1538 byte state = comm.GetByte ();
1539 byte theClass = comm.GetByte ();
1545 bool isError = false;
1547 if (subType == TdsPacketSubType.EED) {
1548 isError = (theClass > 10);
1549 comm.Skip (comm.GetByte ()); // SQL State
1550 comm.Skip (1); // Status
1551 comm.Skip (2); // TranState
1553 isError = (subType == TdsPacketSubType.Error);
1555 message = comm.GetString (comm.GetTdsShort ());
1556 server = comm.GetString (comm.GetByte ());
1557 procedure = comm.GetString (comm.GetByte ());
1558 lineNumber = comm.GetByte ();
1560 source = String.Empty; // FIXME
1563 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1565 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1568 protected void ProcessOutputParam ()
1570 GetSubPacketLength ();
1571 /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1574 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1575 object value = GetColumnValue (colType, true);
1576 outputParameters.Add (value);
1579 protected void ProcessDynamic ()
1582 /*byte type =*/ Comm.GetByte ();
1583 /*byte status =*/ Comm.GetByte ();
1584 /*string id =*/ Comm.GetString (Comm.GetByte ());
1587 protected virtual TdsPacketSubType ProcessSubPacket ()
1589 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1592 case TdsPacketSubType.Dynamic2:
1593 comm.Skip (comm.GetTdsInt ());
1595 case TdsPacketSubType.AltName:
1596 case TdsPacketSubType.AltFormat:
1597 case TdsPacketSubType.Capability:
1598 case TdsPacketSubType.ParamFormat:
1599 comm.Skip (comm.GetTdsShort ());
1601 case TdsPacketSubType.Dynamic:
1604 case TdsPacketSubType.EnvironmentChange:
1605 ProcessEnvironmentChange ();
1607 case TdsPacketSubType.Info: // TDS 4.2/7.0
1608 case TdsPacketSubType.EED: // TDS 5.0
1609 case TdsPacketSubType.Error: // TDS 4.2/7.0
1610 ProcessMessage (subType);
1612 case TdsPacketSubType.Param:
1613 ProcessOutputParam ();
1615 case TdsPacketSubType.LoginAck:
1618 case TdsPacketSubType.Authentication: // TDS 7.0
1619 ProcessAuthentication ();
1621 case TdsPacketSubType.ReturnStatus :
1622 ProcessReturnStatus ();
1624 case TdsPacketSubType.ProcId:
1627 case TdsPacketSubType.Done:
1628 case TdsPacketSubType.DoneProc:
1629 case TdsPacketSubType.DoneInProc:
1630 ProcessEndToken (subType);
1632 case TdsPacketSubType.ColumnName:
1634 ProcessColumnNames ();
1636 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1637 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1638 case TdsPacketSubType.RowFormat: // TDS 5.0
1639 columns = ProcessColumnInfo ();
1641 case TdsPacketSubType.ColumnDetail:
1642 ProcessColumnDetail ();
1644 case TdsPacketSubType.TableName:
1645 ProcessTableName ();
1647 case TdsPacketSubType.ColumnOrder:
1648 comm.Skip (comm.GetTdsShort ());
1650 case TdsPacketSubType.Control:
1651 comm.Skip (comm.GetTdsShort ());
1653 case TdsPacketSubType.Row:
1661 protected void ProcessTableName ()
1663 tableNames = new ArrayList ();
1664 int totalLength = comm.GetTdsShort ();
1668 while (position < totalLength) {
1669 if (tdsVersion == TdsVersion.tds70) {
1670 len = comm.GetTdsShort ();
1671 position += 2 * (len + 1);
1674 len = comm.GetByte ();
1675 position += len + 1;
1677 tableNames.Add (comm.GetString (len));
1681 protected void SetCharset (string charset)
1683 if (charset == null || charset.Length > 30)
1686 if (this.charset != null && this.charset == charset)
1689 if (charset.StartsWith ("cp")) {
1690 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1691 this.charset = charset;
1694 encoder = Encoding.GetEncoding ("iso-8859-1");
1695 this.charset = "iso_1";
1697 comm.Encoder = encoder;
1700 protected void SetLanguage (string language)
1702 if (language == null || language.Length > 30)
1703 language = "us_english";
1705 this.language = language;
1708 protected virtual void ProcessReturnStatus ()
1713 #endregion // Private Methods
1716 #region asynchronous methods
1717 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1718 AsyncCallback callback, object state)
1722 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1723 ar.TdsAsyncState.WantResults = wantResults;
1725 Comm.StartPacket (TdsPacketType.Query);
1729 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1731 } catch (IOException ex) {
1733 throw new TdsInternalException ("Server closed the connection.", ex);
1739 protected void EndExecuteQueryInternal (IAsyncResult ar)
1741 if (!ar.IsCompleted)
1742 ar.AsyncWaitHandle.WaitOne ();
1743 TdsAsyncResult result = (TdsAsyncResult) ar;
1744 if (result.IsCompletedWithException)
1745 throw result.Exception;
1748 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1750 TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1751 TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1754 Comm.EndReadPacket (ar);
1755 if (!tdsState.WantResults)
1757 } catch (Exception e) {
1758 result.MarkComplete (e);
1761 result.MarkComplete ();
1765 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1766 TdsMetaParameterCollection parameters,
1767 AsyncCallback callback,
1770 // abstract, kept to be backward compatiable.
1771 throw new NotImplementedException ("should not be called!");
1774 public virtual void EndExecuteNonQuery (IAsyncResult ar)
1777 throw new NotImplementedException ("should not be called!");
1780 public virtual IAsyncResult BeginExecuteQuery (string sql,
1781 TdsMetaParameterCollection parameters,
1782 AsyncCallback callback,
1785 // abstract, kept to be backward compatiable.
1786 throw new NotImplementedException ("should not be called!");
1789 public virtual void EndExecuteQuery (IAsyncResult ar)
1792 throw new NotImplementedException ("should not be called!");
1795 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
1799 TdsMetaParameterCollection parameters,
1800 AsyncCallback callback,
1803 throw new NotImplementedException ("should not be called!");
1806 public virtual void EndExecuteProcedure (IAsyncResult ar)
1809 throw new NotImplementedException ("should not be called!");
1812 public void WaitFor (IAsyncResult ar)
1814 if (! ar.IsCompleted)
1815 ar.AsyncWaitHandle.WaitOne ();
1818 public void CheckAndThrowException (IAsyncResult ar)
1820 TdsAsyncResult result = (TdsAsyncResult) ar;
1821 if (result.IsCompleted && result.IsCompletedWithException)
1822 throw result.Exception;
1825 #endregion // asynchronous methods