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 public TdsVersion ServerTdsVersion {
207 switch (databaseMajorVersion) {
208 case 4: return TdsVersion.tds42;
209 case 5: return TdsVersion.tds50;
210 case 7: return TdsVersion.tds70;
211 case 8: return TdsVersion.tds80;
212 case 9: return TdsVersion.tds90;
213 case 10: return TdsVersion.tds100;
214 default: return tdsVersion; // return client's version
219 private void SkipRow ()
221 SkipToColumnIndex (Columns.Count);
224 StreamColumnIndex = 0;
226 LoadInProgress = false;
229 private void SkipToColumnIndex (int colIndex)
234 if (colIndex < StreamColumnIndex)
235 throw new Exception ("Cannot Skip to a colindex less than the curr index");
237 while (colIndex != StreamColumnIndex) {
239 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
241 throw new Exception ("Column type unset.");
243 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
245 if (!(colType == TdsColumnType.Image ||
246 colType == TdsColumnType.Text ||
247 colType == TdsColumnType.NText)) {
248 GetColumnValue (colType, false, StreamColumnIndex);
249 StreamColumnIndex ++;
253 Comm.Skip (StreamLength);
260 public object GetSequentialColumnValue (int colIndex)
262 if (colIndex < StreamColumnIndex)
263 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
268 if (colIndex != StreamColumnIndex)
269 SkipToColumnIndex (colIndex);
272 object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
274 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
280 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
282 if (colIndex < StreamColumnIndex)
283 throw new InvalidOperationException ("Invalid attempt to read from column ordinal" + colIndex);
285 if (colIndex != StreamColumnIndex)
286 SkipToColumnIndex (colIndex);
288 if (!LoadInProgress) {
290 BeginLoad (Columns[colIndex].ColumnType);
292 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
298 return LoadData (fieldIndex, buffer, bufferIndex, size);
299 } catch (IOException ex) {
301 throw new TdsInternalException ("Server closed the connection.", ex);
305 private void BeginLoad (
307 TdsColumnType? colType
309 TdsColumnType colType
320 throw new ArgumentNullException ("colType");
324 case TdsColumnType.Text :
325 case TdsColumnType.NText:
326 case TdsColumnType.Image:
327 if (Comm.GetByte () != 0) {
329 StreamLength = Comm.GetTdsInt ();
331 // use -2 to indicate that we're dealing
336 case TdsColumnType.BigVarChar:
337 case TdsColumnType.BigChar:
338 case TdsColumnType.BigBinary:
339 case TdsColumnType.BigVarBinary:
341 StreamLength = Comm.GetTdsShort ();
343 case TdsColumnType.VarChar :
344 case TdsColumnType.NVarChar :
345 case TdsColumnType.Char:
346 case TdsColumnType.NChar:
347 case TdsColumnType.Binary:
348 case TdsColumnType.VarBinary:
349 StreamLength = Comm.GetTdsShort ();
357 LoadInProgress = true;
360 private void EndLoad()
362 if (StreamLength > 0)
363 Comm.Skip (StreamLength);
367 LoadInProgress = false;
370 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
372 if (StreamLength <= 0)
375 if (fieldIndex < StreamIndex)
376 throw new InvalidOperationException (string.Format (
377 "Attempting to read at dataIndex '{0}' is " +
378 "not allowed as this is less than the " +
379 "current position. You must read from " +
380 "dataIndex '{1}' or greater.",
381 fieldIndex, StreamIndex));
383 if (fieldIndex >= (StreamLength + StreamIndex))
386 // determine number of bytes to skip
387 int skip = (int) (fieldIndex - StreamIndex);
390 // update the current position
391 StreamIndex += (fieldIndex - StreamIndex);
392 // update the remaining length
393 StreamLength -= skip;
395 // Load the reqd amt of bytes
396 int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
397 byte[] arr = Comm.GetBytes (loadlen, true);
399 // update the index and stream length
400 StreamIndex += loadlen + (fieldIndex - StreamIndex);
401 StreamLength -= loadlen;
402 arr.CopyTo (buffer, bufferIndex);
407 #endregion // Properties
411 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
412 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
418 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
420 this.tdsVersion = tdsVersion;
421 this.packetSize = packetSize;
422 this.dataSource = dataSource;
423 this.columns = new TdsDataColumnCollection ();
425 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
428 #endregion // Constructors
430 #region Public Methods
432 internal protected void InitExec ()
438 // Reset "read" status variables - used in case of SequentialAccess
439 isResultRead = false;
443 StreamColumnIndex = 0;
444 LoadInProgress = false;
446 // Reset more variables
447 queryInProgress = false;
448 cancelsRequested = 0;
449 cancelsProcessed = 0;
450 recordsAffected = -1;
453 outputParameters.Clear ();
456 public void Cancel ()
458 if (queryInProgress) {
459 if (cancelsRequested == cancelsProcessed) {
460 comm.StartPacket (TdsPacketType.Cancel);
463 } catch (IOException ex) {
465 throw new TdsInternalException ("Server closed the connection.", ex);
467 cancelsRequested += 1;
472 public abstract bool Connect (TdsConnectionParameters connectionParameters);
474 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
476 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
477 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
480 public void Disconnect ()
483 comm.StartPacket (TdsPacketType.Logoff);
484 comm.Append ((byte) 0);
487 // We're closing the socket anyway
493 public virtual bool Reset ()
495 database = originalDatabase;
499 protected virtual bool IsValidRowCount (byte status, byte op)
501 return ((status & (0x10)) != 0) ;
504 public void Execute (string sql)
506 Execute (sql, null, 0, false);
509 public void ExecProc (string sql)
511 ExecProc (sql, null, 0, false);
514 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
516 ExecuteQuery (sql, timeout, wantResults);
519 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
521 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
524 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
526 throw new NotSupportedException ();
529 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
534 CheckForData (timeout);
537 } catch (IOException ex) {
539 throw new TdsInternalException ("Server closed the connection.", ex);
543 internal void ExecBulkCopy (int timeout, bool wantResults)
548 CheckForData (timeout);
551 } catch (IOException ex) {
553 throw new TdsInternalException ("Server closed the connection.", ex);
557 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
561 Comm.StartPacket (TdsPacketType.Query);
565 CheckForData (timeout);
568 } catch (IOException ex) {
570 throw new TdsInternalException ("Server closed the connection.", ex);
574 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
575 int timeout, bool wantResults)
577 Comm.StartPacket (TdsPacketType.DBRPC);
579 byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
580 byte rpcNameLength = (byte) rpcNameBytes.Length;
581 ushort mask = 0x0000;
582 ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
585 Comm.Append (packetLength);
586 Comm.Append (rpcNameLength);
587 Comm.Append (rpcNameBytes);
592 CheckForData (timeout);
595 } catch (IOException ex) {
597 throw new TdsInternalException ("Server closed the connection.", ex);
601 public bool NextResult ()
603 if (SequentialAccess) {
605 while (NextRow ()) {}
607 isResultRead = false;
613 TdsPacketSubType subType;
616 bool outputParams = false;
619 subType = ProcessSubPacket ();
625 case TdsPacketSubType.ColumnInfo:
626 case TdsPacketSubType.ColumnMetadata:
627 case TdsPacketSubType.RowFormat:
628 byte peek = Comm.Peek ();
629 done = (peek != (byte) TdsPacketSubType.TableName);
630 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
635 case TdsPacketSubType.TableName:
638 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
640 case TdsPacketSubType.ColumnDetail:
652 public bool NextRow ()
654 if (SequentialAccess) {
661 TdsPacketSubType subType;
666 subType = ProcessSubPacket ();
668 case TdsPacketSubType.Row:
672 case TdsPacketSubType.Done:
673 case TdsPacketSubType.DoneProc:
674 case TdsPacketSubType.DoneInProc:
684 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
686 throw new NotSupportedException ();
689 public void SkipToEnd ()
692 while (NextResult ()) { /* DO NOTHING */ }
693 } catch (IOException ex) {
695 throw new TdsInternalException ("Server closed the connection.", ex);
699 public virtual void Unprepare (string statementId)
701 throw new NotSupportedException ();
704 #endregion // Public Methods
706 #region // Private Methods
708 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
709 protected void CheckForData (int timeout)
711 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
713 throw CreateTimeoutException (dataSource, "CheckForData()");
717 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
719 return new TdsInternalInfoMessageEventArgs (errors);
722 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
724 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
727 private Encoding GetEncodingFromColumnCollation (int lcid, int sortId)
730 return TdsCharset.GetEncodingFromSortOrder (sortId);
732 return TdsCharset.GetEncodingFromLCID (lcid);
735 protected object GetColumnValue (
737 TdsColumnType? colType,
739 TdsColumnType colType,
743 return GetColumnValue (colType, outParam, -1);
746 private object GetColumnValue (
748 TdsColumnType? colType,
750 TdsColumnType colType,
752 bool outParam, int ordinal)
755 object element = null;
757 int lcid = 0, sortId = 0;
761 throw new ArgumentNullException ("colType");
763 if (ordinal > -1 && tdsVersion > TdsVersion.tds70) {
765 lcid = (int) columns[ordinal].LCID;
766 sortId = (int) columns[ordinal].SortOrder;
768 lcid = (int) columns[ordinal]["LCID"];
769 sortId = (int) columns[ordinal]["SortOrder"];
774 case TdsColumnType.IntN :
777 element = GetIntValue (colType);
779 case TdsColumnType.Int1 :
780 case TdsColumnType.Int2 :
781 case TdsColumnType.Int4 :
782 case TdsColumnType.BigInt :
783 element = GetIntValue (colType);
785 case TdsColumnType.Image :
788 element = GetImageValue ();
790 case TdsColumnType.Text :
791 enc = GetEncodingFromColumnCollation (lcid, sortId);
794 element = GetTextValue (false, enc);
796 case TdsColumnType.NText :
797 enc = GetEncodingFromColumnCollation (lcid, sortId);
800 element = GetTextValue (true, enc);
802 case TdsColumnType.Char :
803 case TdsColumnType.VarChar :
804 enc = GetEncodingFromColumnCollation (lcid, sortId);
807 element = GetStringValue (colType, false, outParam, enc);
809 case TdsColumnType.BigVarBinary :
812 len = comm.GetTdsShort ();
813 element = comm.GetBytes (len, true);
816 case TdsColumnType.BigBinary :
819 len = comm.GetTdsShort ();
820 element = comm.GetBytes (len, true);
823 case TdsColumnType.BigBinary :
826 element = GetBinaryValue ();
828 case TdsColumnType.BigChar :
829 case TdsColumnType.BigVarChar :
830 enc = GetEncodingFromColumnCollation (lcid, sortId);
833 element = GetStringValue (colType, false, outParam, enc);
835 case TdsColumnType.NChar :
836 case TdsColumnType.BigNVarChar :
837 enc = GetEncodingFromColumnCollation (lcid, sortId);
840 element = GetStringValue (colType, true, outParam, enc);
842 case TdsColumnType.NVarChar :
843 enc = GetEncodingFromColumnCollation (lcid, sortId);
846 element = GetStringValue (colType, true, outParam, enc);
848 case TdsColumnType.Real :
849 case TdsColumnType.Float8 :
850 element = GetFloatValue (colType);
852 case TdsColumnType.FloatN :
855 element = GetFloatValue (colType);
857 case TdsColumnType.SmallMoney :
858 case TdsColumnType.Money :
859 element = GetMoneyValue (colType);
861 case TdsColumnType.MoneyN :
864 element = GetMoneyValue (colType);
866 case TdsColumnType.Numeric :
867 case TdsColumnType.Decimal :
872 precision = comm.GetByte ();
873 scale = comm.GetByte ();
877 precision = (byte) columns[ordinal].NumericPrecision;
878 scale = (byte) columns[ordinal].NumericScale;
880 precision = (byte) columns[ordinal]["NumericPrecision"];
881 scale = (byte) columns[ordinal]["NumericScale"];
885 element = GetDecimalValue (precision, scale);
887 // workaround for fact that TDS 7.0 returns
888 // bigint as decimal (19,0), and client code
889 // expects it to be returned as a long
890 if (scale == 0 && precision <= 19 && tdsVersion == TdsVersion.tds70) {
891 if (!(element is System.DBNull))
892 element = Convert.ToInt64 (element);
895 case TdsColumnType.DateTimeN :
898 element = GetDateTimeValue (colType);
900 case TdsColumnType.DateTime4 :
901 case TdsColumnType.DateTime :
902 element = GetDateTimeValue (colType);
904 case TdsColumnType.VarBinary :
905 case TdsColumnType.Binary :
908 element = GetBinaryValue ();
910 case TdsColumnType.BitN :
913 if (comm.GetByte () == 0)
914 element = DBNull.Value;
916 element = (comm.GetByte() != 0);
918 case TdsColumnType.Bit :
919 int columnSize = comm.GetByte ();
920 element = (columnSize != 0);
922 case TdsColumnType.UniqueIdentifier :
923 if (comm.Peek () != 16) { // If it's null, then what to do?
924 /*byte swallowed =*/ comm.GetByte();
925 element = DBNull.Value;
931 len = comm.GetByte () & 0xff;
933 byte[] guidBytes = comm.GetBytes (len, true);
934 if (!BitConverter.IsLittleEndian) {
935 byte[] swappedguidBytes = new byte[len];
936 for (int i = 0; i < 4; i++)
937 swappedguidBytes[i] = guidBytes[4-i-1];
938 for (int i = 4; i < 6; i++)
939 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
940 for (int i = 6; i < 8; i++)
941 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
942 for (int i = 8; i < 16; i++)
943 swappedguidBytes[i] = guidBytes[i];
944 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
946 element = new Guid (guidBytes);
955 private object GetBinaryValue ()
958 object result = DBNull.Value;
960 if (tdsVersion >= TdsVersion.tds70) {
961 len = comm.GetTdsShort ();
962 if (len != 0xffff && len >= 0)
963 result = comm.GetBytes (len, true);
965 len = (comm.GetByte () & 0xff);
967 result = comm.GetBytes (len, true);
973 private object GetDateTimeValue (
986 throw new ArgumentNullException ("type");
989 case TdsColumnType.DateTime4:
992 case TdsColumnType.DateTime:
995 case TdsColumnType.DateTimeN:
996 byte tmp = comm.Peek ();
997 if (tmp != 0 && tmp != 4 && tmp != 8)
999 len = comm.GetByte ();
1003 DateTime epoch = new DateTime (1900, 1, 1);
1007 result = epoch.AddDays (comm.GetTdsInt ());
1008 int seconds = comm.GetTdsInt ();
1009 long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f);
1010 if (seconds != 0 || millis != 0) {
1011 result = ((DateTime) result).AddSeconds (seconds / 300);
1012 result = ((DateTime) result).AddMilliseconds (millis);
1016 // MSDN says small datetime is stored in 2 bytes as no of days
1017 // *after* 1/1/1900. so, cast to unsigned short
1018 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
1019 short minutes = comm.GetTdsShort ();
1021 result = ((DateTime) result).AddMinutes ((int) minutes);
1024 result = DBNull.Value;
1031 private object GetDecimalValue (byte precision, byte scale)
1033 if (tdsVersion < TdsVersion.tds70)
1034 return GetDecimalValueTds50 (precision, scale);
1036 return GetDecimalValueTds70 (precision, scale);
1039 private object GetDecimalValueTds70 (byte precision, byte scale)
1041 int[] bits = new int[4] {0,0,0,0};
1043 int len = (comm.GetByte() & 0xff) - 1;
1045 return DBNull.Value;
1047 bool positive = (comm.GetByte () == 1);
1049 throw new OverflowException ();
1051 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
1052 bits[index] = comm.GetTdsInt ();
1055 return new TdsBigDecimal (precision, scale, !positive, bits);
1057 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
1060 private object GetDecimalValueTds50 (byte precision, byte scale)
1062 int[] bits = new int[4] {0,0,0,0};
1064 int len = (comm.GetByte() & 0xff);
1066 return DBNull.Value;
1068 byte[] dec_bytes=comm.GetBytes(len,false);
1070 byte[] easy=new byte[4];
1072 bool positive = dec_bytes[0]==1;
1075 throw new OverflowException ();
1077 for (int i = 1, index = 0; i < len && i < 16; i +=
1079 for(int j=0; j<4; j++)
1081 easy[j]=dec_bytes[len-
1085 if(!BitConverter.IsLittleEndian)
1086 easy=comm.Swap(easy);
1087 bits[index] = BitConverter.ToInt32(easy,0);
1090 return new TdsBigDecimal (precision,
1091 scale, positive, bits);
1093 return new Decimal(bits[0], bits[1], bits
1094 [2], positive, scale);
1098 private object GetFloatValue (
1100 TdsColumnType? columnType
1102 TdsColumnType columnType
1107 if (columnType == null)
1108 throw new ArgumentNullException ("columnType");
1112 switch (columnType) {
1113 case TdsColumnType.Real:
1116 case TdsColumnType.Float8:
1119 case TdsColumnType.FloatN:
1120 columnSize = comm.GetByte ();
1124 switch (columnSize) {
1126 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1128 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1130 return DBNull.Value;
1134 private object GetImageValue ()
1136 byte hasValue = comm.GetByte ();
1139 return DBNull.Value;
1142 int len = comm.GetTdsInt ();
1145 return DBNull.Value;
1147 return (comm.GetBytes (len, true));
1150 private object GetIntValue (
1162 throw new ArgumentNullException ("type");
1165 case TdsColumnType.BigInt :
1168 case TdsColumnType.IntN :
1169 len = comm.GetByte ();
1171 case TdsColumnType.Int4 :
1174 case TdsColumnType.Int2 :
1177 case TdsColumnType.Int1 :
1181 return DBNull.Value;
1186 return (comm.GetTdsInt64 ());
1188 return (comm.GetTdsInt ());
1190 return (comm.GetTdsShort ());
1192 return (comm.GetByte ());
1194 return DBNull.Value;
1198 private object GetMoneyValue (
1210 throw new ArgumentNullException ("type");
1213 case TdsColumnType.SmallMoney :
1214 case TdsColumnType.Money4 :
1217 case TdsColumnType.Money :
1220 case TdsColumnType.MoneyN :
1221 len = comm.GetByte ();
1224 return DBNull.Value;
1229 int val = Comm.GetTdsInt ();
1230 bool negative = val < 0;
1233 return new Decimal (val, 0, 0, negative, 4);
1236 int hi = Comm.GetTdsInt ();
1237 int lo = Comm.GetTdsInt ();
1238 bool negative = hi < 0;
1244 return new Decimal (lo, hi, 0, negative, 4);
1247 return DBNull.Value;
1251 protected object GetStringValue (
1253 TdsColumnType? colType,
1255 TdsColumnType colType,
1257 bool wideChars, bool outputParam, Encoding encoder)
1259 bool shortLen = false;
1260 Encoding enc = encoder;
1262 if (tdsVersion > TdsVersion.tds70 && outputParam &&
1263 (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
1264 colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
1265 colType == TdsColumnType.NVarChar)) {
1266 // Read collation for SqlServer 2000 and beyond
1268 collation = Comm.GetBytes (5, true);
1269 enc = TdsCharset.GetEncoding (collation);
1272 shortLen = (tdsVersion >= TdsVersion.tds70) && (wideChars || !outputParam);
1275 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1276 return GetStringValue (wideChars, len, enc);
1279 protected object GetStringValue (bool wideChars, int len, Encoding enc)
1281 if (tdsVersion < TdsVersion.tds70 && len == 0)
1282 return DBNull.Value;
1284 else if (len >= 0) {
1287 result = comm.GetString (len / 2, enc);
1289 result = comm.GetString (len, false, enc);
1290 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1291 result = string.Empty;
1295 return DBNull.Value;
1298 protected int GetSubPacketLength ()
1300 return comm.GetTdsShort ();
1303 private object GetTextValue (bool wideChars, Encoding encoder)
1305 string result = null;
1306 byte hasValue = comm.GetByte ();
1309 return DBNull.Value;
1311 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1314 int len = comm.GetTdsInt ();
1316 //if the len is 0 , then the string can be a '' string
1317 // this method is called only for Text and NText. Hence will
1318 // return a empty string
1320 return string.Empty;
1323 result = comm.GetString (len / 2, encoder);
1325 result = comm.GetString (len, false, encoder);
1328 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1329 result = string.Empty;
1334 internal bool IsBlobType (TdsColumnType columnType)
1336 return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1339 internal bool IsLargeType (TdsColumnType columnType)
1341 return ((byte) columnType > 128);
1344 protected bool IsWideType (TdsColumnType columnType)
1346 switch (columnType) {
1347 case TdsColumnType.NChar:
1348 case TdsColumnType.NText:
1349 case TdsColumnType.NVarChar:
1356 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1358 switch (columnType) {
1359 case TdsColumnType.Int1 :
1360 case TdsColumnType.Int2 :
1361 case TdsColumnType.Int4 :
1362 case TdsColumnType.BigInt :
1363 case TdsColumnType.Float8 :
1364 case TdsColumnType.DateTime :
1365 case TdsColumnType.Bit :
1366 case TdsColumnType.Money :
1367 case TdsColumnType.Money4 :
1368 case TdsColumnType.SmallMoney :
1369 case TdsColumnType.Real :
1370 case TdsColumnType.DateTime4 :
1372 case TdsColumnType.Decimal:
1373 case TdsColumnType.Numeric:
1381 protected void LoadRow ()
1383 if (SequentialAccess) {
1387 isResultRead = true;
1391 currentRow = new TdsDataRow ();
1394 foreach (TdsDataColumn column in columns) {
1396 object o = GetColumnValue (column.ColumnType, false, i);
1398 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1402 outputParameters.Add (o);
1404 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1405 currentRow.BigDecimalIndex = i;
1410 internal static int LookupBufferSize (TdsColumnType columnType)
1412 switch (columnType) {
1413 case TdsColumnType.Int1 :
1414 case TdsColumnType.Bit :
1416 case TdsColumnType.Int2 :
1418 case TdsColumnType.Int4 :
1419 case TdsColumnType.Real :
1420 case TdsColumnType.DateTime4 :
1421 case TdsColumnType.Money4 :
1422 case TdsColumnType.SmallMoney :
1424 case TdsColumnType.Float8 :
1425 case TdsColumnType.DateTime :
1426 case TdsColumnType.Money :
1427 case TdsColumnType.BigInt :
1434 protected internal int ProcessAuthentication ()
1436 int pdu_size = Comm.GetTdsShort ();
1437 byte[] msg2 = Comm.GetBytes (pdu_size, true);
1439 Type2Message t2 = new Type2Message (msg2);
1440 // 0x0001 Negotiate Unicode
1441 // 0x0200 Negotiate NTLM
1442 // 0x8000 Negotiate Always Sign
1444 Type3Message t3 = new Type3Message ();
1445 t3.Challenge = t2.Nonce;
1447 t3.Domain = this.connectionParms.DefaultDomain;
1448 t3.Host = this.connectionParms.Hostname;
1449 t3.Username = this.connectionParms.User;
1450 t3.Password = this.connectionParms.Password;
1452 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1453 Comm.Append (t3.GetBytes ());
1456 } catch (IOException ex) {
1458 throw new TdsInternalException ("Server closed the connection.", ex);
1460 return 1; // TDS_SUCCEED
1463 protected void ProcessColumnDetail ()
1465 int len = GetSubPacketLength ();
1466 byte[] values = new byte[3];
1467 int columnNameLength;
1468 string baseColumnName = String.Empty;
1471 while (position < len) {
1472 for (int j = 0; j < 3; j += 1)
1473 values[j] = comm.GetByte ();
1476 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1478 if (tdsVersion >= TdsVersion.tds70) {
1479 columnNameLength = comm.GetByte ();
1480 position += 2 * columnNameLength + 1;
1483 columnNameLength = comm.GetByte ();
1484 position += columnNameLength + 1;
1486 baseColumnName = comm.GetString (columnNameLength);
1489 byte index = (byte) (values[0] - (byte) 1);
1490 byte tableIndex = (byte) (values[1] - (byte) 1);
1491 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1493 TdsDataColumn column = columns [index];
1495 column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1496 column.IsExpression = isExpression;
1497 column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1498 column.IsAliased = isAlias;
1499 column.BaseColumnName = ((isAlias) ? baseColumnName : null);
1500 column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null);
1502 column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0);
1503 column ["IsExpression"] = isExpression;
1504 column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0);
1505 column ["IsAliased"] = isAlias;
1506 column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1507 column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1512 protected abstract void ProcessColumnInfo ();
1514 protected void ProcessColumnNames ()
1516 columnNames = new ArrayList ();
1518 int totalLength = comm.GetTdsShort ();
1522 while (bytesRead < totalLength) {
1523 int columnNameLength = comm.GetByte ();
1524 string columnName = comm.GetString (columnNameLength);
1525 bytesRead = bytesRead + 1 + columnNameLength;
1526 columnNames.Add (columnName);
1531 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1532 protected void ProcessEndToken (TdsPacketSubType type)
1534 byte status = Comm.GetByte ();
1536 byte op = comm.GetByte ();
1539 int rowCount = comm.GetTdsInt ();
1540 bool validRowCount = IsValidRowCount (status,op);
1541 moreResults = ((status & 0x01) != 0);
1542 bool cancelled = ((status & 0x20) != 0);
1545 case TdsPacketSubType.DoneProc:
1547 goto case TdsPacketSubType.Done;
1548 case TdsPacketSubType.Done:
1549 case TdsPacketSubType.DoneInProc:
1550 if (validRowCount) {
1551 if (recordsAffected == -1)
1552 recordsAffected = rowCount;
1554 recordsAffected += rowCount;
1560 queryInProgress = false;
1562 cancelsProcessed += 1;
1563 if (messages.Count > 0 && !moreResults)
1564 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1567 protected void ProcessEnvironmentChange ()
1569 // VARADHAN: TDS 8 Debugging
1570 //Console.WriteLine ("In ProcessEnvironmentChange... entry");
1571 int len = GetSubPacketLength ();
1572 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1576 case TdsEnvPacketSubType.BlockSize :
1578 cLen = comm.GetByte ();
1579 blockSize = comm.GetString (cLen);
1581 if (tdsVersion >= TdsVersion.tds70)
1582 comm.Skip (len - 2 - cLen * 2);
1584 comm.Skip (len - 2 - cLen);
1586 packetSize = Int32.Parse (blockSize);
1587 comm.ResizeOutBuf (packetSize);
1589 case TdsEnvPacketSubType.CharSet :
1590 cLen = comm.GetByte ();
1591 if (tdsVersion == TdsVersion.tds70) {
1592 SetCharset (comm.GetString (cLen));
1593 comm.Skip (len - 2 - cLen * 2);
1596 SetCharset (comm.GetString (cLen));
1597 comm.Skip (len - 2 - cLen);
1601 case TdsEnvPacketSubType.Locale :
1602 cLen = comm.GetByte ();
1604 if (tdsVersion >= TdsVersion.tds70) {
1605 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1606 comm.Skip (len - 2 - cLen * 2);
1609 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1610 comm.Skip (len - 2 - cLen);
1612 locale = new CultureInfo (lcid);
1614 case TdsEnvPacketSubType.Database :
1615 cLen = comm.GetByte ();
1616 string newDB = comm.GetString (cLen);
1617 cLen = comm.GetByte () & 0xff;
1618 comm.GetString (cLen);
1619 if (originalDatabase == string.Empty)
1620 originalDatabase = newDB;
1623 case TdsEnvPacketSubType.CollationInfo:
1624 //Console.WriteLine ("ProcessEnvironmentChange::Got collation info");
1625 cLen = comm.GetByte ();
1626 collation = comm.GetBytes (cLen, true);
1627 lcid = TdsCollation.LCID (collation);
1628 locale = new CultureInfo (lcid);
1629 SetCharset (TdsCharset.GetEncoding (collation));
1633 comm.Skip (len - 1);
1636 // VARADHAN: TDS 8 Debugging
1637 //Console.WriteLine ("In ProcessEnvironmentChange... exit");
1640 protected void ProcessLoginAck ()
1642 uint srvVersion = 0;
1643 GetSubPacketLength ();
1645 //Console.WriteLine ("ProcessLoginAck: B4 tdsVersion:{0}", tdsVersion);
1646 // Valid only for a Login7 request
1647 if (tdsVersion >= TdsVersion.tds70) {
1649 srvVersion = (uint)comm.GetTdsInt ();
1651 //Console.WriteLine ("srvVersion: {0}", srvVersion);
1652 switch (srvVersion) {
1654 tdsVersion = TdsVersion.tds70;
1657 tdsVersion = TdsVersion.tds80;
1660 tdsVersion = TdsVersion.tds81;
1663 tdsVersion = TdsVersion.tds90;
1666 //Console.WriteLine ("ProcessLoginAck: after tdsVersion:{0}", tdsVersion);
1669 if (tdsVersion >= TdsVersion.tds70) {
1670 int nameLength = comm.GetByte ();
1671 databaseProductName = comm.GetString (nameLength);
1672 databaseMajorVersion = comm.GetByte ();
1673 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1674 comm.GetByte ().ToString("00"),
1675 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1678 short nameLength = comm.GetByte ();
1679 databaseProductName = comm.GetString (nameLength);
1681 databaseMajorVersion = comm.GetByte ();
1682 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1686 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1687 int last = databaseProductName.IndexOf ('\0');
1688 databaseProductName = databaseProductName.Substring (0, last);
1692 //Console.WriteLine ("databaseProductVersion:{0}", databaseProductVersion);
1695 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1697 if (TdsErrorMessage != null)
1698 TdsErrorMessage (this, e);
1701 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1703 if (TdsInfoMessage != null)
1704 TdsInfoMessage (this, e);
1708 protected void ProcessMessage (TdsPacketSubType subType)
1710 GetSubPacketLength ();
1712 int number = comm.GetTdsInt ();
1713 byte state = comm.GetByte ();
1714 byte theClass = comm.GetByte ();
1720 bool isError = false;
1722 if (subType == TdsPacketSubType.EED) {
1723 isError = (theClass > 10);
1724 comm.Skip (comm.GetByte ()); // SQL State
1725 comm.Skip (1); // Status
1726 comm.Skip (2); // TranState
1728 isError = (subType == TdsPacketSubType.Error);
1730 message = comm.GetString (comm.GetTdsShort ());
1731 server = comm.GetString (comm.GetByte ());
1732 procedure = comm.GetString (comm.GetByte ());
1733 lineNumber = comm.GetByte ();
1735 source = String.Empty; // FIXME
1738 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1740 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1743 protected virtual void ProcessOutputParam ()
1745 GetSubPacketLength ();
1746 /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1749 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1750 object value = GetColumnValue (colType, true);
1751 outputParameters.Add (value);
1754 protected void ProcessDynamic ()
1757 /*byte type =*/ Comm.GetByte ();
1758 /*byte status =*/ Comm.GetByte ();
1759 /*string id =*/ Comm.GetString (Comm.GetByte ());
1762 protected virtual TdsPacketSubType ProcessSubPacket ()
1764 // VARADHAN: TDS 8 Debugging
1765 // Console.WriteLine ("In ProcessSubPacket... entry");
1767 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1769 // VARADHAN: TDS 8 Debugging
1770 //Console.WriteLine ("Subpacket type: {0}", subType);
1772 case TdsPacketSubType.Dynamic2:
1773 comm.Skip (comm.GetTdsInt ());
1775 case TdsPacketSubType.AltName:
1776 case TdsPacketSubType.AltFormat:
1777 case TdsPacketSubType.Capability:
1778 case TdsPacketSubType.ParamFormat:
1779 comm.Skip (comm.GetTdsShort ());
1781 case TdsPacketSubType.Dynamic:
1784 case TdsPacketSubType.EnvironmentChange:
1785 ProcessEnvironmentChange ();
1787 case TdsPacketSubType.Info: // TDS 4.2/7.0
1788 case TdsPacketSubType.EED: // TDS 5.0
1789 case TdsPacketSubType.Error: // TDS 4.2/7.0
1790 ProcessMessage (subType);
1792 case TdsPacketSubType.Param:
1793 ProcessOutputParam ();
1795 case TdsPacketSubType.LoginAck:
1798 case TdsPacketSubType.Authentication: // TDS 7.0
1799 ProcessAuthentication ();
1801 case TdsPacketSubType.ReturnStatus :
1802 ProcessReturnStatus ();
1804 case TdsPacketSubType.ProcId:
1807 case TdsPacketSubType.Done:
1808 case TdsPacketSubType.DoneProc:
1809 case TdsPacketSubType.DoneInProc:
1810 ProcessEndToken (subType);
1812 case TdsPacketSubType.ColumnName:
1814 ProcessColumnNames ();
1816 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1817 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1818 case TdsPacketSubType.RowFormat: // TDS 5.0
1820 ProcessColumnInfo ();
1822 case TdsPacketSubType.ColumnDetail:
1823 ProcessColumnDetail ();
1825 case TdsPacketSubType.TableName:
1826 ProcessTableName ();
1828 case TdsPacketSubType.ColumnOrder:
1829 comm.Skip (comm.GetTdsShort ());
1831 case TdsPacketSubType.Control:
1832 comm.Skip (comm.GetTdsShort ());
1834 case TdsPacketSubType.Row:
1839 // VARADHAN: TDS 8 Debugging
1840 //Console.WriteLine ("In ProcessSubPacket... exit");
1844 protected void ProcessTableName ()
1846 tableNames = new ArrayList ();
1847 int totalLength = comm.GetTdsShort ();
1851 while (position < totalLength) {
1852 if (tdsVersion >= TdsVersion.tds70) {
1853 len = comm.GetTdsShort ();
1854 position += 2 * (len + 1);
1857 len = comm.GetByte ();
1858 position += len + 1;
1860 tableNames.Add (comm.GetString (len));
1864 protected void SetCharset (Encoding encoder)
1866 comm.Encoder = encoder;
1869 protected void SetCharset (string charset)
1871 if (charset == null || charset.Length > 30)
1874 if (this.charset != null && this.charset == charset)
1877 if (charset.StartsWith ("cp")) {
1878 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1879 this.charset = charset;
1882 encoder = Encoding.GetEncoding ("iso-8859-1");
1883 this.charset = "iso_1";
1885 SetCharset (encoder);
1888 protected void SetLanguage (string language)
1890 if (language == null || language.Length > 30)
1891 language = "us_english";
1893 this.language = language;
1896 protected virtual void ProcessReturnStatus ()
1901 #endregion // Private Methods
1904 #region asynchronous methods
1905 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1906 AsyncCallback callback, object state)
1910 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1911 ar.TdsAsyncState.WantResults = wantResults;
1913 Comm.StartPacket (TdsPacketType.Query);
1917 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1919 } catch (IOException ex) {
1921 throw new TdsInternalException ("Server closed the connection.", ex);
1927 protected void EndExecuteQueryInternal (IAsyncResult ar)
1929 if (!ar.IsCompleted)
1930 ar.AsyncWaitHandle.WaitOne ();
1931 TdsAsyncResult result = (TdsAsyncResult) ar;
1932 if (result.IsCompletedWithException)
1933 throw result.Exception;
1936 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1938 TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1939 TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1942 Comm.EndReadPacket (ar);
1943 if (!tdsState.WantResults)
1945 } catch (Exception e) {
1946 result.MarkComplete (e);
1949 result.MarkComplete ();
1953 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1954 TdsMetaParameterCollection parameters,
1955 AsyncCallback callback,
1958 // abstract, kept to be backward compatiable.
1959 throw new NotImplementedException ("should not be called!");
1962 public virtual void EndExecuteNonQuery (IAsyncResult ar)
1965 throw new NotImplementedException ("should not be called!");
1968 public virtual IAsyncResult BeginExecuteQuery (string sql,
1969 TdsMetaParameterCollection parameters,
1970 AsyncCallback callback,
1973 // abstract, kept to be backward compatiable.
1974 throw new NotImplementedException ("should not be called!");
1977 public virtual void EndExecuteQuery (IAsyncResult ar)
1980 throw new NotImplementedException ("should not be called!");
1983 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
1987 TdsMetaParameterCollection parameters,
1988 AsyncCallback callback,
1991 throw new NotImplementedException ("should not be called!");
1994 public virtual void EndExecuteProcedure (IAsyncResult ar)
1997 throw new NotImplementedException ("should not be called!");
2000 public void WaitFor (IAsyncResult ar)
2002 if (! ar.IsCompleted)
2003 ar.AsyncWaitHandle.WaitOne ();
2006 public void CheckAndThrowException (IAsyncResult ar)
2008 TdsAsyncResult result = (TdsAsyncResult) ar;
2009 if (result.IsCompleted && result.IsCompletedWithException)
2010 throw result.Exception;
2013 #endregion // asynchronous methods