2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / Tds.cs
blob76a810ab80a5f151312757dfbf400143a57710c1
1 //
2 // Mono.Data.Tds.Protocol.Tds.cs
3 //
4 // Author:
5 // Tim Coleman (tim@timcoleman.com)
6 // Sebastien Pouliot (spouliot@motus.com)
7 // Daniel Morgan (danielmorgan@verizon.net)
8 // Veerapuram Varadhan (vvaradhan@novell.com)
9 //
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:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
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;
37 using System;
38 using System.IO;
39 using System.Collections;
40 using System.ComponentModel;
41 using System.Diagnostics;
42 using System.Net.Sockets;
43 using System.Globalization;
44 using System.Text;
46 namespace Mono.Data.Tds.Protocol
48 public abstract class Tds
50 #region Fields
52 TdsComm comm;
53 TdsVersion tdsVersion;
55 protected internal TdsConnectionParameters connectionParms;
56 protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
58 int packetSize;
59 string dataSource;
60 string database;
61 string originalDatabase = string.Empty;
62 string databaseProductName;
63 string databaseProductVersion;
64 int databaseMajorVersion;
65 CultureInfo locale = CultureInfo.InvariantCulture;
67 string charset;
68 string language;
70 bool connected;
71 bool moreResults;
73 Encoding encoder;
74 // bool autoCommit;
76 bool doneProc;
77 bool pooling = true;
78 TdsDataRow currentRow;
79 TdsDataColumnCollection columns;
81 ArrayList tableNames;
82 ArrayList columnNames;
84 TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
86 bool queryInProgress;
87 int cancelsRequested;
88 int cancelsProcessed;
90 // bool isDone;
91 // bool isDoneInProc;
93 ArrayList outputParameters = new ArrayList ();
94 protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
96 int recordsAffected = -1;
98 long StreamLength;
99 long StreamIndex;
100 int StreamColumnIndex;
102 bool sequentialAccess;
103 bool isRowRead;
104 bool isResultRead;
105 bool LoadInProgress;
106 byte [] collation;
108 internal int poolStatus = 0;
110 #endregion // Fields
112 #region Properties
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 {
139 get { return 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 {
206 get {
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);
223 StreamLength = 0;
224 StreamColumnIndex = 0;
225 StreamIndex = 0;
226 LoadInProgress = false;
229 private void SkipToColumnIndex (int colIndex)
231 if (LoadInProgress)
232 EndLoad ();
234 if (colIndex < StreamColumnIndex)
235 throw new Exception ("Cannot Skip to a colindex less than the curr index");
237 while (colIndex != StreamColumnIndex) {
238 #if NET_2_0
239 TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
240 if (colType == null)
241 throw new Exception ("Column type unset.");
242 #else
243 TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"];
244 #endif
245 if (!(colType == TdsColumnType.Image ||
246 colType == TdsColumnType.Text ||
247 colType == TdsColumnType.NText)) {
248 GetColumnValue (colType, false, StreamColumnIndex);
249 StreamColumnIndex ++;
251 else {
252 BeginLoad (colType);
253 Comm.Skip (StreamLength);
254 StreamLength = 0;
255 EndLoad ();
260 public object GetSequentialColumnValue (int colIndex)
262 if (colIndex < StreamColumnIndex)
263 throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
265 if (LoadInProgress)
266 EndLoad ();
268 if (colIndex != StreamColumnIndex)
269 SkipToColumnIndex (colIndex);
271 #if NET_2_0
272 object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
273 #else
274 object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex);
275 #endif
276 StreamColumnIndex++;
277 return o;
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);
284 try {
285 if (colIndex != StreamColumnIndex)
286 SkipToColumnIndex (colIndex);
288 if (!LoadInProgress) {
289 #if NET_2_0
290 BeginLoad (Columns[colIndex].ColumnType);
291 #else
292 BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]);
293 #endif
296 if (buffer == null)
297 return StreamLength;
298 return LoadData (fieldIndex, buffer, bufferIndex, size);
299 } catch (IOException ex) {
300 connected = false;
301 throw new TdsInternalException ("Server closed the connection.", ex);
305 private void BeginLoad (
306 #if NET_2_0
307 TdsColumnType? colType
308 #else
309 TdsColumnType colType
310 #endif
313 if (LoadInProgress)
314 EndLoad ();
316 StreamLength = 0;
318 #if NET_2_0
319 if (colType == null)
320 throw new ArgumentNullException ("colType");
321 #endif
323 switch (colType) {
324 case TdsColumnType.Text :
325 case TdsColumnType.NText:
326 case TdsColumnType.Image:
327 if (Comm.GetByte () != 0) {
328 Comm.Skip (24);
329 StreamLength = Comm.GetTdsInt ();
330 } else {
331 // use -2 to indicate that we're dealing
332 // with a NULL value
333 StreamLength = -2;
335 break;
336 case TdsColumnType.BigVarChar:
337 case TdsColumnType.BigChar:
338 case TdsColumnType.BigBinary:
339 case TdsColumnType.BigVarBinary:
340 Comm.GetTdsShort ();
341 StreamLength = Comm.GetTdsShort ();
342 break;
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 ();
350 break;
351 default :
352 StreamLength = -1;
353 break;
356 StreamIndex = 0;
357 LoadInProgress = true;
360 private void EndLoad()
362 if (StreamLength > 0)
363 Comm.Skip (StreamLength);
364 StreamLength = 0;
365 StreamIndex = 0;
366 StreamColumnIndex++;
367 LoadInProgress = false;
370 private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
372 if (StreamLength <= 0)
373 return StreamLength;
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))
384 return 0;
386 // determine number of bytes to skip
387 int skip = (int) (fieldIndex - StreamIndex);
388 // skip bytes
389 Comm.Skip (skip);
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);
404 return arr.Length;
407 #endregion // Properties
409 #region Events
411 public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
412 public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
414 #endregion // Events
416 #region Constructors
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 ()
434 // clean up
435 moreResults = true;
436 doneProc = false;
438 // Reset "read" status variables - used in case of SequentialAccess
439 isResultRead = false;
440 isRowRead = false;
441 StreamLength = 0;
442 StreamIndex = 0;
443 StreamColumnIndex = 0;
444 LoadInProgress = false;
446 // Reset more variables
447 queryInProgress = false;
448 cancelsRequested = 0;
449 cancelsProcessed = 0;
450 recordsAffected = -1;
452 messages.Clear ();
453 outputParameters.Clear ();
456 public void Cancel ()
458 if (queryInProgress) {
459 if (cancelsRequested == cancelsProcessed) {
460 comm.StartPacket (TdsPacketType.Cancel);
461 try {
462 Comm.SendPacket ();
463 } catch (IOException ex) {
464 connected = false;
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 ()
482 try {
483 comm.StartPacket (TdsPacketType.Logoff);
484 comm.Append ((byte) 0);
485 comm.SendPacket ();
486 } catch {
487 // We're closing the socket anyway
489 connected = false;
490 comm.Close ();
493 public virtual bool Reset ()
495 database = originalDatabase;
496 return true;
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)
531 moreResults = true;
532 try {
533 Comm.SendPacket ();
534 CheckForData (timeout);
535 if (!wantResults)
536 SkipToEnd ();
537 } catch (IOException ex) {
538 connected = false;
539 throw new TdsInternalException ("Server closed the connection.", ex);
543 internal void ExecBulkCopy (int timeout, bool wantResults)
545 moreResults = true;
546 try {
547 Comm.SendPacket ();
548 CheckForData (timeout);
549 if (!wantResults)
550 SkipToEnd ();
551 } catch (IOException ex) {
552 connected = false;
553 throw new TdsInternalException ("Server closed the connection.", ex);
557 protected void ExecuteQuery (string sql, int timeout, bool wantResults)
559 InitExec ();
561 Comm.StartPacket (TdsPacketType.Query);
562 Comm.Append (sql);
563 try {
564 Comm.SendPacket ();
565 CheckForData (timeout);
566 if (!wantResults)
567 SkipToEnd ();
568 } catch (IOException ex) {
569 connected = false;
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 +
583 sizeof (ushort));
585 Comm.Append (packetLength);
586 Comm.Append (rpcNameLength);
587 Comm.Append (rpcNameBytes);
588 Comm.Append (mask);
590 try {
591 Comm.SendPacket ();
592 CheckForData (timeout);
593 if (!wantResults)
594 SkipToEnd ();
595 } catch (IOException ex) {
596 connected = false;
597 throw new TdsInternalException ("Server closed the connection.", ex);
601 public bool NextResult ()
603 if (SequentialAccess) {
604 if (isResultRead) {
605 while (NextRow ()) {}
606 isRowRead = false;
607 isResultRead = false;
610 if (!moreResults)
611 return false;
613 TdsPacketSubType subType;
615 bool done = false;
616 bool outputParams = false;
618 while (!done) {
619 subType = ProcessSubPacket ();
620 if (outputParams) {
621 moreResults = false;
622 break;
624 switch (subType) {
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) {
631 outputParams = true;
632 done = false;
634 break;
635 case TdsPacketSubType.TableName:
636 // done = true;
637 peek = Comm.Peek ();
638 done = (peek != (byte) TdsPacketSubType.ColumnDetail);
639 break;
640 case TdsPacketSubType.ColumnDetail:
641 done = true;
642 break;
643 default:
644 done = !moreResults;
645 break;
649 return moreResults;
652 public bool NextRow ()
654 if (SequentialAccess) {
655 if (isRowRead) {
656 SkipRow ();
657 isRowRead = false;
661 TdsPacketSubType subType;
662 bool done = false;
663 bool result = false;
665 do {
666 subType = ProcessSubPacket ();
667 switch (subType) {
668 case TdsPacketSubType.Row:
669 result = true;
670 done = true;
671 break;
672 case TdsPacketSubType.Done:
673 case TdsPacketSubType.DoneProc:
674 case TdsPacketSubType.DoneInProc:
675 result = false;
676 done = true;
677 break;
679 } while (!done);
681 return result;
684 public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
686 throw new NotSupportedException ();
689 public void SkipToEnd ()
691 try {
692 while (NextResult ()) { /* DO NOTHING */ }
693 } catch (IOException ex) {
694 connected = false;
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)) {
712 Cancel ();
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)
729 if (sortId != 0)
730 return TdsCharset.GetEncodingFromSortOrder (sortId);
731 else
732 return TdsCharset.GetEncodingFromLCID (lcid);
735 protected object GetColumnValue (
736 #if NET_2_0
737 TdsColumnType? colType,
738 #else
739 TdsColumnType colType,
740 #endif
741 bool outParam)
743 return GetColumnValue (colType, outParam, -1);
746 private object GetColumnValue (
747 #if NET_2_0
748 TdsColumnType? colType,
749 #else
750 TdsColumnType colType,
751 #endif
752 bool outParam, int ordinal)
754 int len;
755 object element = null;
756 Encoding enc = null;
757 int lcid = 0, sortId = 0;
759 #if NET_2_0
760 if (colType == null)
761 throw new ArgumentNullException ("colType");
762 #endif
763 if (ordinal > -1 && tdsVersion > TdsVersion.tds70) {
764 #if NET_2_0
765 lcid = (int) columns[ordinal].LCID;
766 sortId = (int) columns[ordinal].SortOrder;
767 #else
768 lcid = (int) columns[ordinal]["LCID"];
769 sortId = (int) columns[ordinal]["SortOrder"];
770 #endif
773 switch (colType) {
774 case TdsColumnType.IntN :
775 if (outParam)
776 comm.Skip (1);
777 element = GetIntValue (colType);
778 break;
779 case TdsColumnType.Int1 :
780 case TdsColumnType.Int2 :
781 case TdsColumnType.Int4 :
782 case TdsColumnType.BigInt :
783 element = GetIntValue (colType);
784 break;
785 case TdsColumnType.Image :
786 if (outParam)
787 comm.Skip (1);
788 element = GetImageValue ();
789 break;
790 case TdsColumnType.Text :
791 enc = GetEncodingFromColumnCollation (lcid, sortId);
792 if (outParam)
793 comm.Skip (1);
794 element = GetTextValue (false, enc);
795 break;
796 case TdsColumnType.NText :
797 enc = GetEncodingFromColumnCollation (lcid, sortId);
798 if (outParam)
799 comm.Skip (1);
800 element = GetTextValue (true, enc);
801 break;
802 case TdsColumnType.Char :
803 case TdsColumnType.VarChar :
804 enc = GetEncodingFromColumnCollation (lcid, sortId);
805 if (outParam)
806 comm.Skip (1);
807 element = GetStringValue (colType, false, outParam, enc);
808 break;
809 case TdsColumnType.BigVarBinary :
810 if (outParam)
811 comm.Skip (1);
812 len = comm.GetTdsShort ();
813 element = comm.GetBytes (len, true);
814 break;
816 case TdsColumnType.BigBinary :
817 if (outParam)
818 comm.Skip (2);
819 len = comm.GetTdsShort ();
820 element = comm.GetBytes (len, true);
821 break;
823 case TdsColumnType.BigBinary :
824 if (outParam)
825 comm.Skip (2);
826 element = GetBinaryValue ();
827 break;
828 case TdsColumnType.BigChar :
829 case TdsColumnType.BigVarChar :
830 enc = GetEncodingFromColumnCollation (lcid, sortId);
831 if (outParam)
832 comm.Skip (2);
833 element = GetStringValue (colType, false, outParam, enc);
834 break;
835 case TdsColumnType.NChar :
836 case TdsColumnType.BigNVarChar :
837 enc = GetEncodingFromColumnCollation (lcid, sortId);
838 if (outParam)
839 comm.Skip(2);
840 element = GetStringValue (colType, true, outParam, enc);
841 break;
842 case TdsColumnType.NVarChar :
843 enc = GetEncodingFromColumnCollation (lcid, sortId);
844 if (outParam)
845 comm.Skip (1);
846 element = GetStringValue (colType, true, outParam, enc);
847 break;
848 case TdsColumnType.Real :
849 case TdsColumnType.Float8 :
850 element = GetFloatValue (colType);
851 break;
852 case TdsColumnType.FloatN :
853 if (outParam)
854 comm.Skip (1);
855 element = GetFloatValue (colType);
856 break;
857 case TdsColumnType.SmallMoney :
858 case TdsColumnType.Money :
859 element = GetMoneyValue (colType);
860 break;
861 case TdsColumnType.MoneyN :
862 if (outParam)
863 comm.Skip (1);
864 element = GetMoneyValue (colType);
865 break;
866 case TdsColumnType.Numeric :
867 case TdsColumnType.Decimal :
868 byte precision;
869 byte scale;
870 if (outParam) {
871 comm.Skip (1);
872 precision = comm.GetByte ();
873 scale = comm.GetByte ();
875 else {
876 #if NET_2_0
877 precision = (byte) columns[ordinal].NumericPrecision;
878 scale = (byte) columns[ordinal].NumericScale;
879 #else
880 precision = (byte) columns[ordinal]["NumericPrecision"];
881 scale = (byte) columns[ordinal]["NumericScale"];
882 #endif
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);
894 break;
895 case TdsColumnType.DateTimeN :
896 if (outParam)
897 comm.Skip (1);
898 element = GetDateTimeValue (colType);
899 break;
900 case TdsColumnType.DateTime4 :
901 case TdsColumnType.DateTime :
902 element = GetDateTimeValue (colType);
903 break;
904 case TdsColumnType.VarBinary :
905 case TdsColumnType.Binary :
906 if (outParam)
907 comm.Skip (1);
908 element = GetBinaryValue ();
909 break;
910 case TdsColumnType.BitN :
911 if (outParam)
912 comm.Skip (1);
913 if (comm.GetByte () == 0)
914 element = DBNull.Value;
915 else
916 element = (comm.GetByte() != 0);
917 break;
918 case TdsColumnType.Bit :
919 int columnSize = comm.GetByte ();
920 element = (columnSize != 0);
921 break;
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;
926 break;
928 if (outParam)
929 comm.Skip (1);
931 len = comm.GetByte () & 0xff;
932 if (len > 0) {
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);
948 break;
949 default :
950 return DBNull.Value;
952 return element;
955 private object GetBinaryValue ()
957 int len;
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);
964 } else {
965 len = (comm.GetByte () & 0xff);
966 if (len != 0)
967 result = comm.GetBytes (len, true);
970 return result;
973 private object GetDateTimeValue (
974 #if NET_2_0
975 TdsColumnType? type
976 #else
977 TdsColumnType type
978 #endif
981 int len = 0;
982 object result;
984 #if NET_2_0
985 if (type == null)
986 throw new ArgumentNullException ("type");
987 #endif
988 switch (type) {
989 case TdsColumnType.DateTime4:
990 len = 4;
991 break;
992 case TdsColumnType.DateTime:
993 len = 8;
994 break;
995 case TdsColumnType.DateTimeN:
996 byte tmp = comm.Peek ();
997 if (tmp != 0 && tmp != 4 && tmp != 8)
998 break;
999 len = comm.GetByte ();
1000 break;
1003 DateTime epoch = new DateTime (1900, 1, 1);
1005 switch (len) {
1006 case 8 :
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);
1014 break;
1015 case 4 :
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 ();
1020 if (minutes != 0)
1021 result = ((DateTime) result).AddMinutes ((int) minutes);
1022 break;
1023 default:
1024 result = DBNull.Value;
1025 break;
1028 return result;
1031 private object GetDecimalValue (byte precision, byte scale)
1033 if (tdsVersion < TdsVersion.tds70)
1034 return GetDecimalValueTds50 (precision, scale);
1035 else
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;
1044 if (len < 0)
1045 return DBNull.Value;
1047 bool positive = (comm.GetByte () == 1);
1048 if (len > 16)
1049 throw new OverflowException ();
1051 for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1)
1052 bits[index] = comm.GetTdsInt ();
1054 if (bits [3] != 0)
1055 return new TdsBigDecimal (precision, scale, !positive, bits);
1056 else
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);
1065 if (len == 0)
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;
1074 if (len > 17)
1075 throw new OverflowException ();
1077 for (int i = 1, index = 0; i < len && i < 16; i +=
1078 4, index += 1) {
1079 for(int j=0; j<4; j++)
1080 if(i+j<len)
1081 easy[j]=dec_bytes[len-
1082 (i+j)];
1083 else
1084 easy[j]=0;
1085 if(!BitConverter.IsLittleEndian)
1086 easy=comm.Swap(easy);
1087 bits[index] = BitConverter.ToInt32(easy,0);
1089 if (bits [3] != 0)
1090 return new TdsBigDecimal (precision,
1091 scale, positive, bits);
1092 else
1093 return new Decimal(bits[0], bits[1], bits
1094 [2], positive, scale);
1098 private object GetFloatValue (
1099 #if NET_2_0
1100 TdsColumnType? columnType
1101 #else
1102 TdsColumnType columnType
1103 #endif
1106 #if NET_2_0
1107 if (columnType == null)
1108 throw new ArgumentNullException ("columnType");
1109 #endif
1110 int columnSize = 0;
1112 switch (columnType) {
1113 case TdsColumnType.Real:
1114 columnSize = 4;
1115 break;
1116 case TdsColumnType.Float8:
1117 columnSize = 8;
1118 break;
1119 case TdsColumnType.FloatN:
1120 columnSize = comm.GetByte ();
1121 break;
1124 switch (columnSize) {
1125 case 8 :
1126 return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
1127 case 4 :
1128 return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
1129 default :
1130 return DBNull.Value;
1134 private object GetImageValue ()
1136 byte hasValue = comm.GetByte ();
1138 if (hasValue == 0)
1139 return DBNull.Value;
1141 comm.Skip (24);
1142 int len = comm.GetTdsInt ();
1144 if (len < 0)
1145 return DBNull.Value;
1147 return (comm.GetBytes (len, true));
1150 private object GetIntValue (
1151 #if NET_2_0
1152 TdsColumnType? type
1153 #else
1154 TdsColumnType type
1155 #endif
1158 int len;
1160 #if NET_2_0
1161 if (type == null)
1162 throw new ArgumentNullException ("type");
1163 #endif
1164 switch (type) {
1165 case TdsColumnType.BigInt :
1166 len = 8;
1167 break;
1168 case TdsColumnType.IntN :
1169 len = comm.GetByte ();
1170 break;
1171 case TdsColumnType.Int4 :
1172 len = 4;
1173 break;
1174 case TdsColumnType.Int2 :
1175 len = 2;
1176 break;
1177 case TdsColumnType.Int1 :
1178 len = 1;
1179 break;
1180 default:
1181 return DBNull.Value;
1184 switch (len) {
1185 case 8:
1186 return (comm.GetTdsInt64 ());
1187 case 4 :
1188 return (comm.GetTdsInt ());
1189 case 2 :
1190 return (comm.GetTdsShort ());
1191 case 1 :
1192 return (comm.GetByte ());
1193 default:
1194 return DBNull.Value;
1198 private object GetMoneyValue (
1199 #if NET_2_0
1200 TdsColumnType? type
1201 #else
1202 TdsColumnType type
1203 #endif
1206 int len;
1208 #if NET_2_0
1209 if (type == null)
1210 throw new ArgumentNullException ("type");
1211 #endif
1212 switch (type) {
1213 case TdsColumnType.SmallMoney :
1214 case TdsColumnType.Money4 :
1215 len = 4;
1216 break;
1217 case TdsColumnType.Money :
1218 len = 8;
1219 break;
1220 case TdsColumnType.MoneyN :
1221 len = comm.GetByte ();
1222 break;
1223 default:
1224 return DBNull.Value;
1227 switch (len) {
1228 case 4: {
1229 int val = Comm.GetTdsInt ();
1230 bool negative = val < 0;
1231 if (negative)
1232 val = ~(val - 1);
1233 return new Decimal (val, 0, 0, negative, 4);
1235 case 8: {
1236 int hi = Comm.GetTdsInt ();
1237 int lo = Comm.GetTdsInt ();
1238 bool negative = hi < 0;
1240 if (negative) {
1241 hi = ~hi;
1242 lo = ~(lo - 1);
1244 return new Decimal (lo, hi, 0, negative, 4);
1246 default:
1247 return DBNull.Value;
1251 protected object GetStringValue (
1252 #if NET_2_0
1253 TdsColumnType? colType,
1254 #else
1255 TdsColumnType colType,
1256 #endif
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
1267 byte[] collation;
1268 collation = Comm.GetBytes (5, true);
1269 enc = TdsCharset.GetEncoding (collation);
1270 shortLen = true;
1271 } else {
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) {
1285 object result;
1286 if (wideChars)
1287 result = comm.GetString (len / 2, enc);
1288 else
1289 result = comm.GetString (len, false, enc);
1290 if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
1291 result = string.Empty;
1292 return result;
1294 else
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 ();
1308 if (hasValue != 16)
1309 return DBNull.Value;
1311 // 16 Byte TEXTPTR, 8 Byte TIMESTAMP
1312 comm.Skip (24);
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
1319 if (len == 0)
1320 return string.Empty;
1322 if (wideChars)
1323 result = comm.GetString (len / 2, encoder);
1324 else
1325 result = comm.GetString (len, false, encoder);
1326 len /= 2;
1328 if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
1329 result = string.Empty;
1331 return result;
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:
1350 return true;
1351 default:
1352 return false;
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:
1375 return true;
1376 default :
1377 return false;
1381 protected void LoadRow ()
1383 if (SequentialAccess) {
1384 if (isRowRead)
1385 SkipRow ();
1386 isRowRead = true;
1387 isResultRead = true;
1388 return;
1391 currentRow = new TdsDataRow ();
1393 int i = 0;
1394 foreach (TdsDataColumn column in columns) {
1395 #if NET_2_0
1396 object o = GetColumnValue (column.ColumnType, false, i);
1397 #else
1398 object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i);
1399 #endif
1400 currentRow.Add (o);
1401 if (doneProc)
1402 outputParameters.Add (o);
1404 if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
1405 currentRow.BigDecimalIndex = i;
1406 i += 1;
1410 internal static int LookupBufferSize (TdsColumnType columnType)
1412 switch (columnType) {
1413 case TdsColumnType.Int1 :
1414 case TdsColumnType.Bit :
1415 return 1;
1416 case TdsColumnType.Int2 :
1417 return 2;
1418 case TdsColumnType.Int4 :
1419 case TdsColumnType.Real :
1420 case TdsColumnType.DateTime4 :
1421 case TdsColumnType.Money4 :
1422 case TdsColumnType.SmallMoney :
1423 return 4;
1424 case TdsColumnType.Float8 :
1425 case TdsColumnType.DateTime :
1426 case TdsColumnType.Money :
1427 case TdsColumnType.BigInt :
1428 return 8;
1429 default :
1430 return 0;
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 ());
1454 try {
1455 Comm.SendPacket ();
1456 } catch (IOException ex) {
1457 connected = false;
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;
1469 int position = 0;
1471 while (position < len) {
1472 for (int j = 0; j < 3; j += 1)
1473 values[j] = comm.GetByte ();
1474 position += 3;
1476 bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
1477 if (isAlias) {
1478 if (tdsVersion >= TdsVersion.tds70) {
1479 columnNameLength = comm.GetByte ();
1480 position += 2 * columnNameLength + 1;
1482 else {
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];
1494 #if NET_2_0
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);
1501 #else
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);
1508 #endif
1512 protected abstract void ProcessColumnInfo ();
1514 protected void ProcessColumnNames ()
1516 columnNames = new ArrayList ();
1518 int totalLength = comm.GetTdsShort ();
1519 int bytesRead = 0;
1520 int i = 0;
1522 while (bytesRead < totalLength) {
1523 int columnNameLength = comm.GetByte ();
1524 string columnName = comm.GetString (columnNameLength);
1525 bytesRead = bytesRead + 1 + columnNameLength;
1526 columnNames.Add (columnName);
1527 i += 1;
1531 [MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
1532 protected void ProcessEndToken (TdsPacketSubType type)
1534 byte status = Comm.GetByte ();
1535 Comm.Skip (1);
1536 byte op = comm.GetByte ();
1537 Comm.Skip (1);
1539 int rowCount = comm.GetTdsInt ();
1540 bool validRowCount = IsValidRowCount (status,op);
1541 moreResults = ((status & 0x01) != 0);
1542 bool cancelled = ((status & 0x20) != 0);
1544 switch (type) {
1545 case TdsPacketSubType.DoneProc:
1546 doneProc = true;
1547 goto case TdsPacketSubType.Done;
1548 case TdsPacketSubType.Done:
1549 case TdsPacketSubType.DoneInProc:
1550 if (validRowCount) {
1551 if (recordsAffected == -1)
1552 recordsAffected = rowCount;
1553 else
1554 recordsAffected += rowCount;
1556 break;
1559 if (moreResults)
1560 queryInProgress = false;
1561 if (cancelled)
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 ();
1573 int cLen;
1575 switch (type) {
1576 case TdsEnvPacketSubType.BlockSize :
1577 string blockSize;
1578 cLen = comm.GetByte ();
1579 blockSize = comm.GetString (cLen);
1581 if (tdsVersion >= TdsVersion.tds70)
1582 comm.Skip (len - 2 - cLen * 2);
1583 else
1584 comm.Skip (len - 2 - cLen);
1586 packetSize = Int32.Parse (blockSize);
1587 comm.ResizeOutBuf (packetSize);
1588 break;
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);
1595 else {
1596 SetCharset (comm.GetString (cLen));
1597 comm.Skip (len - 2 - cLen);
1600 break;
1601 case TdsEnvPacketSubType.Locale :
1602 cLen = comm.GetByte ();
1603 int lcid = 0;
1604 if (tdsVersion >= TdsVersion.tds70) {
1605 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1606 comm.Skip (len - 2 - cLen * 2);
1608 else {
1609 lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int));
1610 comm.Skip (len - 2 - cLen);
1612 locale = new CultureInfo (lcid);
1613 break;
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;
1621 database = newDB;
1622 break;
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));
1630 break;
1632 default:
1633 comm.Skip (len - 1);
1634 break;
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) {
1648 comm.Skip (1);
1649 srvVersion = (uint)comm.GetTdsInt ();
1651 //Console.WriteLine ("srvVersion: {0}", srvVersion);
1652 switch (srvVersion) {
1653 case 0x00000007:
1654 tdsVersion = TdsVersion.tds70;
1655 break;
1656 case 0x00000107:
1657 tdsVersion = TdsVersion.tds80;
1658 break;
1659 case 0x01000071:
1660 tdsVersion = TdsVersion.tds81;
1661 break;
1662 case 0x02000972:
1663 tdsVersion = TdsVersion.tds90;
1664 break;
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"));
1676 } else {
1677 comm.Skip (5);
1678 short nameLength = comm.GetByte ();
1679 databaseProductName = comm.GetString (nameLength);
1680 comm.Skip (1);
1681 databaseMajorVersion = comm.GetByte ();
1682 databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
1683 comm.Skip (1);
1686 if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
1687 int last = databaseProductName.IndexOf ('\0');
1688 databaseProductName = databaseProductName.Substring (0, last);
1691 connected = true;
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);
1705 messages.Clear ();
1708 protected void ProcessMessage (TdsPacketSubType subType)
1710 GetSubPacketLength ();
1712 int number = comm.GetTdsInt ();
1713 byte state = comm.GetByte ();
1714 byte theClass = comm.GetByte ();
1715 string message;
1716 string server;
1717 string procedure;
1718 byte lineNumber;
1719 string source;
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
1727 } else
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 ();
1734 comm.Skip (1);
1735 source = String.Empty; // FIXME
1737 if (isError)
1738 OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
1739 else
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);
1747 comm.Skip (5);
1749 TdsColumnType colType = (TdsColumnType) comm.GetByte ();
1750 object value = GetColumnValue (colType, true);
1751 outputParameters.Add (value);
1754 protected void ProcessDynamic ()
1756 Comm.Skip (2);
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);
1771 switch (subType) {
1772 case TdsPacketSubType.Dynamic2:
1773 comm.Skip (comm.GetTdsInt ());
1774 break;
1775 case TdsPacketSubType.AltName:
1776 case TdsPacketSubType.AltFormat:
1777 case TdsPacketSubType.Capability:
1778 case TdsPacketSubType.ParamFormat:
1779 comm.Skip (comm.GetTdsShort ());
1780 break;
1781 case TdsPacketSubType.Dynamic:
1782 ProcessDynamic ();
1783 break;
1784 case TdsPacketSubType.EnvironmentChange:
1785 ProcessEnvironmentChange ();
1786 break;
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);
1791 break;
1792 case TdsPacketSubType.Param:
1793 ProcessOutputParam ();
1794 break;
1795 case TdsPacketSubType.LoginAck:
1796 ProcessLoginAck ();
1797 break;
1798 case TdsPacketSubType.Authentication: // TDS 7.0
1799 ProcessAuthentication ();
1800 break;
1801 case TdsPacketSubType.ReturnStatus :
1802 ProcessReturnStatus ();
1803 break;
1804 case TdsPacketSubType.ProcId:
1805 Comm.Skip (8);
1806 break;
1807 case TdsPacketSubType.Done:
1808 case TdsPacketSubType.DoneProc:
1809 case TdsPacketSubType.DoneInProc:
1810 ProcessEndToken (subType);
1811 break;
1812 case TdsPacketSubType.ColumnName:
1813 Comm.Skip (8);
1814 ProcessColumnNames ();
1815 break;
1816 case TdsPacketSubType.ColumnInfo: // TDS 4.2
1817 case TdsPacketSubType.ColumnMetadata: // TDS 7.0
1818 case TdsPacketSubType.RowFormat: // TDS 5.0
1819 Columns.Clear ();
1820 ProcessColumnInfo ();
1821 break;
1822 case TdsPacketSubType.ColumnDetail:
1823 ProcessColumnDetail ();
1824 break;
1825 case TdsPacketSubType.TableName:
1826 ProcessTableName ();
1827 break;
1828 case TdsPacketSubType.ColumnOrder:
1829 comm.Skip (comm.GetTdsShort ());
1830 break;
1831 case TdsPacketSubType.Control:
1832 comm.Skip (comm.GetTdsShort ());
1833 break;
1834 case TdsPacketSubType.Row:
1835 LoadRow ();
1836 break;
1839 // VARADHAN: TDS 8 Debugging
1840 //Console.WriteLine ("In ProcessSubPacket... exit");
1841 return subType;
1844 protected void ProcessTableName ()
1846 tableNames = new ArrayList ();
1847 int totalLength = comm.GetTdsShort ();
1848 int position = 0;
1849 int len;
1851 while (position < totalLength) {
1852 if (tdsVersion >= TdsVersion.tds70) {
1853 len = comm.GetTdsShort ();
1854 position += 2 * (len + 1);
1856 else {
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)
1872 charset = "iso_1";
1874 if (this.charset != null && this.charset == charset)
1875 return;
1877 if (charset.StartsWith ("cp")) {
1878 encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
1879 this.charset = charset;
1881 else {
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 ()
1898 comm.Skip(4);
1901 #endregion // Private Methods
1903 #if NET_2_0
1904 #region asynchronous methods
1905 protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
1906 AsyncCallback callback, object state)
1908 InitExec ();
1910 TdsAsyncResult ar = new TdsAsyncResult (callback, state);
1911 ar.TdsAsyncState.WantResults = wantResults;
1913 Comm.StartPacket (TdsPacketType.Query);
1914 Comm.Append (sql);
1915 try {
1916 Comm.SendPacket ();
1917 Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
1918 ar);
1919 } catch (IOException ex) {
1920 connected = false;
1921 throw new TdsInternalException ("Server closed the connection.", ex);
1924 return ar;
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;
1941 try {
1942 Comm.EndReadPacket (ar);
1943 if (!tdsState.WantResults)
1944 SkipToEnd ();
1945 } catch (Exception e) {
1946 result.MarkComplete (e);
1947 return;
1949 result.MarkComplete ();
1953 public virtual IAsyncResult BeginExecuteNonQuery (string sql,
1954 TdsMetaParameterCollection parameters,
1955 AsyncCallback callback,
1956 object state)
1958 // abstract, kept to be backward compatiable.
1959 throw new NotImplementedException ("should not be called!");
1962 public virtual void EndExecuteNonQuery (IAsyncResult ar)
1964 // abstract method
1965 throw new NotImplementedException ("should not be called!");
1968 public virtual IAsyncResult BeginExecuteQuery (string sql,
1969 TdsMetaParameterCollection parameters,
1970 AsyncCallback callback,
1971 object state)
1973 // abstract, kept to be backward compatiable.
1974 throw new NotImplementedException ("should not be called!");
1977 public virtual void EndExecuteQuery (IAsyncResult ar)
1979 // abstract method
1980 throw new NotImplementedException ("should not be called!");
1983 public virtual IAsyncResult BeginExecuteProcedure (string prolog,
1984 string epilog,
1985 string cmdText,
1986 bool IsNonQuery,
1987 TdsMetaParameterCollection parameters,
1988 AsyncCallback callback,
1989 object state)
1991 throw new NotImplementedException ("should not be called!");
1994 public virtual void EndExecuteProcedure (IAsyncResult ar)
1996 // abstract method
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
2014 #endif // NET_2_0