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)
8 // Veerapuram Varadhan (vvaradhan@novell.com)
10 // Copyright (C) 2002 Tim Coleman
11 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
12 // Portions (C) 2003,2005 Daniel Morgan
13 // Portions (C) 2008,2009 Novell Inc.
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using Mono.Security.Protocol.Ntlm;
39 using System.Collections;
40 using System.ComponentModel;
41 using System.Diagnostics;
42 using System.Net.Sockets;
43 using System.Globalization;
46 namespace Mono.Data.Tds.Protocol
48 public abstract class Tds
53 TdsVersion tdsVersion;
55 protected internal TdsConnectionParameters connectionParms;
56 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
61 string originalDatabase = string.Empty;
62 string databaseProductName;
63 string databaseProductVersion;
64 int databaseMajorVersion;
65 CultureInfo locale = CultureInfo.InvariantCulture;
78 TdsDataRow currentRow;
79 TdsDataColumnCollection columns;
82 ArrayList columnNames;
84 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
93 ArrayList outputParameters = new ArrayList ();
94 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
96 int recordsAffected = -1;
100 int StreamColumnIndex;
102 bool sequentialAccess;
108 internal int poolStatus = 0;
114 protected string Charset {
115 get { return charset; }
118 protected CultureInfo Locale {
119 get { return locale; }
122 public bool DoneProc {
123 get { return doneProc; }
126 protected string Language {
127 get { return language; }
130 protected ArrayList ColumnNames {
131 get { return columnNames; }
134 public TdsDataRow ColumnValues {
135 get { return currentRow; }
138 internal TdsComm Comm {
142 public string Database {
143 get { return database; }
146 public string DataSource {
147 get { return dataSource; }
150 public bool IsConnected {
151 get { return connected && comm != null && comm.IsConnected (); }
152 set { connected = value; }
155 public bool Pooling {
156 get { return pooling; }
157 set { pooling = value; }
160 public bool MoreResults {
161 get { return moreResults; }
162 set { moreResults = value; }
165 public int PacketSize {
166 get { return packetSize; }
169 public int RecordsAffected {
170 get { return recordsAffected; }
171 set { recordsAffected = value; }
174 public string ServerVersion {
175 get { return databaseProductVersion; }
178 public TdsDataColumnCollection Columns {
179 get { return columns; }
182 public TdsVersion TdsVersion {
183 get { return tdsVersion; }
186 public ArrayList OutputParameters {
187 get { return outputParameters; }
188 set { outputParameters = value; }
191 protected TdsMetaParameterCollection Parameters {
192 get { return parameters; }
193 set { parameters = value; }
196 public bool SequentialAccess {
197 get { return sequentialAccess; }
198 set { sequentialAccess = value; }
201 public byte[] Collation {
202 get {return collation; }
205 private void SkipRow ()
207 SkipToColumnIndex (Columns.Count);
210 StreamColumnIndex = 0;
212 LoadInProgress = false;
215 private void SkipToColumnIndex (int colIndex)
220 if (colIndex < StreamColumnIndex)
221 throw new Exception ("Cannot Skip to a colindex less than the curr index");
223 while (colIndex != StreamColumnIndex) {
225 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
227 throw new Exception ("Column type unset.");
229 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
231 if (!(colType == TdsColumnType.Image ||
232 colType == TdsColumnType.Text ||
233 colType == TdsColumnType.NText)) {
234 GetColumnValue (colType, false, StreamColumnIndex);
235 StreamColumnIndex ++;
239 Comm.Skip (StreamLength);
246 public object GetSequentialColumnValue (int colIndex)
248 if (colIndex < StreamColumnIndex)
249 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
254 if (colIndex != StreamColumnIndex)
255 SkipToColumnIndex (colIndex);
258 object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
260 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
266 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
268 if (colIndex < StreamColumnIndex)
269 throw new InvalidOperationException ("Invalid attempt to read from column ordinal" + colIndex);
271 if (colIndex != StreamColumnIndex)
272 SkipToColumnIndex (colIndex);
274 if (!LoadInProgress) {
276 BeginLoad (Columns[colIndex].ColumnType);
278 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
284 return LoadData (fieldIndex, buffer, bufferIndex, size);
285 } catch (IOException ex) {
287 throw new TdsInternalException ("Server closed the connection.", ex);
291 private void BeginLoad (
293 TdsColumnType? colType
295 TdsColumnType colType
306 throw new ArgumentNullException ("colType");
310 case TdsColumnType.Text :
311 case TdsColumnType.NText:
312 case TdsColumnType.Image:
313 if (Comm.GetByte () != 0) {
315 StreamLength = Comm.GetTdsInt ();
317 // use -2 to indicate that we're dealing
322 case TdsColumnType.BigVarChar:
323 case TdsColumnType.BigChar:
324 case TdsColumnType.BigBinary:
325 case TdsColumnType.BigVarBinary:
327 StreamLength = Comm.GetTdsShort ();
329 case TdsColumnType.VarChar :
330 case TdsColumnType.NVarChar :
331 case TdsColumnType.Char:
332 case TdsColumnType.NChar:
333 case TdsColumnType.Binary:
334 case TdsColumnType.VarBinary:
335 StreamLength = Comm.GetTdsShort ();
343 LoadInProgress = true;
346 private void EndLoad()
348 if (StreamLength > 0)
349 Comm.Skip (StreamLength);
353 LoadInProgress = false;
356 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
358 if (StreamLength <= 0)
361 if (fieldIndex < StreamIndex)
362 throw new InvalidOperationException (string.Format (
363 "Attempting to read at dataIndex '{0}' is " +
364 "not allowed as this is less than the " +
365 "current position. You must read from " +
366 "dataIndex '{1}' or greater.",
367 fieldIndex, StreamIndex));
369 if (fieldIndex >= (StreamLength + StreamIndex))
372 // determine number of bytes to skip
373 int skip = (int) (fieldIndex - StreamIndex);
376 // update the current position
377 StreamIndex += (fieldIndex - StreamIndex);
378 // update the remaining length
379 StreamLength -= skip;
381 // Load the reqd amt of bytes
382 int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
383 byte[] arr = Comm.GetBytes (loadlen, true);
385 // update the index and stream length
386 StreamIndex += loadlen + (fieldIndex - StreamIndex);
387 StreamLength -= loadlen;
388 arr.CopyTo (buffer, bufferIndex);
393 #endregion // Properties
397 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
398 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
404 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
406 this.tdsVersion = tdsVersion;
407 this.packetSize = packetSize;
408 this.dataSource = dataSource;
410 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
413 #endregion // Constructors
415 #region Public Methods
417 internal protected void InitExec ()
423 outputParameters.Clear ();
426 public void Cancel ()
428 if (queryInProgress) {
429 if (cancelsRequested == cancelsProcessed) {
430 comm.StartPacket (TdsPacketType.Cancel);
433 } catch (IOException ex) {
435 throw new TdsInternalException ("Server closed the connection.", ex);
437 cancelsRequested += 1;
442 public abstract bool Connect (TdsConnectionParameters connectionParameters);
444 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
446 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
447 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
450 public void Disconnect ()
453 comm.StartPacket (TdsPacketType.Logoff);
454 comm.Append ((byte) 0);
457 // We're closing the socket anyway
463 public virtual bool Reset ()
465 database = originalDatabase;
469 protected virtual bool IsValidRowCount (byte status, byte op)
471 return ((status & (0x10)) != 0) ;
474 public void Execute (string sql)
476 Execute (sql, null, 0, false);
479 public void ExecProc (string sql)
481 ExecProc (sql, null, 0, false);
484 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
486 ExecuteQuery (sql, timeout, wantResults);
489 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
491 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
494 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
496 throw new NotSupportedException ();
499 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
504 CheckForData (timeout);
507 } catch (IOException ex) {
509 throw new TdsInternalException ("Server closed the connection.", ex);
513 internal void ExecBulkCopy (int timeout, bool wantResults)
518 CheckForData (timeout);
521 } catch (IOException ex) {
523 throw new TdsInternalException ("Server closed the connection.", ex);
527 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
531 Comm.StartPacket (TdsPacketType.Query);
535 CheckForData (timeout);
538 } catch (IOException ex) {
540 throw new TdsInternalException ("Server closed the connection.", ex);
544 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
545 int timeout, bool wantResults)
547 Comm.StartPacket (TdsPacketType.DBRPC);
549 byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
550 byte rpcNameLength = (byte) rpcNameBytes.Length;
551 ushort mask = 0x0000;
552 ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
555 Comm.Append (packetLength);
556 Comm.Append (rpcNameLength);
557 Comm.Append (rpcNameBytes);
562 CheckForData (timeout);
565 } catch (IOException ex) {
567 throw new TdsInternalException ("Server closed the connection.", ex);
571 public bool NextResult ()
573 if (SequentialAccess) {
575 while (NextRow ()) {}
577 isResultRead = false;
583 TdsPacketSubType subType;
586 bool outputParams = false;
589 subType = ProcessSubPacket ();
595 case TdsPacketSubType.ColumnInfo:
596 case TdsPacketSubType.ColumnMetadata:
597 case TdsPacketSubType.RowFormat:
598 byte peek = Comm.Peek ();
599 done = (peek != (byte) TdsPacketSubType.TableName);
600 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
605 case TdsPacketSubType.TableName:
608 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
610 case TdsPacketSubType.ColumnDetail:
622 public bool NextRow ()
624 if (SequentialAccess) {
631 TdsPacketSubType subType;
636 subType = ProcessSubPacket ();
638 case TdsPacketSubType.Row:
642 case TdsPacketSubType.Done:
643 case TdsPacketSubType.DoneProc:
644 case TdsPacketSubType.DoneInProc:
654 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
656 throw new NotSupportedException ();
659 public void SkipToEnd ()
662 while (NextResult ()) { /* DO NOTHING */ }
663 } catch (IOException ex) {
665 throw new TdsInternalException ("Server closed the connection.", ex);
669 public virtual void Unprepare (string statementId)
671 throw new NotSupportedException ();
674 #endregion // Public Methods
676 #region // Private Methods
678 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
679 protected void CheckForData (int timeout)
681 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
683 throw CreateTimeoutException (dataSource, "CheckForData()");
687 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
689 return new TdsInternalInfoMessageEventArgs (errors);
692 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
694 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
697 private Encoding GetEncodingFromColumnCollation (int lcid, int sortId)
700 return TdsCharset.GetEncodingFromSortOrder (sortId);
702 return TdsCharset.GetEncodingFromLCID (lcid);
705 protected object GetColumnValue (
707 TdsColumnType? colType,
709 TdsColumnType colType,
713 return GetColumnValue (colType, outParam, -1);
716 private object GetColumnValue (
718 TdsColumnType? colType,
720 TdsColumnType colType,
722 bool outParam, int ordinal)
725 object element = null;
727 int lcid = 0, sortId = 0;
731 throw new ArgumentNullException ("colType");
733 if (ordinal > -1 && tdsVersion > TdsVersion.tds70) {
735 lcid = (int) columns[ordinal].LCID;
736 sortId = (int) columns[ordinal].SortOrder;
738 lcid = (int) columns[ordinal]["LCID"];
739 sortId = (int) columns[ordinal]["SortOrder"];
744 case TdsColumnType.IntN :
747 element = GetIntValue (colType);
749 case TdsColumnType.Int1 :
750 case TdsColumnType.Int2 :
751 case TdsColumnType.Int4 :
752 case TdsColumnType.BigInt :
753 element = GetIntValue (colType);
755 case TdsColumnType.Image :
758 element = GetImageValue ();
760 case TdsColumnType.Text :
761 enc = GetEncodingFromColumnCollation (lcid, sortId);
764 element = GetTextValue (false, enc);
766 case TdsColumnType.NText :
767 enc = GetEncodingFromColumnCollation (lcid, sortId);
770 element = GetTextValue (true, enc);
772 case TdsColumnType.Char :
773 case TdsColumnType.VarChar :
774 enc = GetEncodingFromColumnCollation (lcid, sortId);
777 element = GetStringValue (colType, false, outParam, enc);
779 case TdsColumnType.BigVarBinary :
782 len = comm.GetTdsShort ();
783 element = comm.GetBytes (len, true);
786 case TdsColumnType.BigBinary :
789 len = comm.GetTdsShort ();
790 element = comm.GetBytes (len, true);
793 case TdsColumnType.BigBinary :
796 element = GetBinaryValue ();
798 case TdsColumnType.BigChar :
799 case TdsColumnType.BigVarChar :
800 enc = GetEncodingFromColumnCollation (lcid, sortId);
803 element = GetStringValue (colType, false, outParam, enc);
805 case TdsColumnType.NChar :
806 case TdsColumnType.BigNVarChar :
807 enc = GetEncodingFromColumnCollation (lcid, sortId);
810 element = GetStringValue (colType, true, outParam, enc);
812 case TdsColumnType.NVarChar :
813 enc = GetEncodingFromColumnCollation (lcid, sortId);
816 element = GetStringValue (colType, true, outParam, enc);
818 case TdsColumnType.Real :
819 case TdsColumnType.Float8 :
820 element = GetFloatValue (colType);
822 case TdsColumnType.FloatN :
825 element = GetFloatValue (colType);
827 case TdsColumnType.SmallMoney :
828 case TdsColumnType.Money :
829 element = GetMoneyValue (colType);
831 case TdsColumnType.MoneyN :
834 element = GetMoneyValue (colType);
836 case TdsColumnType.Numeric :
837 case TdsColumnType.Decimal :
842 precision = comm.GetByte ();
843 scale = comm.GetByte ();
847 precision = (byte) columns[ordinal].NumericPrecision;
848 scale = (byte) columns[ordinal].NumericScale;
850 precision = (byte) columns[ordinal]["NumericPrecision"];
851 scale = (byte) columns[ordinal]["NumericScale"];
855 element = GetDecimalValue (precision, scale);
857 // workaround for fact that TDS 7.0 returns
858 // bigint as decimal (19,0), and client code
859 // expects it to be returned as a long
860 if (scale == 0 && precision <= 19 && tdsVersion == TdsVersion.tds70) {
861 if (!(element is System.DBNull))
862 element = Convert.ToInt64 (element);
865 case TdsColumnType.DateTimeN :
868 element = GetDateTimeValue (colType);
870 case TdsColumnType.DateTime4 :
871 case TdsColumnType.DateTime :
872 element = GetDateTimeValue (colType);
874 case TdsColumnType.VarBinary :
875 case TdsColumnType.Binary :
878 element = GetBinaryValue ();
880 case TdsColumnType.BitN :
883 if (comm.GetByte () == 0)
884 element = DBNull.Value;
886 element = (comm.GetByte() != 0);
888 case TdsColumnType.Bit :
889 int columnSize = comm.GetByte ();
890 element = (columnSize != 0);
892 case TdsColumnType.UniqueIdentifier :
893 if (comm.Peek () != 16) { // If it's null, then what to do?
894 /*byte swallowed =*/ comm.GetByte();
895 element = DBNull.Value;
901 len = comm.GetByte () & 0xff;
903 byte[] guidBytes = comm.GetBytes (len, true);
904 if (!BitConverter.IsLittleEndian) {
905 byte[] swappedguidBytes = new byte[len];
906 for (int i = 0; i < 4; i++)
907 swappedguidBytes[i] = guidBytes[4-i-1];
908 for (int i = 4; i < 6; i++)
909 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
910 for (int i = 6; i < 8; i++)
911 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
912 for (int i = 8; i < 16; i++)
913 swappedguidBytes[i] = guidBytes[i];
914 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
916 element = new Guid (guidBytes);
925 private object GetBinaryValue ()
928 object result = DBNull.Value;
930 if (tdsVersion >= TdsVersion.tds70) {
931 len = comm.GetTdsShort ();
932 if (len != 0xffff && len >= 0)
933 result = comm.GetBytes (len, true);
935 len = (comm.GetByte () & 0xff);
937 result = comm.GetBytes (len, true);
943 private object GetDateTimeValue (
956 throw new ArgumentNullException ("type");
959 case TdsColumnType.DateTime4:
962 case TdsColumnType.DateTime:
965 case TdsColumnType.DateTimeN:
966 byte tmp = comm.Peek ();
967 if (tmp != 0 && tmp != 4 && tmp != 8)
969 len = comm.GetByte ();
973 DateTime epoch = new DateTime (1900, 1, 1);
977 result = epoch.AddDays (comm.GetTdsInt ());
978 int seconds = comm.GetTdsInt ();
979 long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f);
980 if (seconds != 0 || millis != 0) {
981 result = ((DateTime) result).AddSeconds (seconds / 300);
982 result = ((DateTime) result).AddMilliseconds (millis);
986 // MSDN says small datetime is stored in 2 bytes as no of days
987 // *after* 1/1/1900. so, cast to unsigned short
988 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
989 short minutes = comm.GetTdsShort ();
991 result = ((DateTime) result).AddMinutes ((int) minutes);
994 result = DBNull.Value;
1001 private object GetDecimalValue (byte precision, byte scale)
1003 if (tdsVersion < TdsVersion.tds70)
1004 return GetDecimalValueTds50 (precision, scale);
1006 return GetDecimalValueTds70 (precision, scale);
1009 private object GetDecimalValueTds70 (byte precision, byte scale)
1011 int[] bits = new int[4] {0,0,0,0};
1013 int len = (comm.GetByte() & 0xff) - 1;
1015 return DBNull.Value;
1017 bool positive = (comm.GetByte () == 1);
1019 throw new OverflowException ();
1021 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
1022 bits[index] = comm.GetTdsInt ();
1025 return new TdsBigDecimal (precision, scale, !positive, bits);
1027 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
1030 private object GetDecimalValueTds50 (byte precision, byte scale)
1032 int[] bits = new int[4] {0,0,0,0};
1034 int len = (comm.GetByte() & 0xff);
1036 return DBNull.Value;
1038 byte[] dec_bytes=comm.GetBytes(len,false);
1040 byte[] easy=new byte[4];
1042 bool positive = dec_bytes[0]==1;
1045 throw new OverflowException ();
1047 for (int i = 1, index = 0; i < len && i < 16; i +=
1049 for(int j=0; j<4; j++)
1051 easy[j]=dec_bytes[len-
1055 if(!BitConverter.IsLittleEndian)
1056 easy=comm.Swap(easy);
1057 bits[index] = BitConverter.ToInt32(easy,0);
1060 return new TdsBigDecimal (precision,
1061 scale, positive, bits);
1063 return new Decimal(bits[0], bits[1], bits
1064 [2], positive, scale);
1068 private object GetFloatValue (
1070 TdsColumnType? columnType
1072 TdsColumnType columnType
1077 if (columnType == null)
1078 throw new ArgumentNullException ("columnType");
1082 switch (columnType) {
1083 case TdsColumnType.Real:
1086 case TdsColumnType.Float8:
1089 case TdsColumnType.FloatN:
1090 columnSize = comm.GetByte ();
1094 switch (columnSize) {
1096 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1098 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1100 return DBNull.Value;
1104 private object GetImageValue ()
1106 byte hasValue = comm.GetByte ();
1109 return DBNull.Value;
1112 int len = comm.GetTdsInt ();
1115 return DBNull.Value;
1117 return (comm.GetBytes (len, true));
1120 private object GetIntValue (
1132 throw new ArgumentNullException ("type");
1135 case TdsColumnType.BigInt :
1138 case TdsColumnType.IntN :
1139 len = comm.GetByte ();
1141 case TdsColumnType.Int4 :
1144 case TdsColumnType.Int2 :
1147 case TdsColumnType.Int1 :
1151 return DBNull.Value;
1156 return (comm.GetTdsInt64 ());
1158 return (comm.GetTdsInt ());
1160 return (comm.GetTdsShort ());
1162 return (comm.GetByte ());
1164 return DBNull.Value;
1168 private object GetMoneyValue (
1180 throw new ArgumentNullException ("type");
1183 case TdsColumnType.SmallMoney :
1184 case TdsColumnType.Money4 :
1187 case TdsColumnType.Money :
1190 case TdsColumnType.MoneyN :
1191 len = comm.GetByte ();
1194 return DBNull.Value;
1199 int val = Comm.GetTdsInt ();
1200 bool negative = val < 0;
1203 return new Decimal (val, 0, 0, negative, 4);
1206 int hi = Comm.GetTdsInt ();
1207 int lo = Comm.GetTdsInt ();
1208 bool negative = hi < 0;
1214 return new Decimal (lo, hi, 0, negative, 4);
1216 return DBNull.Value;
1220 protected object GetStringValue (
1222 TdsColumnType? colType,
1224 TdsColumnType colType,
1226 bool wideChars, bool outputParam, Encoding encoder)
1228 bool shortLen = false;
1229 Encoding enc = encoder;
1231 if (tdsVersion > TdsVersion.tds70 && outputParam &&
1232 (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
1233 colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
1234 colType == TdsColumnType.NVarChar)) {
1235 // Read collation for SqlServer 2000 and beyond
1237 collation = Comm.GetBytes (5, true);
1238 enc = TdsCharset.GetEncoding (collation);
1241 shortLen = (tdsVersion >= TdsVersion.tds70) && (wideChars || !outputParam);
1244 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1245 return GetStringValue (wideChars, len, enc);
1248 protected object GetStringValue (bool wideChars, int len, Encoding enc)
1250 if (tdsVersion < TdsVersion.tds70 && len == 0)
1251 return DBNull.Value;
1253 else if (len >= 0) {
1256 result = comm.GetString (len / 2, enc);
1258 result = comm.GetString (len, false, enc);
1259 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1260 result = string.Empty;
1264 return DBNull.Value;
1267 protected int GetSubPacketLength ()
1269 return comm.GetTdsShort ();
1272 private object GetTextValue (bool wideChars, Encoding encoder)
1274 string result = null;
1275 byte hasValue = comm.GetByte ();
1278 return DBNull.Value;
1280 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1283 int len = comm.GetTdsInt ();
1285 //if the len is 0 , then the string can be a '' string
1286 // this method is called only for Text and NText. Hence will
1287 // return a empty string
1289 return string.Empty;
1292 result = comm.GetString (len / 2, encoder);
1294 result = comm.GetString (len, false, encoder);
1297 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1298 result = string.Empty;
1303 internal bool IsBlobType (TdsColumnType columnType)
1305 return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1308 internal bool IsLargeType (TdsColumnType columnType)
1310 return ((byte) columnType > 128);
1313 protected bool IsWideType (TdsColumnType columnType)
1315 switch (columnType) {
1316 case TdsColumnType.NChar:
1317 case TdsColumnType.NText:
1318 case TdsColumnType.NVarChar:
1325 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1327 switch (columnType) {
1328 case TdsColumnType.Int1 :
1329 case TdsColumnType.Int2 :
1330 case TdsColumnType.Int4 :
1331 case TdsColumnType.BigInt :
1332 case TdsColumnType.Float8 :
1333 case TdsColumnType.DateTime :
1334 case TdsColumnType.Bit :
1335 case TdsColumnType.Money :
1336 case TdsColumnType.Money4 :
1337 case TdsColumnType.SmallMoney :
1338 case TdsColumnType.Real :
1339 case TdsColumnType.DateTime4 :
1341 case TdsColumnType.Decimal:
1342 case TdsColumnType.Numeric:
1350 protected void LoadRow ()
1352 if (SequentialAccess) {
1356 isResultRead = true;
1360 currentRow = new TdsDataRow ();
1363 foreach (TdsDataColumn column in columns) {
1365 object o = GetColumnValue (column.ColumnType, false, i);
1367 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1371 outputParameters.Add (o);
1373 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1374 currentRow.BigDecimalIndex = i;
1379 internal static int LookupBufferSize (TdsColumnType columnType)
1381 switch (columnType) {
1382 case TdsColumnType.Int1 :
1383 case TdsColumnType.Bit :
1385 case TdsColumnType.Int2 :
1387 case TdsColumnType.Int4 :
1388 case TdsColumnType.Real :
1389 case TdsColumnType.DateTime4 :
1390 case TdsColumnType.Money4 :
1391 case TdsColumnType.SmallMoney :
1393 case TdsColumnType.Float8 :
1394 case TdsColumnType.DateTime :
1395 case TdsColumnType.Money :
1396 case TdsColumnType.BigInt :
1403 protected internal int ProcessAuthentication ()
1405 int pdu_size = Comm.GetTdsShort ();
1406 byte[] msg2 = Comm.GetBytes (pdu_size, true);
1408 Type2Message t2 = new Type2Message (msg2);
1409 // 0x0001 Negotiate Unicode
1410 // 0x0200 Negotiate NTLM
1411 // 0x8000 Negotiate Always Sign
1413 Type3Message t3 = new Type3Message ();
1414 t3.Challenge = t2.Nonce;
1416 t3.Domain = this.connectionParms.DefaultDomain;
1417 t3.Host = this.connectionParms.Hostname;
1418 t3.Username = this.connectionParms.User;
1419 t3.Password = this.connectionParms.Password;
1421 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1422 Comm.Append (t3.GetBytes ());
1425 } catch (IOException ex) {
1427 throw new TdsInternalException ("Server closed the connection.", ex);
1429 return 1; // TDS_SUCCEED
1432 protected void ProcessColumnDetail ()
1434 int len = GetSubPacketLength ();
1435 byte[] values = new byte[3];
1436 int columnNameLength;
1437 string baseColumnName = String.Empty;
1440 while (position < len) {
1441 for (int j = 0; j < 3; j += 1)
1442 values[j] = comm.GetByte ();
1445 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1447 if (tdsVersion >= TdsVersion.tds70) {
1448 columnNameLength = comm.GetByte ();
1449 position += 2 * columnNameLength + 1;
1452 columnNameLength = comm.GetByte ();
1453 position += columnNameLength + 1;
1455 baseColumnName = comm.GetString (columnNameLength);
1458 byte index = (byte) (values[0] - (byte) 1);
1459 byte tableIndex = (byte) (values[1] - (byte) 1);
1460 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1462 TdsDataColumn column = columns [index];
1464 column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1465 column.IsExpression = isExpression;
1466 column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1467 column.IsAliased = isAlias;
1468 column.BaseColumnName = ((isAlias) ? baseColumnName : null);
1469 column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null);
1471 column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0);
1472 column ["IsExpression"] = isExpression;
1473 column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0);
1474 column ["IsAliased"] = isAlias;
1475 column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1476 column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1481 protected abstract TdsDataColumnCollection ProcessColumnInfo ();
1483 protected void ProcessColumnNames ()
1485 columnNames = new ArrayList ();
1487 int totalLength = comm.GetTdsShort ();
1491 while (bytesRead < totalLength) {
1492 int columnNameLength = comm.GetByte ();
1493 string columnName = comm.GetString (columnNameLength);
1494 bytesRead = bytesRead + 1 + columnNameLength;
1495 columnNames.Add (columnName);
1500 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1501 protected void ProcessEndToken (TdsPacketSubType type)
1503 byte status = Comm.GetByte ();
1505 byte op = comm.GetByte ();
1508 int rowCount = comm.GetTdsInt ();
1509 bool validRowCount = IsValidRowCount (status,op);
1510 moreResults = ((status & 0x01) != 0);
1511 bool cancelled = ((status & 0x20) != 0);
1514 case TdsPacketSubType.DoneProc:
1516 goto case TdsPacketSubType.Done;
1517 case TdsPacketSubType.Done:
1518 case TdsPacketSubType.DoneInProc:
1519 if (validRowCount) {
1520 if (recordsAffected == -1)
1521 recordsAffected = rowCount;
1523 recordsAffected += rowCount;
1529 queryInProgress = false;
1531 cancelsProcessed += 1;
1532 if (messages.Count > 0 && !moreResults)
1533 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1536 protected void ProcessEnvironmentChange ()
1538 // VARADHAN: TDS 8 Debugging
1539 //Console.WriteLine ("In ProcessEnvironmentChange... entry");
1540 int len = GetSubPacketLength ();
1541 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1545 case TdsEnvPacketSubType.BlockSize :
1547 cLen = comm.GetByte ();
1548 blockSize = comm.GetString (cLen);
1550 if (tdsVersion >= TdsVersion.tds70)
1551 comm.Skip (len - 2 - cLen * 2);
1553 comm.Skip (len - 2 - cLen);
1555 packetSize = Int32.Parse (blockSize);
1556 comm.ResizeOutBuf (packetSize);
1558 case TdsEnvPacketSubType.CharSet :
1559 cLen = comm.GetByte ();
1560 if (tdsVersion == TdsVersion.tds70) {
1561 SetCharset (comm.GetString (cLen));
1562 comm.Skip (len - 2 - cLen * 2);
1565 SetCharset (comm.GetString (cLen));
1566 comm.Skip (len - 2 - cLen);
1570 case TdsEnvPacketSubType.Locale :
1571 cLen = comm.GetByte ();
1573 if (tdsVersion >= TdsVersion.tds70) {
1574 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1575 comm.Skip (len - 2 - cLen * 2);
1578 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1579 comm.Skip (len - 2 - cLen);
1581 locale = new CultureInfo (lcid);
1583 case TdsEnvPacketSubType.Database :
1584 cLen = comm.GetByte ();
1585 string newDB = comm.GetString (cLen);
1586 cLen = comm.GetByte () & 0xff;
1587 comm.GetString (cLen);
1588 if (originalDatabase == string.Empty)
1589 originalDatabase = newDB;
1592 case TdsEnvPacketSubType.CollationInfo:
1593 cLen = comm.GetByte ();
1594 collation = comm.GetBytes (cLen, true);
1595 lcid = TdsCollation.LCID (collation);
1596 locale = new CultureInfo (lcid);
1597 SetCharset (TdsCharset.GetEncoding (collation));
1601 comm.Skip (len - 1);
1604 // VARADHAN: TDS 8 Debugging
1605 //Console.WriteLine ("In ProcessEnvironmentChange... exit");
1608 protected void ProcessLoginAck ()
1610 uint srvVersion = 0;
1611 GetSubPacketLength ();
1613 // Valid only for a Login7 request
1614 if (tdsVersion >= TdsVersion.tds70) {
1616 srvVersion = (uint)comm.GetTdsInt ();
1618 switch (srvVersion) {
1620 tdsVersion = TdsVersion.tds70;
1623 tdsVersion = TdsVersion.tds80;
1626 tdsVersion = TdsVersion.tds81;
1629 tdsVersion = TdsVersion.tds90;
1634 if (tdsVersion >= TdsVersion.tds70) {
1635 int nameLength = comm.GetByte ();
1636 databaseProductName = comm.GetString (nameLength);
1637 databaseMajorVersion = comm.GetByte ();
1638 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1639 comm.GetByte ().ToString("00"),
1640 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1643 short nameLength = comm.GetByte ();
1644 databaseProductName = comm.GetString (nameLength);
1646 databaseMajorVersion = comm.GetByte ();
1647 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1651 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1652 int last = databaseProductName.IndexOf ('\0');
1653 databaseProductName = databaseProductName.Substring (0, last);
1659 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1661 if (TdsErrorMessage != null)
1662 TdsErrorMessage (this, e);
1665 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1667 if (TdsInfoMessage != null)
1668 TdsInfoMessage (this, e);
1672 protected void ProcessMessage (TdsPacketSubType subType)
1674 GetSubPacketLength ();
1676 int number = comm.GetTdsInt ();
1677 byte state = comm.GetByte ();
1678 byte theClass = comm.GetByte ();
1684 bool isError = false;
1686 if (subType == TdsPacketSubType.EED) {
1687 isError = (theClass > 10);
1688 comm.Skip (comm.GetByte ()); // SQL State
1689 comm.Skip (1); // Status
1690 comm.Skip (2); // TranState
1692 isError = (subType == TdsPacketSubType.Error);
1694 message = comm.GetString (comm.GetTdsShort ());
1695 server = comm.GetString (comm.GetByte ());
1696 procedure = comm.GetString (comm.GetByte ());
1697 lineNumber = comm.GetByte ();
1699 source = String.Empty; // FIXME
1702 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1704 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1707 protected virtual void ProcessOutputParam ()
1709 GetSubPacketLength ();
1710 /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1713 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1714 object value = GetColumnValue (colType, true);
1715 outputParameters.Add (value);
1718 protected void ProcessDynamic ()
1721 /*byte type =*/ Comm.GetByte ();
1722 /*byte status =*/ Comm.GetByte ();
1723 /*string id =*/ Comm.GetString (Comm.GetByte ());
1726 protected virtual TdsPacketSubType ProcessSubPacket ()
1728 // VARADHAN: TDS 8 Debugging
1729 // Console.WriteLine ("In ProcessSubPacket... entry");
1731 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1733 // VARADHAN: TDS 8 Debugging
1734 //Console.WriteLine ("Subpacket type: {0}", subType);
1736 case TdsPacketSubType.Dynamic2:
1737 comm.Skip (comm.GetTdsInt ());
1739 case TdsPacketSubType.AltName:
1740 case TdsPacketSubType.AltFormat:
1741 case TdsPacketSubType.Capability:
1742 case TdsPacketSubType.ParamFormat:
1743 comm.Skip (comm.GetTdsShort ());
1745 case TdsPacketSubType.Dynamic:
1748 case TdsPacketSubType.EnvironmentChange:
1749 ProcessEnvironmentChange ();
1751 case TdsPacketSubType.Info: // TDS 4.2/7.0
1752 case TdsPacketSubType.EED: // TDS 5.0
1753 case TdsPacketSubType.Error: // TDS 4.2/7.0
1754 ProcessMessage (subType);
1756 case TdsPacketSubType.Param:
1757 ProcessOutputParam ();
1759 case TdsPacketSubType.LoginAck:
1762 case TdsPacketSubType.Authentication: // TDS 7.0
1763 ProcessAuthentication ();
1765 case TdsPacketSubType.ReturnStatus :
1766 ProcessReturnStatus ();
1768 case TdsPacketSubType.ProcId:
1771 case TdsPacketSubType.Done:
1772 case TdsPacketSubType.DoneProc:
1773 case TdsPacketSubType.DoneInProc:
1774 ProcessEndToken (subType);
1776 case TdsPacketSubType.ColumnName:
1778 ProcessColumnNames ();
1780 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1781 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1782 case TdsPacketSubType.RowFormat: // TDS 5.0
1783 columns = ProcessColumnInfo ();
1785 case TdsPacketSubType.ColumnDetail:
1786 ProcessColumnDetail ();
1788 case TdsPacketSubType.TableName:
1789 ProcessTableName ();
1791 case TdsPacketSubType.ColumnOrder:
1792 comm.Skip (comm.GetTdsShort ());
1794 case TdsPacketSubType.Control:
1795 comm.Skip (comm.GetTdsShort ());
1797 case TdsPacketSubType.Row:
1802 // VARADHAN: TDS 8 Debugging
1803 //Console.WriteLine ("In ProcessSubPacket... exit");
1807 protected void ProcessTableName ()
1809 tableNames = new ArrayList ();
1810 int totalLength = comm.GetTdsShort ();
1814 while (position < totalLength) {
1815 if (tdsVersion >= TdsVersion.tds70) {
1816 len = comm.GetTdsShort ();
1817 position += 2 * (len + 1);
1820 len = comm.GetByte ();
1821 position += len + 1;
1823 tableNames.Add (comm.GetString (len));
1827 protected void SetCharset (Encoding encoder)
1829 comm.Encoder = encoder;
1832 protected void SetCharset (string charset)
1834 if (charset == null || charset.Length > 30)
1837 if (this.charset != null && this.charset == charset)
1840 if (charset.StartsWith ("cp")) {
1841 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1842 this.charset = charset;
1845 encoder = Encoding.GetEncoding ("iso-8859-1");
1846 this.charset = "iso_1";
1848 SetCharset (encoder);
1851 protected void SetLanguage (string language)
1853 if (language == null || language.Length > 30)
1854 language = "us_english";
1856 this.language = language;
1859 protected virtual void ProcessReturnStatus ()
1864 #endregion // Private Methods
1867 #region asynchronous methods
1868 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1869 AsyncCallback callback, object state)
1873 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1874 ar.TdsAsyncState.WantResults = wantResults;
1876 Comm.StartPacket (TdsPacketType.Query);
1880 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1882 } catch (IOException ex) {
1884 throw new TdsInternalException ("Server closed the connection.", ex);
1890 protected void EndExecuteQueryInternal (IAsyncResult ar)
1892 if (!ar.IsCompleted)
1893 ar.AsyncWaitHandle.WaitOne ();
1894 TdsAsyncResult result = (TdsAsyncResult) ar;
1895 if (result.IsCompletedWithException)
1896 throw result.Exception;
1899 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1901 TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1902 TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1905 Comm.EndReadPacket (ar);
1906 if (!tdsState.WantResults)
1908 } catch (Exception e) {
1909 result.MarkComplete (e);
1912 result.MarkComplete ();
1916 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1917 TdsMetaParameterCollection parameters,
1918 AsyncCallback callback,
1921 // abstract, kept to be backward compatiable.
1922 throw new NotImplementedException ("should not be called!");
1925 public virtual void EndExecuteNonQuery (IAsyncResult ar)
1928 throw new NotImplementedException ("should not be called!");
1931 public virtual IAsyncResult BeginExecuteQuery (string sql,
1932 TdsMetaParameterCollection parameters,
1933 AsyncCallback callback,
1936 // abstract, kept to be backward compatiable.
1937 throw new NotImplementedException ("should not be called!");
1940 public virtual void EndExecuteQuery (IAsyncResult ar)
1943 throw new NotImplementedException ("should not be called!");
1946 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
1950 TdsMetaParameterCollection parameters,
1951 AsyncCallback callback,
1954 throw new NotImplementedException ("should not be called!");
1957 public virtual void EndExecuteProcedure (IAsyncResult ar)
1960 throw new NotImplementedException ("should not be called!");
1963 public void WaitFor (IAsyncResult ar)
1965 if (! ar.IsCompleted)
1966 ar.AsyncWaitHandle.WaitOne ();
1969 public void CheckAndThrowException (IAsyncResult ar)
1971 TdsAsyncResult result = (TdsAsyncResult) ar;
1972 if (result.IsCompleted && result.IsCompletedWithException)
1973 throw result.Exception;
1976 #endregion // asynchronous methods