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;
44 using System.Security;
46 using System.Runtime.InteropServices;
48 namespace Mono.Data.Tds.Protocol
50 public abstract class Tds
55 TdsVersion tdsVersion;
57 protected internal TdsConnectionParameters connectionParms;
58 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
63 string originalDatabase = string.Empty;
64 string databaseProductName;
65 string databaseProductVersion;
66 int databaseMajorVersion;
67 CultureInfo locale = CultureInfo.InvariantCulture;
69 readonly int lifeTime;
70 readonly DateTime created = DateTime.Now;
83 TdsDataRow currentRow;
84 TdsDataColumnCollection columns;
87 ArrayList columnNames;
89 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
98 ArrayList outputParameters = new ArrayList ();
99 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
101 int recordsAffected = -1;
105 int StreamColumnIndex;
107 bool sequentialAccess;
113 internal int poolStatus = 0;
119 protected string Charset {
120 get { return charset; }
123 protected CultureInfo Locale {
124 get { return locale; }
127 public bool DoneProc {
128 get { return doneProc; }
131 protected string Language {
132 get { return language; }
135 protected ArrayList ColumnNames {
136 get { return columnNames; }
139 public TdsDataRow ColumnValues {
140 get { return currentRow; }
143 internal TdsComm Comm {
147 public string Database {
148 get { return database; }
151 public string DataSource {
152 get { return dataSource; }
155 public virtual bool IsConnected {
156 get { return connected && comm != null && comm.IsConnected (); }
157 set { connected = value; }
160 public bool Pooling {
161 get { return pooling; }
162 set { pooling = value; }
165 public bool MoreResults {
166 get { return moreResults; }
167 set { moreResults = value; }
170 public int PacketSize {
171 get { return packetSize; }
174 public int RecordsAffected {
175 get { return recordsAffected; }
176 set { recordsAffected = value; }
179 public string ServerVersion {
180 get { return databaseProductVersion; }
183 public TdsDataColumnCollection Columns {
184 get { return columns; }
187 public TdsVersion TdsVersion {
188 get { return tdsVersion; }
191 public ArrayList OutputParameters {
192 get { return outputParameters; }
193 set { outputParameters = value; }
196 protected TdsMetaParameterCollection Parameters {
197 get { return parameters; }
198 set { parameters = value; }
201 public bool SequentialAccess {
202 get { return sequentialAccess; }
203 set { sequentialAccess = value; }
206 public byte[] Collation {
207 get {return collation; }
210 public TdsVersion ServerTdsVersion {
212 switch (databaseMajorVersion) {
213 case 4: return TdsVersion.tds42;
214 case 5: return TdsVersion.tds50;
215 case 7: return TdsVersion.tds70;
216 case 8: return TdsVersion.tds80;
217 case 9: return TdsVersion.tds90;
218 case 10: return TdsVersion.tds100;
219 default: return tdsVersion; // return client's version
224 private void SkipRow ()
226 SkipToColumnIndex (Columns.Count);
229 StreamColumnIndex = 0;
231 LoadInProgress = false;
234 private void SkipToColumnIndex (int colIndex)
239 if (colIndex < StreamColumnIndex)
240 throw new Exception ("Cannot Skip to a colindex less than the curr index");
242 while (colIndex != StreamColumnIndex) {
244 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
246 throw new Exception ("Column type unset.");
248 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
250 if (!(colType == TdsColumnType.Image ||
251 colType == TdsColumnType.Text ||
252 colType == TdsColumnType.NText)) {
253 GetColumnValue (colType, false, StreamColumnIndex);
254 StreamColumnIndex ++;
258 Comm.Skip (StreamLength);
265 public object GetSequentialColumnValue (int colIndex)
267 if (colIndex < StreamColumnIndex)
268 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
273 if (colIndex != StreamColumnIndex)
274 SkipToColumnIndex (colIndex);
277 object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
279 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
285 public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
287 if (colIndex < StreamColumnIndex)
288 throw new InvalidOperationException ("Invalid attempt to read from column ordinal" + colIndex);
290 if (colIndex != StreamColumnIndex)
291 SkipToColumnIndex (colIndex);
293 if (!LoadInProgress) {
295 BeginLoad (Columns[colIndex].ColumnType);
297 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
303 return LoadData (fieldIndex, buffer, bufferIndex, size);
304 } catch (IOException ex) {
306 throw new TdsInternalException ("Server closed the connection.", ex);
310 private void BeginLoad (
312 TdsColumnType? colType
314 TdsColumnType colType
325 throw new ArgumentNullException ("colType");
329 case TdsColumnType.Text :
330 case TdsColumnType.NText:
331 case TdsColumnType.Image:
332 if (Comm.GetByte () != 0) {
334 StreamLength = Comm.GetTdsInt ();
336 // use -2 to indicate that we're dealing
341 case TdsColumnType.BigVarChar:
342 case TdsColumnType.BigChar:
343 case TdsColumnType.BigBinary:
344 case TdsColumnType.BigVarBinary:
346 StreamLength = Comm.GetTdsShort ();
348 case TdsColumnType.VarChar :
349 case TdsColumnType.NVarChar :
350 case TdsColumnType.Char:
351 case TdsColumnType.NChar:
352 case TdsColumnType.Binary:
353 case TdsColumnType.VarBinary:
354 StreamLength = Comm.GetTdsShort ();
362 LoadInProgress = true;
365 private void EndLoad()
367 if (StreamLength > 0)
368 Comm.Skip (StreamLength);
372 LoadInProgress = false;
375 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
377 if (StreamLength <= 0)
380 if (fieldIndex < StreamIndex)
381 throw new InvalidOperationException (string.Format (
382 "Attempting to read at dataIndex '{0}' is " +
383 "not allowed as this is less than the " +
384 "current position. You must read from " +
385 "dataIndex '{1}' or greater.",
386 fieldIndex, StreamIndex));
388 if (fieldIndex >= (StreamLength + StreamIndex))
391 // determine number of bytes to skip
392 int skip = (int) (fieldIndex - StreamIndex);
395 // update the current position
396 StreamIndex += (fieldIndex - StreamIndex);
397 // update the remaining length
398 StreamLength -= skip;
400 // Load the reqd amt of bytes
401 int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
402 byte[] arr = Comm.GetBytes (loadlen, true);
404 // update the index and stream length
405 StreamIndex += loadlen + (fieldIndex - StreamIndex);
406 StreamLength -= loadlen;
407 arr.CopyTo (buffer, bufferIndex);
412 #endregion // Properties
416 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
417 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
423 public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
424 : this (dataSource, port, packetSize, timeout, 0, tdsVersion)
428 public Tds (string dataSource, int port, int packetSize, int timeout, int lifeTime, TdsVersion tdsVersion)
430 this.tdsVersion = tdsVersion;
431 this.packetSize = packetSize;
432 this.dataSource = dataSource;
433 this.columns = new TdsDataColumnCollection ();
434 this.lifeTime = lifeTime;
436 InitComm (port, timeout);
439 protected virtual void InitComm (int port, int timeout)
441 comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion);
444 #endregion // Constructors
446 #region Public Methods
448 internal bool Expired {
452 return DateTime.Now > (created + TimeSpan.FromSeconds (lifeTime));
456 internal protected void InitExec ()
462 // Reset "read" status variables - used in case of SequentialAccess
463 isResultRead = false;
467 StreamColumnIndex = 0;
468 LoadInProgress = false;
470 // Reset more variables
471 queryInProgress = false;
472 cancelsRequested = 0;
473 cancelsProcessed = 0;
474 recordsAffected = -1;
477 outputParameters.Clear ();
480 public void Cancel ()
482 if (queryInProgress) {
483 if (cancelsRequested == cancelsProcessed) {
484 comm.StartPacket (TdsPacketType.Cancel);
487 } catch (IOException ex) {
489 throw new TdsInternalException ("Server closed the connection.", ex);
491 cancelsRequested += 1;
496 public abstract bool Connect (TdsConnectionParameters connectionParameters);
498 public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
500 string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
501 return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
504 public virtual void Disconnect ()
507 comm.StartPacket (TdsPacketType.Logoff);
508 comm.Append ((byte) 0);
511 // We're closing the socket anyway
517 public virtual bool Reset ()
519 database = originalDatabase;
523 protected virtual bool IsValidRowCount (byte status, byte op)
525 return ((status & (0x10)) != 0) ;
528 public void Execute (string sql)
530 Execute (sql, null, 0, false);
533 public void ExecProc (string sql)
535 ExecProc (sql, null, 0, false);
538 public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
540 ExecuteQuery (sql, timeout, wantResults);
543 public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
545 ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
548 public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
550 throw new NotSupportedException ();
553 internal void ExecBulkCopyMetaData (int timeout, bool wantResults)
558 CheckForData (timeout);
561 } catch (IOException ex) {
563 throw new TdsInternalException ("Server closed the connection.", ex);
567 internal void ExecBulkCopy (int timeout, bool wantResults)
572 CheckForData (timeout);
575 } catch (IOException ex) {
577 throw new TdsInternalException ("Server closed the connection.", ex);
581 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
585 Comm.StartPacket (TdsPacketType.Query);
589 CheckForData (timeout);
592 } catch (IOException ex) {
594 throw new TdsInternalException ("Server closed the connection.", ex);
598 protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters,
599 int timeout, bool wantResults)
601 Comm.StartPacket (TdsPacketType.DBRPC);
603 byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
604 byte rpcNameLength = (byte) rpcNameBytes.Length;
605 ushort mask = 0x0000;
606 ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
609 Comm.Append (packetLength);
610 Comm.Append (rpcNameLength);
611 Comm.Append (rpcNameBytes);
616 CheckForData (timeout);
619 } catch (IOException ex) {
621 throw new TdsInternalException ("Server closed the connection.", ex);
625 public bool NextResult ()
627 if (SequentialAccess) {
629 while (NextRow ()) {}
631 isResultRead = false;
637 TdsPacketSubType subType;
640 bool outputParams = false;
643 subType = ProcessSubPacket ();
649 case TdsPacketSubType.ColumnInfo:
650 case TdsPacketSubType.ColumnMetadata:
651 case TdsPacketSubType.RowFormat:
652 byte peek = Comm.Peek ();
653 done = (peek != (byte) TdsPacketSubType.TableName);
654 if (done && doneProc && peek == (byte) TdsPacketSubType.Row) {
659 case TdsPacketSubType.TableName:
662 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
664 case TdsPacketSubType.ColumnDetail:
676 public bool NextRow ()
678 if (SequentialAccess) {
685 TdsPacketSubType subType;
690 subType = ProcessSubPacket ();
692 case TdsPacketSubType.Row:
696 case TdsPacketSubType.Done:
697 case TdsPacketSubType.DoneProc:
698 case TdsPacketSubType.DoneInProc:
708 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
710 throw new NotSupportedException ();
713 public void SkipToEnd ()
716 while (NextResult ()) { /* DO NOTHING */ }
717 } catch (IOException ex) {
719 throw new TdsInternalException ("Server closed the connection.", ex);
723 public virtual void Unprepare (string statementId)
725 throw new NotSupportedException ();
728 #endregion // Public Methods
730 #region // Private Methods
732 [MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
733 protected void CheckForData (int timeout)
735 if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
737 throw CreateTimeoutException (dataSource, "CheckForData()");
741 protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
743 return new TdsInternalInfoMessageEventArgs (errors);
746 protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
748 return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
751 private Encoding GetEncodingFromColumnCollation (int lcid, int sortId)
754 return TdsCharset.GetEncodingFromSortOrder (sortId);
756 return TdsCharset.GetEncodingFromLCID (lcid);
759 protected object GetColumnValue (
761 TdsColumnType? colType,
763 TdsColumnType colType,
767 return GetColumnValue (colType, outParam, -1);
770 private object GetColumnValue (
772 TdsColumnType? colType,
774 TdsColumnType colType,
776 bool outParam, int ordinal)
779 object element = null;
781 int lcid = 0, sortId = 0;
785 throw new ArgumentNullException ("colType");
787 if (ordinal > -1 && tdsVersion > TdsVersion.tds70) {
789 lcid = (int) columns[ordinal].LCID;
790 sortId = (int) columns[ordinal].SortOrder;
792 lcid = (int) columns[ordinal]["LCID"];
793 sortId = (int) columns[ordinal]["SortOrder"];
798 case TdsColumnType.IntN :
801 element = GetIntValue (colType);
803 case TdsColumnType.Int1 :
804 case TdsColumnType.Int2 :
805 case TdsColumnType.Int4 :
806 case TdsColumnType.BigInt :
807 element = GetIntValue (colType);
809 case TdsColumnType.Image :
812 element = GetImageValue ();
814 case TdsColumnType.Text :
815 enc = GetEncodingFromColumnCollation (lcid, sortId);
818 element = GetTextValue (false, enc);
820 case TdsColumnType.NText :
821 enc = GetEncodingFromColumnCollation (lcid, sortId);
824 element = GetTextValue (true, enc);
826 case TdsColumnType.Char :
827 case TdsColumnType.VarChar :
828 enc = GetEncodingFromColumnCollation (lcid, sortId);
831 element = GetStringValue (colType, false, outParam, enc);
833 case TdsColumnType.BigVarBinary :
836 len = comm.GetTdsShort ();
837 element = comm.GetBytes (len, true);
840 case TdsColumnType.BigBinary :
843 len = comm.GetTdsShort ();
844 element = comm.GetBytes (len, true);
847 case TdsColumnType.BigBinary :
850 element = GetBinaryValue ();
852 case TdsColumnType.BigChar :
853 case TdsColumnType.BigVarChar :
854 enc = GetEncodingFromColumnCollation (lcid, sortId);
857 element = GetStringValue (colType, false, outParam, enc);
859 case TdsColumnType.NChar :
860 case TdsColumnType.BigNVarChar :
861 enc = GetEncodingFromColumnCollation (lcid, sortId);
864 element = GetStringValue (colType, true, outParam, enc);
866 case TdsColumnType.NVarChar :
867 enc = GetEncodingFromColumnCollation (lcid, sortId);
870 element = GetStringValue (colType, true, outParam, enc);
872 case TdsColumnType.Real :
873 case TdsColumnType.Float8 :
874 element = GetFloatValue (colType);
876 case TdsColumnType.FloatN :
879 element = GetFloatValue (colType);
881 case TdsColumnType.SmallMoney :
882 case TdsColumnType.Money :
883 element = GetMoneyValue (colType);
885 case TdsColumnType.MoneyN :
888 element = GetMoneyValue (colType);
890 case TdsColumnType.Numeric :
891 case TdsColumnType.Decimal :
896 precision = comm.GetByte ();
897 scale = comm.GetByte ();
901 precision = (byte) columns[ordinal].NumericPrecision;
902 scale = (byte) columns[ordinal].NumericScale;
904 precision = (byte) columns[ordinal]["NumericPrecision"];
905 scale = (byte) columns[ordinal]["NumericScale"];
909 element = GetDecimalValue (precision, scale);
911 // workaround for fact that TDS 7.0 returns
912 // bigint as decimal (19,0), and client code
913 // expects it to be returned as a long
914 if (scale == 0 && precision <= 19 && tdsVersion == TdsVersion.tds70) {
915 if (!(element is System.DBNull))
916 element = Convert.ToInt64 (element);
919 case TdsColumnType.DateTimeN :
922 element = GetDateTimeValue (colType);
924 case TdsColumnType.DateTime4 :
925 case TdsColumnType.DateTime :
926 element = GetDateTimeValue (colType);
928 case TdsColumnType.VarBinary :
929 case TdsColumnType.Binary :
932 element = GetBinaryValue ();
934 case TdsColumnType.BitN :
937 if (comm.GetByte () == 0)
938 element = DBNull.Value;
940 element = (comm.GetByte() != 0);
942 case TdsColumnType.Bit :
943 int columnSize = comm.GetByte ();
944 element = (columnSize != 0);
946 case TdsColumnType.UniqueIdentifier :
947 if (comm.Peek () != 16) { // If it's null, then what to do?
948 /*byte swallowed =*/ comm.GetByte();
949 element = DBNull.Value;
955 len = comm.GetByte () & 0xff;
957 byte[] guidBytes = comm.GetBytes (len, true);
958 if (!BitConverter.IsLittleEndian) {
959 byte[] swappedguidBytes = new byte[len];
960 for (int i = 0; i < 4; i++)
961 swappedguidBytes[i] = guidBytes[4-i-1];
962 for (int i = 4; i < 6; i++)
963 swappedguidBytes[i] = guidBytes[6-(i-4)-1];
964 for (int i = 6; i < 8; i++)
965 swappedguidBytes[i] = guidBytes[8-(i-6)-1];
966 for (int i = 8; i < 16; i++)
967 swappedguidBytes[i] = guidBytes[i];
968 Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
970 element = new Guid (guidBytes);
979 private object GetBinaryValue ()
982 object result = DBNull.Value;
984 if (tdsVersion >= TdsVersion.tds70) {
985 len = comm.GetTdsShort ();
986 if (len != 0xffff && len >= 0)
987 result = comm.GetBytes (len, true);
989 len = (comm.GetByte () & 0xff);
991 result = comm.GetBytes (len, true);
997 private object GetDateTimeValue (
1010 throw new ArgumentNullException ("type");
1013 case TdsColumnType.DateTime4:
1016 case TdsColumnType.DateTime:
1019 case TdsColumnType.DateTimeN:
1020 byte tmp = comm.Peek ();
1021 if (tmp != 0 && tmp != 4 && tmp != 8)
1023 len = comm.GetByte ();
1027 DateTime epoch = new DateTime (1900, 1, 1);
1031 result = epoch.AddDays (comm.GetTdsInt ());
1032 int seconds = comm.GetTdsInt ();
1033 long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f);
1034 if (seconds != 0 || millis != 0) {
1035 result = ((DateTime) result).AddSeconds (seconds / 300);
1036 result = ((DateTime) result).AddMilliseconds (millis);
1040 // MSDN says small datetime is stored in 2 bytes as no of days
1041 // *after* 1/1/1900. so, cast to unsigned short
1042 result = epoch.AddDays ((ushort) comm.GetTdsShort ());
1043 short minutes = comm.GetTdsShort ();
1045 result = ((DateTime) result).AddMinutes ((int) minutes);
1048 result = DBNull.Value;
1055 private object GetDecimalValue (byte precision, byte scale)
1057 if (tdsVersion < TdsVersion.tds70)
1058 return GetDecimalValueTds50 (precision, scale);
1060 return GetDecimalValueTds70 (precision, scale);
1063 private object GetDecimalValueTds70 (byte precision, byte scale)
1065 int[] bits = new int[4] {0,0,0,0};
1067 int len = (comm.GetByte() & 0xff) - 1;
1069 return DBNull.Value;
1071 bool positive = (comm.GetByte () == 1);
1073 throw new OverflowException ();
1075 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
1076 bits[index] = comm.GetTdsInt ();
1079 return new TdsBigDecimal (precision, scale, !positive, bits);
1081 return new Decimal (bits[0], bits[1], bits[2], !positive, scale);
1084 private object GetDecimalValueTds50 (byte precision, byte scale)
1086 int[] bits = new int[4] {0,0,0,0};
1088 int len = (comm.GetByte() & 0xff);
1090 return DBNull.Value;
1092 byte[] dec_bytes=comm.GetBytes(len,false);
1094 byte[] easy=new byte[4];
1096 bool positive = dec_bytes[0]==1;
1099 throw new OverflowException ();
1101 for (int i = 1, index = 0; i < len && i < 16; i +=
1103 for(int j=0; j<4; j++)
1105 easy[j]=dec_bytes[len-
1109 if(!BitConverter.IsLittleEndian)
1110 easy=comm.Swap(easy);
1111 bits[index] = BitConverter.ToInt32(easy,0);
1114 return new TdsBigDecimal (precision,
1115 scale, positive, bits);
1117 return new Decimal(bits[0], bits[1], bits
1118 [2], positive, scale);
1122 private object GetFloatValue (
1124 TdsColumnType? columnType
1126 TdsColumnType columnType
1131 if (columnType == null)
1132 throw new ArgumentNullException ("columnType");
1136 switch (columnType) {
1137 case TdsColumnType.Real:
1140 case TdsColumnType.Float8:
1143 case TdsColumnType.FloatN:
1144 columnSize = comm.GetByte ();
1148 switch (columnSize) {
1150 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1152 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1154 return DBNull.Value;
1158 private object GetImageValue ()
1160 byte hasValue = comm.GetByte ();
1163 return DBNull.Value;
1166 int len = comm.GetTdsInt ();
1169 return DBNull.Value;
1171 return (comm.GetBytes (len, true));
1174 private object GetIntValue (
1186 throw new ArgumentNullException ("type");
1189 case TdsColumnType.BigInt :
1192 case TdsColumnType.IntN :
1193 len = comm.GetByte ();
1195 case TdsColumnType.Int4 :
1198 case TdsColumnType.Int2 :
1201 case TdsColumnType.Int1 :
1205 return DBNull.Value;
1210 return (comm.GetTdsInt64 ());
1212 return (comm.GetTdsInt ());
1214 return (comm.GetTdsShort ());
1216 return (comm.GetByte ());
1218 return DBNull.Value;
1222 private object GetMoneyValue (
1234 throw new ArgumentNullException ("type");
1237 case TdsColumnType.SmallMoney :
1238 case TdsColumnType.Money4 :
1241 case TdsColumnType.Money :
1244 case TdsColumnType.MoneyN :
1245 len = comm.GetByte ();
1248 return DBNull.Value;
1253 int val = Comm.GetTdsInt ();
1254 bool negative = val < 0;
1257 return new Decimal (val, 0, 0, negative, 4);
1260 int hi = Comm.GetTdsInt ();
1261 int lo = Comm.GetTdsInt ();
1262 bool negative = hi < 0;
1268 return new Decimal (lo, hi, 0, negative, 4);
1271 return DBNull.Value;
1275 protected object GetStringValue (
1277 TdsColumnType? colType,
1279 TdsColumnType colType,
1281 bool wideChars, bool outputParam, Encoding encoder)
1283 bool shortLen = false;
1284 Encoding enc = encoder;
1286 if (tdsVersion > TdsVersion.tds70 && outputParam &&
1287 (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar ||
1288 colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar ||
1289 colType == TdsColumnType.NVarChar)) {
1290 // Read collation for SqlServer 2000 and beyond
1292 collation = Comm.GetBytes (5, true);
1293 enc = TdsCharset.GetEncoding (collation);
1296 shortLen = (tdsVersion >= TdsVersion.tds70) && (wideChars || !outputParam);
1299 int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff);
1300 return GetStringValue (wideChars, len, enc);
1303 protected object GetStringValue (bool wideChars, int len, Encoding enc)
1305 if (tdsVersion < TdsVersion.tds70 && len == 0)
1306 return DBNull.Value;
1308 else if (len >= 0) {
1311 result = comm.GetString (len / 2, enc);
1313 result = comm.GetString (len, false, enc);
1314 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1315 result = string.Empty;
1319 return DBNull.Value;
1322 protected int GetSubPacketLength ()
1324 return comm.GetTdsShort ();
1327 private object GetTextValue (bool wideChars, Encoding encoder)
1329 string result = null;
1330 byte hasValue = comm.GetByte ();
1333 return DBNull.Value;
1335 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1338 int len = comm.GetTdsInt ();
1340 //if the len is 0 , then the string can be a '' string
1341 // this method is called only for Text and NText. Hence will
1342 // return a empty string
1344 return string.Empty;
1347 result = comm.GetString (len / 2, encoder);
1349 result = comm.GetString (len, false, encoder);
1352 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1353 result = string.Empty;
1358 internal bool IsBlobType (TdsColumnType columnType)
1360 return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText);
1363 internal bool IsLargeType (TdsColumnType columnType)
1365 return ((byte) columnType > 128);
1368 protected bool IsWideType (TdsColumnType columnType)
1370 switch (columnType) {
1371 case TdsColumnType.NChar:
1372 case TdsColumnType.NText:
1373 case TdsColumnType.NVarChar:
1380 internal static bool IsFixedSizeColumn (TdsColumnType columnType)
1382 switch (columnType) {
1383 case TdsColumnType.Int1 :
1384 case TdsColumnType.Int2 :
1385 case TdsColumnType.Int4 :
1386 case TdsColumnType.BigInt :
1387 case TdsColumnType.Float8 :
1388 case TdsColumnType.DateTime :
1389 case TdsColumnType.Bit :
1390 case TdsColumnType.Money :
1391 case TdsColumnType.Money4 :
1392 case TdsColumnType.SmallMoney :
1393 case TdsColumnType.Real :
1394 case TdsColumnType.DateTime4 :
1396 case TdsColumnType.Decimal:
1397 case TdsColumnType.Numeric:
1405 protected void LoadRow ()
1407 if (SequentialAccess) {
1411 isResultRead = true;
1415 currentRow = new TdsDataRow ();
1418 foreach (TdsDataColumn column in columns) {
1420 object o = GetColumnValue (column.ColumnType, false, i);
1422 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1426 outputParameters.Add (o);
1428 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1429 currentRow.BigDecimalIndex = i;
1434 internal static int LookupBufferSize (TdsColumnType columnType)
1436 switch (columnType) {
1437 case TdsColumnType.Int1 :
1438 case TdsColumnType.Bit :
1440 case TdsColumnType.Int2 :
1442 case TdsColumnType.Int4 :
1443 case TdsColumnType.Real :
1444 case TdsColumnType.DateTime4 :
1445 case TdsColumnType.Money4 :
1446 case TdsColumnType.SmallMoney :
1448 case TdsColumnType.Float8 :
1449 case TdsColumnType.DateTime :
1450 case TdsColumnType.Money :
1451 case TdsColumnType.BigInt :
1458 protected internal int ProcessAuthentication ()
1460 int pdu_size = Comm.GetTdsShort ();
1461 byte[] msg2 = Comm.GetBytes (pdu_size, true);
1463 Type2Message t2 = new Type2Message (msg2);
1464 // 0x0001 Negotiate Unicode
1465 // 0x0200 Negotiate NTLM
1466 // 0x8000 Negotiate Always Sign
1468 Type3Message t3 = new Type3Message (t2);
1470 t3.Domain = this.connectionParms.DefaultDomain;
1471 t3.Host = this.connectionParms.Hostname;
1472 t3.Username = this.connectionParms.User;
1473 t3.Password = GetPlainPassword(this.connectionParms.Password);
1475 Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
1476 Comm.Append (t3.GetBytes ());
1479 } catch (IOException ex) {
1481 throw new TdsInternalException ("Server closed the connection.", ex);
1483 return 1; // TDS_SUCCEED
1486 protected void ProcessColumnDetail ()
1488 int len = GetSubPacketLength ();
1489 byte[] values = new byte[3];
1490 int columnNameLength;
1491 string baseColumnName = String.Empty;
1494 while (position < len) {
1495 for (int j = 0; j < 3; j += 1)
1496 values[j] = comm.GetByte ();
1499 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1501 if (tdsVersion >= TdsVersion.tds70) {
1502 columnNameLength = comm.GetByte ();
1503 position += 2 * columnNameLength + 1;
1506 columnNameLength = comm.GetByte ();
1507 position += columnNameLength + 1;
1509 baseColumnName = comm.GetString (columnNameLength);
1512 byte index = (byte) (values[0] - (byte) 1);
1513 byte tableIndex = (byte) (values[1] - (byte) 1);
1514 bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
1516 TdsDataColumn column = columns [index];
1518 column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0);
1519 column.IsExpression = isExpression;
1520 column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0);
1521 column.IsAliased = isAlias;
1522 column.BaseColumnName = ((isAlias) ? baseColumnName : null);
1523 column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null);
1525 column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0);
1526 column ["IsExpression"] = isExpression;
1527 column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0);
1528 column ["IsAliased"] = isAlias;
1529 column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null);
1530 column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null);
1535 protected abstract void ProcessColumnInfo ();
1537 protected void ProcessColumnNames ()
1539 columnNames = new ArrayList ();
1541 int totalLength = comm.GetTdsShort ();
1545 while (bytesRead < totalLength) {
1546 int columnNameLength = comm.GetByte ();
1547 string columnName = comm.GetString (columnNameLength);
1548 bytesRead = bytesRead + 1 + columnNameLength;
1549 columnNames.Add (columnName);
1554 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1555 protected void ProcessEndToken (TdsPacketSubType type)
1557 byte status = Comm.GetByte ();
1559 byte op = comm.GetByte ();
1562 int rowCount = comm.GetTdsInt ();
1563 bool validRowCount = IsValidRowCount (status,op);
1564 moreResults = ((status & 0x01) != 0);
1565 bool cancelled = ((status & 0x20) != 0);
1568 case TdsPacketSubType.DoneProc:
1570 goto case TdsPacketSubType.Done;
1571 case TdsPacketSubType.Done:
1572 case TdsPacketSubType.DoneInProc:
1573 if (validRowCount) {
1574 if (recordsAffected == -1)
1575 recordsAffected = rowCount;
1577 recordsAffected += rowCount;
1583 queryInProgress = false;
1585 cancelsProcessed += 1;
1586 if (messages.Count > 0 && !moreResults)
1587 OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
1590 protected void ProcessEnvironmentChange ()
1592 // VARADHAN: TDS 8 Debugging
1593 //Console.WriteLine ("In ProcessEnvironmentChange... entry");
1594 int len = GetSubPacketLength ();
1595 TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
1599 case TdsEnvPacketSubType.BlockSize :
1601 cLen = comm.GetByte ();
1602 blockSize = comm.GetString (cLen);
1604 if (tdsVersion >= TdsVersion.tds70)
1605 comm.Skip (len - 2 - cLen * 2);
1607 comm.Skip (len - 2 - cLen);
1609 packetSize = Int32.Parse (blockSize);
1610 comm.ResizeOutBuf (packetSize);
1612 case TdsEnvPacketSubType.CharSet :
1613 cLen = comm.GetByte ();
1614 if (tdsVersion == TdsVersion.tds70) {
1615 SetCharset (comm.GetString (cLen));
1616 comm.Skip (len - 2 - cLen * 2);
1619 SetCharset (comm.GetString (cLen));
1620 comm.Skip (len - 2 - cLen);
1624 case TdsEnvPacketSubType.Locale :
1625 cLen = comm.GetByte ();
1627 if (tdsVersion >= TdsVersion.tds70) {
1628 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1629 comm.Skip (len - 2 - cLen * 2);
1632 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1633 comm.Skip (len - 2 - cLen);
1635 locale = new CultureInfo (lcid);
1637 case TdsEnvPacketSubType.Database :
1638 cLen = comm.GetByte ();
1639 string newDB = comm.GetString (cLen);
1640 cLen = comm.GetByte () & 0xff;
1641 comm.GetString (cLen);
1642 if (originalDatabase == string.Empty)
1643 originalDatabase = newDB;
1646 case TdsEnvPacketSubType.CollationInfo:
1647 //Console.WriteLine ("ProcessEnvironmentChange::Got collation info");
1648 cLen = comm.GetByte ();
1649 collation = comm.GetBytes (cLen, true);
1650 lcid = TdsCollation.LCID (collation);
1651 locale = new CultureInfo (lcid);
1652 SetCharset (TdsCharset.GetEncoding (collation));
1656 comm.Skip (len - 1);
1659 // VARADHAN: TDS 8 Debugging
1660 //Console.WriteLine ("In ProcessEnvironmentChange... exit");
1663 protected void ProcessLoginAck ()
1665 uint srvVersion = 0;
1666 GetSubPacketLength ();
1668 //Console.WriteLine ("ProcessLoginAck: B4 tdsVersion:{0}", tdsVersion);
1669 // Valid only for a Login7 request
1670 if (tdsVersion >= TdsVersion.tds70) {
1672 srvVersion = (uint)comm.GetTdsInt ();
1674 //Console.WriteLine ("srvVersion: {0}", srvVersion);
1675 switch (srvVersion) {
1677 tdsVersion = TdsVersion.tds70;
1680 tdsVersion = TdsVersion.tds80;
1683 tdsVersion = TdsVersion.tds81;
1686 tdsVersion = TdsVersion.tds90;
1689 //Console.WriteLine ("ProcessLoginAck: after tdsVersion:{0}", tdsVersion);
1692 if (tdsVersion >= TdsVersion.tds70) {
1693 int nameLength = comm.GetByte ();
1694 databaseProductName = comm.GetString (nameLength);
1695 databaseMajorVersion = comm.GetByte ();
1696 databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"),
1697 comm.GetByte ().ToString("00"),
1698 (256 * comm.GetByte () + comm.GetByte ()).ToString("0000"));
1701 short nameLength = comm.GetByte ();
1702 databaseProductName = comm.GetString (nameLength);
1704 databaseMajorVersion = comm.GetByte ();
1705 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1709 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1710 int last = databaseProductName.IndexOf ('\0');
1711 databaseProductName = databaseProductName.Substring (0, last);
1715 //Console.WriteLine ("databaseProductVersion:{0}", databaseProductVersion);
1718 protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
1720 if (TdsErrorMessage != null)
1721 TdsErrorMessage (this, e);
1724 protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
1726 if (TdsInfoMessage != null)
1727 TdsInfoMessage (this, e);
1731 protected void ProcessMessage (TdsPacketSubType subType)
1733 GetSubPacketLength ();
1735 int number = comm.GetTdsInt ();
1736 byte state = comm.GetByte ();
1737 byte theClass = comm.GetByte ();
1743 bool isError = false;
1745 if (subType == TdsPacketSubType.EED) {
1746 isError = (theClass > 10);
1747 comm.Skip (comm.GetByte ()); // SQL State
1748 comm.Skip (1); // Status
1749 comm.Skip (2); // TranState
1751 isError = (subType == TdsPacketSubType.Error);
1753 message = comm.GetString (comm.GetTdsShort ());
1754 server = comm.GetString (comm.GetByte ());
1755 procedure = comm.GetString (comm.GetByte ());
1756 lineNumber = comm.GetByte ();
1758 source = String.Empty; // FIXME
1761 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1763 messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
1766 protected virtual void ProcessOutputParam ()
1768 GetSubPacketLength ();
1769 /*string paramName = */comm.GetString (comm.GetByte () & 0xff);
1772 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1773 object value = GetColumnValue (colType, true);
1774 outputParameters.Add (value);
1777 protected void ProcessDynamic ()
1780 /*byte type =*/ Comm.GetByte ();
1781 /*byte status =*/ Comm.GetByte ();
1782 /*string id =*/ Comm.GetString (Comm.GetByte ());
1785 protected virtual TdsPacketSubType ProcessSubPacket ()
1787 // VARADHAN: TDS 8 Debugging
1788 // Console.WriteLine ("In ProcessSubPacket... entry");
1790 TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte ();
1792 // VARADHAN: TDS 8 Debugging
1793 //Console.WriteLine ("Subpacket type: {0}", subType);
1795 case TdsPacketSubType.Dynamic2:
1796 comm.Skip (comm.GetTdsInt ());
1798 case TdsPacketSubType.AltName:
1799 case TdsPacketSubType.AltFormat:
1800 case TdsPacketSubType.Capability:
1801 case TdsPacketSubType.ParamFormat:
1802 comm.Skip (comm.GetTdsShort ());
1804 case TdsPacketSubType.Dynamic:
1807 case TdsPacketSubType.EnvironmentChange:
1808 ProcessEnvironmentChange ();
1810 case TdsPacketSubType.Info: // TDS 4.2/7.0
1811 case TdsPacketSubType.EED: // TDS 5.0
1812 case TdsPacketSubType.Error: // TDS 4.2/7.0
1813 ProcessMessage (subType);
1815 case TdsPacketSubType.Param:
1816 ProcessOutputParam ();
1818 case TdsPacketSubType.LoginAck:
1821 case TdsPacketSubType.Authentication: // TDS 7.0
1822 ProcessAuthentication ();
1824 case TdsPacketSubType.ReturnStatus :
1825 ProcessReturnStatus ();
1827 case TdsPacketSubType.ProcId:
1830 case TdsPacketSubType.Done:
1831 case TdsPacketSubType.DoneProc:
1832 case TdsPacketSubType.DoneInProc:
1833 ProcessEndToken (subType);
1835 case TdsPacketSubType.ColumnName:
1837 ProcessColumnNames ();
1839 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1840 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1841 case TdsPacketSubType.RowFormat: // TDS 5.0
1843 ProcessColumnInfo ();
1845 case TdsPacketSubType.ColumnDetail:
1846 ProcessColumnDetail ();
1848 case TdsPacketSubType.TableName:
1849 ProcessTableName ();
1851 case TdsPacketSubType.ColumnOrder:
1852 comm.Skip (comm.GetTdsShort ());
1854 case TdsPacketSubType.Control:
1855 comm.Skip (comm.GetTdsShort ());
1857 case TdsPacketSubType.Row:
1862 // VARADHAN: TDS 8 Debugging
1863 //Console.WriteLine ("In ProcessSubPacket... exit");
1867 protected void ProcessTableName ()
1869 tableNames = new ArrayList ();
1870 int totalLength = comm.GetTdsShort ();
1874 while (position < totalLength) {
1875 if (tdsVersion >= TdsVersion.tds70) {
1876 len = comm.GetTdsShort ();
1877 position += 2 * (len + 1);
1880 len = comm.GetByte ();
1881 position += len + 1;
1883 tableNames.Add (comm.GetString (len));
1887 protected void SetCharset (Encoding encoder)
1889 comm.Encoder = encoder;
1892 protected void SetCharset (string charset)
1894 if (charset == null || charset.Length > 30)
1897 if (this.charset != null && this.charset == charset)
1900 if (charset.StartsWith ("cp")) {
1901 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1902 this.charset = charset;
1905 encoder = Encoding.GetEncoding ("iso-8859-1");
1906 this.charset = "iso_1";
1908 SetCharset (encoder);
1911 protected void SetLanguage (string language)
1913 if (language == null || language.Length > 30)
1914 language = "us_english";
1916 this.language = language;
1919 protected virtual void ProcessReturnStatus ()
1924 public static string GetPlainPassword(SecureString secPass)
1926 IntPtr plainString = IntPtr.Zero;
1929 plainString = Marshal.SecureStringToGlobalAllocUnicode(secPass);
1930 return Marshal.PtrToStringUni(plainString);
1934 Marshal.ZeroFreeGlobalAllocUnicode(plainString);
1938 #endregion // Private Methods
1941 #region asynchronous methods
1942 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1943 AsyncCallback callback, object state)
1947 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1948 ar.TdsAsyncState.WantResults = wantResults;
1950 Comm.StartPacket (TdsPacketType.Query);
1954 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1956 } catch (IOException ex) {
1958 throw new TdsInternalException ("Server closed the connection.", ex);
1964 protected void EndExecuteQueryInternal (IAsyncResult ar)
1966 if (!ar.IsCompleted)
1967 ar.AsyncWaitHandle.WaitOne ();
1968 TdsAsyncResult result = (TdsAsyncResult) ar;
1969 if (result.IsCompletedWithException)
1970 throw result.Exception;
1973 protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
1975 TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
1976 TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
1979 Comm.EndReadPacket (ar);
1980 if (!tdsState.WantResults)
1982 } catch (Exception e) {
1983 result.MarkComplete (e);
1986 result.MarkComplete ();
1990 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1991 TdsMetaParameterCollection parameters,
1992 AsyncCallback callback,
1995 // abstract, kept to be backward compatiable.
1996 throw new NotImplementedException ("should not be called!");
1999 public virtual void EndExecuteNonQuery (IAsyncResult ar)
2002 throw new NotImplementedException ("should not be called!");
2005 public virtual IAsyncResult BeginExecuteQuery (string sql,
2006 TdsMetaParameterCollection parameters,
2007 AsyncCallback callback,
2010 // abstract, kept to be backward compatiable.
2011 throw new NotImplementedException ("should not be called!");
2014 public virtual void EndExecuteQuery (IAsyncResult ar)
2017 throw new NotImplementedException ("should not be called!");
2020 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
2024 TdsMetaParameterCollection parameters,
2025 AsyncCallback callback,
2028 throw new NotImplementedException ("should not be called!");
2031 public virtual void EndExecuteProcedure (IAsyncResult ar)
2034 throw new NotImplementedException ("should not be called!");
2037 public void WaitFor (IAsyncResult ar)
2039 if (! ar.IsCompleted)
2040 ar.AsyncWaitHandle.WaitOne ();
2043 public void CheckAndThrowException (IAsyncResult ar)
2045 TdsAsyncResult result = (TdsAsyncResult) ar;
2046 if (result.IsCompleted && result.IsCompletedWithException)
2047 throw result.Exception;
2050 #endregion // asynchronous methods