X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FMono.Data.Tds%2FMono.Data.Tds.Protocol%2FTds.cs;h=f06932c802620d1fed8f3fcb74fb9d5e0fb1ee26;hb=a83e9cf4aaaef69d3eabbca2de74005ed207374f;hp=12af0d836699b7d87c5649f09e1e8a4f8b0e34d8;hpb=64f85a65b023522d3f34e9932e6a843e0ad8fc3b;p=mono.git diff --git a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs index 12af0d83669..f06932c8026 100644 --- a/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs +++ b/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs @@ -5,11 +5,12 @@ // Tim Coleman (tim@timcoleman.com) // Sebastien Pouliot (spouliot@motus.com) // Daniel Morgan (danielmorgan@verizon.net) +// Veerapuram Varadhan (vvaradhan@novell.com) // // Copyright (C) 2002 Tim Coleman // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com) // Portions (C) 2003,2005 Daniel Morgan -// +// Portions (C) 2008,2009 Novell Inc. // // Permission is hereby granted, free of charge, to any person obtaining @@ -34,14 +35,19 @@ using Mono.Security.Protocol.Ntlm; using System; +using System.IO; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Net.Sockets; +using System.Globalization; +using System.Security; using System.Text; +using System.Runtime.InteropServices; -namespace Mono.Data.Tds.Protocol { - public abstract class Tds : Component, ITds +namespace Mono.Data.Tds.Protocol +{ + public abstract class Tds { #region Fields @@ -58,24 +64,29 @@ namespace Mono.Data.Tds.Protocol { string databaseProductName; string databaseProductVersion; int databaseMajorVersion; + CultureInfo locale = CultureInfo.InvariantCulture; + + readonly int lifeTime; + readonly DateTime created = DateTime.Now; string charset; string language; - bool connected = false; + bool connected; bool moreResults; Encoding encoder; // bool autoCommit; bool doneProc; - TdsDataRow currentRow = null; + bool pooling = true; + TdsDataRow currentRow; TdsDataColumnCollection columns; ArrayList tableNames; ArrayList columnNames; - TdsMetaParameterCollection parameters; + TdsMetaParameterCollection parameters = new TdsMetaParameterCollection (); bool queryInProgress; int cancelsRequested; @@ -89,14 +100,17 @@ namespace Mono.Data.Tds.Protocol { int recordsAffected = -1; - long StreamLength = 0; - long StreamIndex = 0; - int StreamColumnIndex = 0; + long StreamLength; + long StreamIndex; + int StreamColumnIndex; - bool sequentialAccess = false; - bool isRowRead = false; - bool isResultRead = false; - bool LoadInProgress = false; + bool sequentialAccess; + bool isRowRead; + bool isResultRead; + bool LoadInProgress; + byte [] collation; + + internal int poolStatus = 0; #endregion // Fields @@ -106,6 +120,10 @@ namespace Mono.Data.Tds.Protocol { get { return charset; } } + protected CultureInfo Locale { + get { return locale; } + } + public bool DoneProc { get { return doneProc; } } @@ -134,11 +152,16 @@ namespace Mono.Data.Tds.Protocol { get { return dataSource; } } - public bool IsConnected { - get { return connected; } + public virtual bool IsConnected { + get { return connected && comm != null && comm.IsConnected (); } set { connected = value; } } + public bool Pooling { + get { return pooling; } + set { pooling = value; } + } + public bool MoreResults { get { return moreResults; } set { moreResults = value; } @@ -180,6 +203,24 @@ namespace Mono.Data.Tds.Protocol { set { sequentialAccess = value; } } + public byte[] Collation { + get {return collation; } + } + + public TdsVersion ServerTdsVersion { + get { + switch (databaseMajorVersion) { + case 4: return TdsVersion.tds42; + case 5: return TdsVersion.tds50; + case 7: return TdsVersion.tds70; + case 8: return TdsVersion.tds80; + case 9: return TdsVersion.tds90; + case 10: return TdsVersion.tds100; + default: return tdsVersion; // return client's version + } + } + } + private void SkipRow () { SkipToColumnIndex (Columns.Count); @@ -199,7 +240,13 @@ namespace Mono.Data.Tds.Protocol { throw new Exception ("Cannot Skip to a colindex less than the curr index"); while (colIndex != StreamColumnIndex) { - TdsColumnType colType = (TdsColumnType)Columns[StreamColumnIndex]["ColumnType"]; +#if NET_2_0 + TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType; + if (colType == null) + throw new Exception ("Column type unset."); +#else + TdsColumnType colType = (TdsColumnType) Columns [StreamColumnIndex]["ColumnType"]; +#endif if (!(colType == TdsColumnType.Image || colType == TdsColumnType.Text || colType == TdsColumnType.NText)) { @@ -226,7 +273,11 @@ namespace Mono.Data.Tds.Protocol { if (colIndex != StreamColumnIndex) SkipToColumnIndex (colIndex); +#if NET_2_0 + object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex); +#else object o = GetColumnValue ((TdsColumnType)Columns[colIndex]["ColumnType"], false, colIndex); +#endif StreamColumnIndex++; return o; } @@ -234,27 +285,46 @@ namespace Mono.Data.Tds.Protocol { public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size) { if (colIndex < StreamColumnIndex) - throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex); + throw new InvalidOperationException ("Invalid attempt to read from column ordinal" + colIndex); + try { + if (colIndex != StreamColumnIndex) + SkipToColumnIndex (colIndex); - if (colIndex != StreamColumnIndex) - SkipToColumnIndex (colIndex); - - if (!LoadInProgress) - BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]); + if (!LoadInProgress) { +#if NET_2_0 + BeginLoad (Columns[colIndex].ColumnType); +#else + BeginLoad ((TdsColumnType)Columns[colIndex]["ColumnType"]); +#endif + } - if (buffer == null) { - return StreamLength; + if (buffer == null) + return StreamLength; + return LoadData (fieldIndex, buffer, bufferIndex, size); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); } - return LoadData (fieldIndex, buffer, bufferIndex, size); } - private void BeginLoad(TdsColumnType colType) + private void BeginLoad ( +#if NET_2_0 + TdsColumnType? colType +#else + TdsColumnType colType +#endif + ) { if (LoadInProgress) EndLoad (); StreamLength = 0; - + +#if NET_2_0 + if (colType == null) + throw new ArgumentNullException ("colType"); +#endif + switch (colType) { case TdsColumnType.Text : case TdsColumnType.NText: @@ -262,6 +332,10 @@ namespace Mono.Data.Tds.Protocol { if (Comm.GetByte () != 0) { Comm.Skip (24); StreamLength = Comm.GetTdsInt (); + } else { + // use -2 to indicate that we're dealing + // with a NULL value + StreamLength = -2; } break; case TdsColumnType.BigVarChar: @@ -304,16 +378,26 @@ namespace Mono.Data.Tds.Protocol { return StreamLength; if (fieldIndex < StreamIndex) - throw new InvalidOperationException ("field index less than stream pos"); + throw new InvalidOperationException (string.Format ( + "Attempting to read at dataIndex '{0}' is " + + "not allowed as this is less than the " + + "current position. You must read from " + + "dataIndex '{1}' or greater.", + fieldIndex, StreamIndex)); if (fieldIndex >= (StreamLength + StreamIndex)) return 0; - // Skip to the index - Comm.Skip ((int) (fieldIndex - StreamIndex)); + // determine number of bytes to skip + int skip = (int) (fieldIndex - StreamIndex); + // skip bytes + Comm.Skip (skip); + // update the current position StreamIndex += (fieldIndex - StreamIndex); + // update the remaining length + StreamLength -= skip; - // Load the reqd amt of bytes + // Load the reqd amt of bytes int loadlen = (int) ((size > StreamLength) ? StreamLength : size); byte[] arr = Comm.GetBytes (loadlen, true); @@ -337,11 +421,23 @@ namespace Mono.Data.Tds.Protocol { #region Constructors public Tds (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion) + : this (dataSource, port, packetSize, timeout, 0, tdsVersion) + { + } + + public Tds (string dataSource, int port, int packetSize, int timeout, int lifeTime, TdsVersion tdsVersion) { this.tdsVersion = tdsVersion; this.packetSize = packetSize; this.dataSource = dataSource; + this.columns = new TdsDataColumnCollection (); + this.lifeTime = lifeTime; + InitComm (port, timeout); + } + + protected virtual void InitComm (int port, int timeout) + { comm = new TdsComm (dataSource, port, packetSize, timeout, tdsVersion); } @@ -349,11 +445,34 @@ namespace Mono.Data.Tds.Protocol { #region Public Methods + internal bool Expired { + get { + if (lifeTime == 0) + return false; + return DateTime.Now > (created + TimeSpan.FromSeconds (lifeTime)); + } + } + internal protected void InitExec () { - // clean up + // clean up moreResults = true; doneProc = false; + + // Reset "read" status variables - used in case of SequentialAccess + isResultRead = false; + isRowRead = false; + StreamLength = 0; + StreamIndex = 0; + StreamColumnIndex = 0; + LoadInProgress = false; + + // Reset more variables + queryInProgress = false; + cancelsRequested = 0; + cancelsProcessed = 0; + recordsAffected = -1; + messages.Clear (); outputParameters.Clear (); } @@ -363,10 +482,15 @@ namespace Mono.Data.Tds.Protocol { if (queryInProgress) { if (cancelsRequested == cancelsProcessed) { comm.StartPacket (TdsPacketType.Cancel); - comm.SendPacket (); + try { + Comm.SendPacket (); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } cancelsRequested += 1; } - } + } } public abstract bool Connect (TdsConnectionParameters connectionParameters); @@ -377,13 +501,17 @@ namespace Mono.Data.Tds.Protocol { return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0); } - public void Disconnect () + public virtual void Disconnect () { - comm.StartPacket (TdsPacketType.Logoff); - comm.Append ((byte) 0); - comm.SendPacket (); - comm.Close (); + try { + comm.StartPacket (TdsPacketType.Logoff); + comm.Append ((byte) 0); + comm.SendPacket (); + } catch { + // We're closing the socket anyway + } connected = false; + comm.Close (); } public virtual bool Reset () @@ -422,17 +550,49 @@ namespace Mono.Data.Tds.Protocol { throw new NotSupportedException (); } + internal void ExecBulkCopyMetaData (int timeout, bool wantResults) + { + moreResults = true; + try { + Comm.SendPacket (); + CheckForData (timeout); + if (!wantResults) + SkipToEnd (); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } + } + + internal void ExecBulkCopy (int timeout, bool wantResults) + { + moreResults = true; + try { + Comm.SendPacket (); + CheckForData (timeout); + if (!wantResults) + SkipToEnd (); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } + } + protected void ExecuteQuery (string sql, int timeout, bool wantResults) { InitExec (); Comm.StartPacket (TdsPacketType.Query); Comm.Append (sql); - Comm.SendPacket (); - - CheckForData (timeout); - if (!wantResults) - SkipToEnd (); + try { + Comm.SendPacket (); + CheckForData (timeout); + if (!wantResults) + SkipToEnd (); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } } protected virtual void ExecRPC (string rpcName, TdsMetaParameterCollection parameters, @@ -451,10 +611,15 @@ namespace Mono.Data.Tds.Protocol { Comm.Append (rpcNameBytes); Comm.Append (mask); - Comm.SendPacket (); - CheckForData (timeout); - if (!wantResults) - SkipToEnd (); + try { + Comm.SendPacket (); + CheckForData (timeout); + if (!wantResults) + SkipToEnd (); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } } public bool NextResult () @@ -480,24 +645,21 @@ namespace Mono.Data.Tds.Protocol { moreResults = false; break; } - switch (subType) { case TdsPacketSubType.ColumnInfo: case TdsPacketSubType.ColumnMetadata: - case TdsPacketSubType.RowFormat: + case TdsPacketSubType.RowFormat: byte peek = Comm.Peek (); done = (peek != (byte) TdsPacketSubType.TableName); if (done && doneProc && peek == (byte) TdsPacketSubType.Row) { outputParams = true; done = false; } - break; case TdsPacketSubType.TableName: // done = true; peek = Comm.Peek (); done = (peek != (byte) TdsPacketSubType.ColumnDetail); - break; case TdsPacketSubType.ColumnDetail: done = true; @@ -550,7 +712,12 @@ namespace Mono.Data.Tds.Protocol { public void SkipToEnd () { - while (NextResult ()) { /* DO NOTHING */ } + try { + while (NextResult ()) { /* DO NOTHING */ } + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } } public virtual void Unprepare (string statementId) @@ -581,16 +748,52 @@ namespace Mono.Data.Tds.Protocol { return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state)); } - private object GetColumnValue (TdsColumnType colType, bool outParam) + private Encoding GetEncodingFromColumnCollation (int lcid, int sortId) + { + if (sortId != 0) + return TdsCharset.GetEncodingFromSortOrder (sortId); + else + return TdsCharset.GetEncodingFromLCID (lcid); + } + + protected object GetColumnValue ( +#if NET_2_0 + TdsColumnType? colType, +#else + TdsColumnType colType, +#endif + bool outParam) { return GetColumnValue (colType, outParam, -1); } - private object GetColumnValue (TdsColumnType colType, bool outParam, int ordinal) + private object GetColumnValue ( +#if NET_2_0 + TdsColumnType? colType, +#else + TdsColumnType colType, +#endif + bool outParam, int ordinal) { int len; object element = null; + Encoding enc = null; + int lcid = 0, sortId = 0; +#if NET_2_0 + if (colType == null) + throw new ArgumentNullException ("colType"); +#endif + if (ordinal > -1 && tdsVersion > TdsVersion.tds70) { +#if NET_2_0 + lcid = (int) columns[ordinal].LCID; + sortId = (int) columns[ordinal].SortOrder; +#else + lcid = (int) columns[ordinal]["LCID"]; + sortId = (int) columns[ordinal]["SortOrder"]; +#endif + } + switch (colType) { case TdsColumnType.IntN : if (outParam) @@ -600,6 +803,7 @@ namespace Mono.Data.Tds.Protocol { case TdsColumnType.Int1 : case TdsColumnType.Int2 : case TdsColumnType.Int4 : + case TdsColumnType.BigInt : element = GetIntValue (colType); break; case TdsColumnType.Image : @@ -608,35 +812,62 @@ namespace Mono.Data.Tds.Protocol { element = GetImageValue (); break; case TdsColumnType.Text : + enc = GetEncodingFromColumnCollation (lcid, sortId); if (outParam) comm.Skip (1); - element = GetTextValue (false); + element = GetTextValue (false, enc); break; case TdsColumnType.NText : + enc = GetEncodingFromColumnCollation (lcid, sortId); if (outParam) comm.Skip (1); - element = GetTextValue (true); + element = GetTextValue (true, enc); break; case TdsColumnType.Char : case TdsColumnType.VarChar : + enc = GetEncodingFromColumnCollation (lcid, sortId); if (outParam) comm.Skip (1); - element = GetStringValue (false, false); + element = GetStringValue (colType, false, outParam, enc); break; case TdsColumnType.BigVarBinary : - comm.GetTdsShort (); + if (outParam) + comm.Skip (1); + len = comm.GetTdsShort (); + element = comm.GetBytes (len, true); + break; + /* + case TdsColumnType.BigBinary : + if (outParam) + comm.Skip (2); len = comm.GetTdsShort (); element = comm.GetBytes (len, true); break; + */ + case TdsColumnType.BigBinary : + if (outParam) + comm.Skip (2); + element = GetBinaryValue (); + break; + case TdsColumnType.BigChar : case TdsColumnType.BigVarChar : - comm.Skip (2); - element = GetStringValue (false, false); + enc = GetEncodingFromColumnCollation (lcid, sortId); + if (outParam) + comm.Skip (2); + element = GetStringValue (colType, false, outParam, enc); break; case TdsColumnType.NChar : + case TdsColumnType.BigNVarChar : + enc = GetEncodingFromColumnCollation (lcid, sortId); + if (outParam) + comm.Skip(2); + element = GetStringValue (colType, true, outParam, enc); + break; case TdsColumnType.NVarChar : + enc = GetEncodingFromColumnCollation (lcid, sortId); if (outParam) comm.Skip (1); - element = GetStringValue (true, false); + element = GetStringValue (colType, true, outParam, enc); break; case TdsColumnType.Real : case TdsColumnType.Float8 : @@ -666,11 +897,24 @@ namespace Mono.Data.Tds.Protocol { scale = comm.GetByte (); } else { +#if NET_2_0 + precision = (byte) columns[ordinal].NumericPrecision; + scale = (byte) columns[ordinal].NumericScale; +#else precision = (byte) columns[ordinal]["NumericPrecision"]; scale = (byte) columns[ordinal]["NumericScale"]; +#endif } element = GetDecimalValue (precision, scale); + + // workaround for fact that TDS 7.0 returns + // bigint as decimal (19,0), and client code + // expects it to be returned as a long + if (scale == 0 && precision <= 19 && tdsVersion == TdsVersion.tds70) { + if (!(element is System.DBNull)) + element = Convert.ToInt64 (element); + } break; case TdsColumnType.DateTimeN : if (outParam) @@ -701,10 +945,13 @@ namespace Mono.Data.Tds.Protocol { break; case TdsColumnType.UniqueIdentifier : if (comm.Peek () != 16) { // If it's null, then what to do? - /*byte swallowed =*/ comm.GetByte(); + /*byte swallowed =*/ comm.GetByte(); element = DBNull.Value; break; } + if (outParam) + comm.Skip (1); + len = comm.GetByte () & 0xff; if (len > 0) { byte[] guidBytes = comm.GetBytes (len, true); @@ -726,7 +973,6 @@ namespace Mono.Data.Tds.Protocol { default : return DBNull.Value; } - return element; } @@ -734,24 +980,35 @@ namespace Mono.Data.Tds.Protocol { { int len; object result = DBNull.Value; - if (tdsVersion == TdsVersion.tds70) { + + if (tdsVersion >= TdsVersion.tds70) { len = comm.GetTdsShort (); - if (len != 0xffff && len > 0) + if (len != 0xffff && len >= 0) result = comm.GetBytes (len, true); - } - else { + } else { len = (comm.GetByte () & 0xff); if (len != 0) result = comm.GetBytes (len, true); } + return result; } - private object GetDateTimeValue (TdsColumnType type) + private object GetDateTimeValue ( +#if NET_2_0 + TdsColumnType? type +#else + TdsColumnType type +#endif + ) { int len = 0; object result; - + +#if NET_2_0 + if (type == null) + throw new ArgumentNullException ("type"); +#endif switch (type) { case TdsColumnType.DateTime4: len = 4; @@ -773,15 +1030,15 @@ namespace Mono.Data.Tds.Protocol { case 8 : result = epoch.AddDays (comm.GetTdsInt ()); int seconds = comm.GetTdsInt (); - long millis = ((((long) seconds) % 300L) * 1000L) / 300L; + long millis = (long) System.Math.Round (((((long) seconds) % 300L) * 1000L) / 300f); if (seconds != 0 || millis != 0) { result = ((DateTime) result).AddSeconds (seconds / 300); result = ((DateTime) result).AddMilliseconds (millis); } break; case 4 : - // MSDN says small datetime is stored in 2 bytes as no of days - // *after* 1/1/1900. so, cast to unsigned short + // MSDN says small datetime is stored in 2 bytes as no of days + // *after* 1/1/1900. so, cast to unsigned short result = epoch.AddDays ((ushort) comm.GetTdsShort ()); short minutes = comm.GetTdsShort (); if (minutes != 0) @@ -795,14 +1052,16 @@ namespace Mono.Data.Tds.Protocol { return result; } - private object GetDecimalValue (byte precision, byte scale) { + private object GetDecimalValue (byte precision, byte scale) + { if (tdsVersion < TdsVersion.tds70) return GetDecimalValueTds50 (precision, scale); else return GetDecimalValueTds70 (precision, scale); } - private object GetDecimalValueTds70 (byte precision, byte scale) { + private object GetDecimalValueTds70 (byte precision, byte scale) + { int[] bits = new int[4] {0,0,0,0}; int len = (comm.GetByte() & 0xff) - 1; @@ -810,27 +1069,27 @@ namespace Mono.Data.Tds.Protocol { return DBNull.Value; bool positive = (comm.GetByte () == 1); - if (len > 16) throw new OverflowException (); for (int i = 0, index = 0; i < len && i < 16; i += 4, index += 1) bits[index] = comm.GetTdsInt (); - + if (bits [3] != 0) return new TdsBigDecimal (precision, scale, !positive, bits); else return new Decimal (bits[0], bits[1], bits[2], !positive, scale); } - private object GetDecimalValueTds50 (byte precision, byte scale) { + private object GetDecimalValueTds50 (byte precision, byte scale) + { int[] bits = new int[4] {0,0,0,0}; int len = (comm.GetByte() & 0xff); if (len == 0) return DBNull.Value; - byte[] dec_bytes=comm.GetBytes(len,false); + byte[] dec_bytes=comm.GetBytes(len,false); byte[] easy=new byte[4]; @@ -860,8 +1119,18 @@ namespace Mono.Data.Tds.Protocol { } - private object GetFloatValue (TdsColumnType columnType) + private object GetFloatValue ( +#if NET_2_0 + TdsColumnType? columnType +#else + TdsColumnType columnType +#endif + ) { +#if NET_2_0 + if (columnType == null) + throw new ArgumentNullException ("columnType"); +#endif int columnSize = 0; switch (columnType) { @@ -902,11 +1171,24 @@ namespace Mono.Data.Tds.Protocol { return (comm.GetBytes (len, true)); } - private object GetIntValue (TdsColumnType type) + private object GetIntValue ( +#if NET_2_0 + TdsColumnType? type +#else + TdsColumnType type +#endif + ) { int len; +#if NET_2_0 + if (type == null) + throw new ArgumentNullException ("type"); +#endif switch (type) { + case TdsColumnType.BigInt : + len = 8; + break; case TdsColumnType.IntN : len = comm.GetByte (); break; @@ -924,6 +1206,8 @@ namespace Mono.Data.Tds.Protocol { } switch (len) { + case 8: + return (comm.GetTdsInt64 ()); case 4 : return (comm.GetTdsInt ()); case 2 : @@ -935,12 +1219,20 @@ namespace Mono.Data.Tds.Protocol { } } - [MonoTODO] - private object GetMoneyValue (TdsColumnType type) + private object GetMoneyValue ( +#if NET_2_0 + TdsColumnType? type +#else + TdsColumnType type +#endif + ) { int len; - object result = null; +#if NET_2_0 + if (type == null) + throw new ArgumentNullException ("type"); +#endif switch (type) { case TdsColumnType.SmallMoney : case TdsColumnType.Money4 : @@ -957,32 +1249,70 @@ namespace Mono.Data.Tds.Protocol { } switch (len) { - case 4: - return new Decimal (Comm.GetTdsInt (), 0, 0, false, 4); - case 8: + case 4: { + int val = Comm.GetTdsInt (); + bool negative = val < 0; + if (negative) + val = ~(val - 1); + return new Decimal (val, 0, 0, negative, 4); + } + case 8: { int hi = Comm.GetTdsInt (); int lo = Comm.GetTdsInt (); - return new Decimal (lo, hi, 0, false, 4); + bool negative = hi < 0; + + if (negative) { + hi = ~hi; + lo = ~(lo - 1); + } + return new Decimal (lo, hi, 0, negative, 4); + } default: return DBNull.Value; } } - private object GetStringValue (bool wideChars, bool outputParam) + protected object GetStringValue ( +#if NET_2_0 + TdsColumnType? colType, +#else + TdsColumnType colType, +#endif + bool wideChars, bool outputParam, Encoding encoder) { - bool shortLen = (tdsVersion == TdsVersion.tds70) && (wideChars || !outputParam); + bool shortLen = false; + Encoding enc = encoder; + + if (tdsVersion > TdsVersion.tds70 && outputParam && + (colType == TdsColumnType.BigChar || colType == TdsColumnType.BigNVarChar || + colType == TdsColumnType.BigVarChar || colType == TdsColumnType.NChar || + colType == TdsColumnType.NVarChar)) { + // Read collation for SqlServer 2000 and beyond + byte[] collation; + collation = Comm.GetBytes (5, true); + enc = TdsCharset.GetEncoding (collation); + shortLen = true; + } else { + shortLen = (tdsVersion >= TdsVersion.tds70) && (wideChars || !outputParam); + } + int len = shortLen ? comm.GetTdsShort () : (comm.GetByte () & 0xff); + return GetStringValue (wideChars, len, enc); + } + protected object GetStringValue (bool wideChars, int len, Encoding enc) + { if (tdsVersion < TdsVersion.tds70 && len == 0) return DBNull.Value; + else if (len >= 0) { object result; if (wideChars) - result = comm.GetString (len / 2); + result = comm.GetString (len / 2, enc); else - result = comm.GetString (len, false); + result = comm.GetString (len, false, enc); if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" ")) - result = ""; + result = string.Empty; return result; } else @@ -994,7 +1324,7 @@ namespace Mono.Data.Tds.Protocol { return comm.GetTdsShort (); } - private object GetTextValue (bool wideChars) + private object GetTextValue (bool wideChars, Encoding encoder) { string result = null; byte hasValue = comm.GetByte (); @@ -1011,18 +1341,40 @@ namespace Mono.Data.Tds.Protocol { // this method is called only for Text and NText. Hence will // return a empty string if (len == 0) - return ""; + return string.Empty; if (wideChars) - result = comm.GetString (len / 2); + result = comm.GetString (len / 2, encoder); else - result = comm.GetString (len, false); + result = comm.GetString (len, false, encoder); len /= 2; if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ") - result = ""; + result = string.Empty; - return result; + return result; + } + + internal bool IsBlobType (TdsColumnType columnType) + { + return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText); + } + + internal bool IsLargeType (TdsColumnType columnType) + { + return ((byte) columnType > 128); + } + + protected bool IsWideType (TdsColumnType columnType) + { + switch (columnType) { + case TdsColumnType.NChar: + case TdsColumnType.NText: + case TdsColumnType.NVarChar: + return true; + default: + return false; + } } internal static bool IsFixedSizeColumn (TdsColumnType columnType) @@ -1031,6 +1383,7 @@ namespace Mono.Data.Tds.Protocol { case TdsColumnType.Int1 : case TdsColumnType.Int2 : case TdsColumnType.Int4 : + case TdsColumnType.BigInt : case TdsColumnType.Float8 : case TdsColumnType.DateTime : case TdsColumnType.Bit : @@ -1039,24 +1392,11 @@ namespace Mono.Data.Tds.Protocol { case TdsColumnType.SmallMoney : case TdsColumnType.Real : case TdsColumnType.DateTime4 : + /* + case TdsColumnType.Decimal: + case TdsColumnType.Numeric: + */ return true; - case TdsColumnType.IntN : - case TdsColumnType.MoneyN : - case TdsColumnType.VarChar : - case TdsColumnType.NVarChar : - case TdsColumnType.DateTimeN : - case TdsColumnType.FloatN : - case TdsColumnType.Char : - case TdsColumnType.NChar : - case TdsColumnType.NText : - case TdsColumnType.Image : - case TdsColumnType.VarBinary : - case TdsColumnType.Binary : - case TdsColumnType.Decimal : - case TdsColumnType.Numeric : - case TdsColumnType.BitN : - case TdsColumnType.UniqueIdentifier : - return false; default : return false; } @@ -1076,7 +1416,11 @@ namespace Mono.Data.Tds.Protocol { int i = 0; foreach (TdsDataColumn column in columns) { - object o = GetColumnValue ((TdsColumnType) column["ColumnType"], false, i); +#if NET_2_0 + object o = GetColumnValue (column.ColumnType, false, i); +#else + object o = GetColumnValue ((TdsColumnType)column["ColumnType"], false, i); +#endif currentRow.Add (o); if (doneProc) outputParameters.Add (o); @@ -1104,41 +1448,13 @@ namespace Mono.Data.Tds.Protocol { case TdsColumnType.Float8 : case TdsColumnType.DateTime : case TdsColumnType.Money : + case TdsColumnType.BigInt : return 8; default : return 0; } } - private int LookupDisplaySize (TdsColumnType columnType) - { - switch (columnType) { - case TdsColumnType.Int1 : - return 3; - case TdsColumnType.Int2 : - return 6; - case TdsColumnType.Int4 : - return 11; - case TdsColumnType.Real : - return 14; - case TdsColumnType.Float8 : - return 24; - case TdsColumnType.DateTime : - return 23; - case TdsColumnType.DateTime4 : - return 16; - case TdsColumnType.Bit : - return 1; - case TdsColumnType.Money : - return 21; - case TdsColumnType.Money4 : - case TdsColumnType.SmallMoney : - return 12; - default: - return 0; - } - } - protected internal int ProcessAuthentication () { int pdu_size = Comm.GetTdsShort (); @@ -1149,17 +1465,21 @@ namespace Mono.Data.Tds.Protocol { // 0x0200 Negotiate NTLM // 0x8000 Negotiate Always Sign - Type3Message t3 = new Type3Message (); - t3.Challenge = t2.Nonce; + Type3Message t3 = new Type3Message (t2); t3.Domain = this.connectionParms.DefaultDomain; t3.Host = this.connectionParms.Hostname; t3.Username = this.connectionParms.User; - t3.Password = this.connectionParms.Password; + t3.Password = GetPlainPassword(this.connectionParms.Password); Comm.StartPacket (TdsPacketType.SspAuth); // 0x11 Comm.Append (t3.GetBytes ()); - Comm.SendPacket (); + try { + Comm.SendPacket (); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } return 1; // TDS_SUCCEED } @@ -1178,7 +1498,7 @@ namespace Mono.Data.Tds.Protocol { bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0); if (isAlias) { - if (tdsVersion == TdsVersion.tds70) { + if (tdsVersion >= TdsVersion.tds70) { columnNameLength = comm.GetByte (); position += 2 * columnNameLength + 1; } @@ -1193,17 +1513,26 @@ namespace Mono.Data.Tds.Protocol { byte tableIndex = (byte) (values[1] - (byte) 1); bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0); - columns [index]["IsExpression"] = isExpression; - columns [index]["IsKey"] = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0); - columns [index]["IsHidden"] = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0); - columns [index]["IsAliased"] = isAlias; - - columns [index]["BaseColumnName"] = ((isAlias) ? baseColumnName : null); - columns [index]["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null); + TdsDataColumn column = columns [index]; +#if NET_2_0 + column.IsHidden = ((values[2] & (byte) TdsColumnStatus.Hidden) != 0); + column.IsExpression = isExpression; + column.IsKey = ((values[2] & (byte) TdsColumnStatus.IsKey) != 0); + column.IsAliased = isAlias; + column.BaseColumnName = ((isAlias) ? baseColumnName : null); + column.BaseTableName = ((!isExpression) ? (string) tableNames [tableIndex] : null); +#else + column ["IsHidden"] = ((values [2] & (byte) TdsColumnStatus.Hidden) != 0); + column ["IsExpression"] = isExpression; + column ["IsKey"] = ((values [2] & (byte) TdsColumnStatus.IsKey) != 0); + column ["IsAliased"] = isAlias; + column ["BaseColumnName"] = ((isAlias) ? baseColumnName : null); + column ["BaseTableName"] = ((!isExpression) ? tableNames [tableIndex] : null); +#endif } } - protected abstract TdsDataColumnCollection ProcessColumnInfo (); + protected abstract void ProcessColumnInfo (); protected void ProcessColumnNames () { @@ -1231,7 +1560,6 @@ namespace Mono.Data.Tds.Protocol { Comm.Skip (1); int rowCount = comm.GetTdsInt (); - bool validRowCount = IsValidRowCount (status,op); moreResults = ((status & 0x01) != 0); bool cancelled = ((status & 0x20) != 0); @@ -1261,6 +1589,8 @@ namespace Mono.Data.Tds.Protocol { protected void ProcessEnvironmentChange () { + // VARADHAN: TDS 8 Debugging + //Console.WriteLine ("In ProcessEnvironmentChange... entry"); int len = GetSubPacketLength (); TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte (); int cLen; @@ -1268,10 +1598,10 @@ namespace Mono.Data.Tds.Protocol { switch (type) { case TdsEnvPacketSubType.BlockSize : string blockSize; - cLen = comm.GetByte () & 0xff; + cLen = comm.GetByte (); blockSize = comm.GetString (cLen); - if (tdsVersion == TdsVersion.tds70) + if (tdsVersion >= TdsVersion.tds70) comm.Skip (len - 2 - cLen * 2); else comm.Skip (len - 2 - cLen); @@ -1280,7 +1610,7 @@ namespace Mono.Data.Tds.Protocol { comm.ResizeOutBuf (packetSize); break; case TdsEnvPacketSubType.CharSet : - cLen = comm.GetByte () & 0xff; + cLen = comm.GetByte (); if (tdsVersion == TdsVersion.tds70) { SetCharset (comm.GetString (cLen)); comm.Skip (len - 2 - cLen * 2); @@ -1290,9 +1620,22 @@ namespace Mono.Data.Tds.Protocol { comm.Skip (len - 2 - cLen); } + break; + case TdsEnvPacketSubType.Locale : + cLen = comm.GetByte (); + int lcid = 0; + if (tdsVersion >= TdsVersion.tds70) { + lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int)); + comm.Skip (len - 2 - cLen * 2); + } + else { + lcid = (int) Convert.ChangeType (comm.GetString (cLen), typeof (int)); + comm.Skip (len - 2 - cLen); + } + locale = new CultureInfo (lcid); break; case TdsEnvPacketSubType.Database : - cLen = comm.GetByte () & 0xff; + cLen = comm.GetByte (); string newDB = comm.GetString (cLen); cLen = comm.GetByte () & 0xff; comm.GetString (cLen); @@ -1300,26 +1643,60 @@ namespace Mono.Data.Tds.Protocol { originalDatabase = newDB; database = newDB; break; + case TdsEnvPacketSubType.CollationInfo: + //Console.WriteLine ("ProcessEnvironmentChange::Got collation info"); + cLen = comm.GetByte (); + collation = comm.GetBytes (cLen, true); + lcid = TdsCollation.LCID (collation); + locale = new CultureInfo (lcid); + SetCharset (TdsCharset.GetEncoding (collation)); + break; + default: comm.Skip (len - 1); break; } + // VARADHAN: TDS 8 Debugging + //Console.WriteLine ("In ProcessEnvironmentChange... exit"); } protected void ProcessLoginAck () { + uint srvVersion = 0; GetSubPacketLength (); + + //Console.WriteLine ("ProcessLoginAck: B4 tdsVersion:{0}", tdsVersion); + // Valid only for a Login7 request + if (tdsVersion >= TdsVersion.tds70) { + comm.Skip (1); + srvVersion = (uint)comm.GetTdsInt (); - if (tdsVersion == TdsVersion.tds70) { - comm.Skip (5); + //Console.WriteLine ("srvVersion: {0}", srvVersion); + switch (srvVersion) { + case 0x00000007: + tdsVersion = TdsVersion.tds70; + break; + case 0x00000107: + tdsVersion = TdsVersion.tds80; + break; + case 0x01000071: + tdsVersion = TdsVersion.tds81; + break; + case 0x02000972: + tdsVersion = TdsVersion.tds90; + break; + } + //Console.WriteLine ("ProcessLoginAck: after tdsVersion:{0}", tdsVersion); + } + + if (tdsVersion >= TdsVersion.tds70) { int nameLength = comm.GetByte (); databaseProductName = comm.GetString (nameLength); databaseMajorVersion = comm.GetByte (); databaseProductVersion = String.Format ("{0}.{1}.{2}", databaseMajorVersion.ToString("00"), comm.GetByte ().ToString("00"), (256 * comm.GetByte () + comm.GetByte ()).ToString("0000")); - } - else { + } else { comm.Skip (5); short nameLength = comm.GetByte (); databaseProductName = comm.GetString (nameLength); @@ -1335,6 +1712,7 @@ namespace Mono.Data.Tds.Protocol { } connected = true; + //Console.WriteLine ("databaseProductVersion:{0}", databaseProductVersion); } protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e) @@ -1385,15 +1763,14 @@ namespace Mono.Data.Tds.Protocol { messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state)); } - protected void ProcessOutputParam () + protected virtual void ProcessOutputParam () { GetSubPacketLength (); - comm.GetString (comm.GetByte () & 0xff); + /*string paramName = */comm.GetString (comm.GetByte () & 0xff); comm.Skip (5); TdsColumnType colType = (TdsColumnType) comm.GetByte (); object value = GetColumnValue (colType, true); - outputParameters.Add (value); } @@ -1407,8 +1784,13 @@ namespace Mono.Data.Tds.Protocol { protected virtual TdsPacketSubType ProcessSubPacket () { + // VARADHAN: TDS 8 Debugging + // Console.WriteLine ("In ProcessSubPacket... entry"); + TdsPacketSubType subType = (TdsPacketSubType) comm.GetByte (); + // VARADHAN: TDS 8 Debugging + //Console.WriteLine ("Subpacket type: {0}", subType); switch (subType) { case TdsPacketSubType.Dynamic2: comm.Skip (comm.GetTdsInt ()); @@ -1440,7 +1822,7 @@ namespace Mono.Data.Tds.Protocol { ProcessAuthentication (); break; case TdsPacketSubType.ReturnStatus : - Comm.Skip (4); + ProcessReturnStatus (); break; case TdsPacketSubType.ProcId: Comm.Skip (8); @@ -1457,7 +1839,8 @@ namespace Mono.Data.Tds.Protocol { case TdsPacketSubType.ColumnInfo: // TDS 4.2 case TdsPacketSubType.ColumnMetadata: // TDS 7.0 case TdsPacketSubType.RowFormat: // TDS 5.0 - columns = ProcessColumnInfo (); + Columns.Clear (); + ProcessColumnInfo (); break; case TdsPacketSubType.ColumnDetail: ProcessColumnDetail (); @@ -1476,6 +1859,8 @@ namespace Mono.Data.Tds.Protocol { break; } + // VARADHAN: TDS 8 Debugging + //Console.WriteLine ("In ProcessSubPacket... exit"); return subType; } @@ -1487,7 +1872,7 @@ namespace Mono.Data.Tds.Protocol { int len; while (position < totalLength) { - if (tdsVersion == TdsVersion.tds70) { + if (tdsVersion >= TdsVersion.tds70) { len = comm.GetTdsShort (); position += 2 * (len + 1); } @@ -1496,9 +1881,14 @@ namespace Mono.Data.Tds.Protocol { position += len + 1; } tableNames.Add (comm.GetString (len)); - } + } } + protected void SetCharset (Encoding encoder) + { + comm.Encoder = encoder; + } + protected void SetCharset (string charset) { if (charset == null || charset.Length > 30) @@ -1515,7 +1905,7 @@ namespace Mono.Data.Tds.Protocol { encoder = Encoding.GetEncoding ("iso-8859-1"); this.charset = "iso_1"; } - comm.Encoder = encoder; + SetCharset (encoder); } protected void SetLanguage (string language) @@ -1526,6 +1916,25 @@ namespace Mono.Data.Tds.Protocol { this.language = language; } + protected virtual void ProcessReturnStatus () + { + comm.Skip(4); + } + + public static string GetPlainPassword(SecureString secPass) + { + IntPtr plainString = IntPtr.Zero; + try + { + plainString = Marshal.SecureStringToGlobalAllocUnicode(secPass); + return Marshal.PtrToStringUni(plainString); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(plainString); + } + } + #endregion // Private Methods #if NET_2_0 @@ -1540,10 +1949,15 @@ namespace Mono.Data.Tds.Protocol { Comm.StartPacket (TdsPacketType.Query); Comm.Append (sql); - Comm.SendPacket (); + try { + Comm.SendPacket (); + Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback), + ar); + } catch (IOException ex) { + connected = false; + throw new TdsInternalException ("Server closed the connection.", ex); + } - Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback), - ar); return ar; }