Xamarin Public Jenkins 6992685b86 Imported Upstream version 4.2.0.179
Former-commit-id: 0a113cb3a6feb7873f632839b1307cc6033cd595
2015-11-10 14:54:39 +00:00

2014 lines
52 KiB
C#

//
// Mono.Data.Tds.Protocol.Tds.cs
//
// Author:
// 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
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
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
{
#region Fields
TdsComm comm;
TdsVersion tdsVersion;
protected internal TdsConnectionParameters connectionParms;
protected readonly byte[] NTLMSSP_ID = new byte[] {0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00};
int packetSize;
string dataSource;
string database;
string originalDatabase = string.Empty;
string databaseProductName;
string databaseProductVersion;
int databaseMajorVersion;
CultureInfo locale = CultureInfo.InvariantCulture;
readonly int lifeTime;
readonly DateTime created = DateTime.Now;
string charset;
string language;
bool connected;
bool moreResults;
Encoding encoder;
// bool autoCommit;
bool doneProc;
bool pooling = true;
TdsDataRow currentRow;
TdsDataColumnCollection columns;
ArrayList tableNames;
ArrayList columnNames;
TdsMetaParameterCollection parameters = new TdsMetaParameterCollection ();
bool queryInProgress;
int cancelsRequested;
int cancelsProcessed;
// bool isDone;
// bool isDoneInProc;
ArrayList outputParameters = new ArrayList ();
protected TdsInternalErrorCollection messages = new TdsInternalErrorCollection ();
int recordsAffected = -1;
long StreamLength;
long StreamIndex;
int StreamColumnIndex;
bool sequentialAccess;
bool isRowRead;
bool isResultRead;
bool LoadInProgress;
byte [] collation;
internal int poolStatus = 0;
#endregion // Fields
#region Properties
protected string Charset {
get { return charset; }
}
protected CultureInfo Locale {
get { return locale; }
}
public bool DoneProc {
get { return doneProc; }
}
protected string Language {
get { return language; }
}
protected ArrayList ColumnNames {
get { return columnNames; }
}
public TdsDataRow ColumnValues {
get { return currentRow; }
}
internal TdsComm Comm {
get { return comm; }
}
public string Database {
get { return database; }
}
public string DataSource {
get { return dataSource; }
}
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; }
}
public int PacketSize {
get { return packetSize; }
}
public int RecordsAffected {
get { return recordsAffected; }
set { recordsAffected = value; }
}
public string ServerVersion {
get { return databaseProductVersion; }
}
public TdsDataColumnCollection Columns {
get { return columns; }
}
public TdsVersion TdsVersion {
get { return tdsVersion; }
}
public ArrayList OutputParameters {
get { return outputParameters; }
set { outputParameters = value; }
}
protected TdsMetaParameterCollection Parameters {
get { return parameters; }
set { parameters = value; }
}
public bool SequentialAccess {
get { return sequentialAccess; }
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);
StreamLength = 0;
StreamColumnIndex = 0;
StreamIndex = 0;
LoadInProgress = false;
}
private void SkipToColumnIndex (int colIndex)
{
if (LoadInProgress)
EndLoad ();
if (colIndex < StreamColumnIndex)
throw new Exception ("Cannot Skip to a colindex less than the curr index");
while (colIndex != StreamColumnIndex) {
TdsColumnType? colType = Columns[StreamColumnIndex].ColumnType;
if (colType == null)
throw new Exception ("Column type unset.");
if (!(colType == TdsColumnType.Image ||
colType == TdsColumnType.Text ||
colType == TdsColumnType.NText)) {
GetColumnValue (colType, false, StreamColumnIndex);
StreamColumnIndex ++;
}
else {
BeginLoad (colType);
Comm.Skip (StreamLength);
StreamLength = 0;
EndLoad ();
}
}
}
public object GetSequentialColumnValue (int colIndex)
{
if (colIndex < StreamColumnIndex)
throw new InvalidOperationException ("Invalid attempt tp read from column ordinal" + colIndex);
if (LoadInProgress)
EndLoad ();
if (colIndex != StreamColumnIndex)
SkipToColumnIndex (colIndex);
object o = GetColumnValue (Columns[colIndex].ColumnType, false, colIndex);
StreamColumnIndex++;
return o;
}
public long GetSequentialColumnValue (int colIndex, long fieldIndex, byte[] buffer, int bufferIndex, int size)
{
if (colIndex < StreamColumnIndex)
throw new InvalidOperationException ("Invalid attempt to read from column ordinal" + colIndex);
try {
if (colIndex != StreamColumnIndex)
SkipToColumnIndex (colIndex);
if (!LoadInProgress) {
BeginLoad (Columns[colIndex].ColumnType);
}
if (buffer == null)
return StreamLength;
return LoadData (fieldIndex, buffer, bufferIndex, size);
} catch (IOException ex) {
connected = false;
throw new TdsInternalException ("Server closed the connection.", ex);
}
}
private void BeginLoad (
TdsColumnType? colType
)
{
if (LoadInProgress)
EndLoad ();
StreamLength = 0;
if (colType == null)
throw new ArgumentNullException ("colType");
switch (colType) {
case TdsColumnType.Text :
case TdsColumnType.NText:
case TdsColumnType.Image:
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:
case TdsColumnType.BigChar:
case TdsColumnType.BigBinary:
case TdsColumnType.BigVarBinary:
Comm.GetTdsShort ();
StreamLength = Comm.GetTdsShort ();
break;
case TdsColumnType.VarChar :
case TdsColumnType.NVarChar :
case TdsColumnType.Char:
case TdsColumnType.NChar:
case TdsColumnType.Binary:
case TdsColumnType.VarBinary:
StreamLength = Comm.GetTdsShort ();
break;
default :
StreamLength = -1;
break;
}
StreamIndex = 0;
LoadInProgress = true;
}
private void EndLoad()
{
if (StreamLength > 0)
Comm.Skip (StreamLength);
StreamLength = 0;
StreamIndex = 0;
StreamColumnIndex++;
LoadInProgress = false;
}
private long LoadData (long fieldIndex, byte[] buffer, int bufferIndex, int size)
{
if (StreamLength <= 0)
return StreamLength;
if (fieldIndex < StreamIndex)
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;
// 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
int loadlen = (int) ((size > StreamLength) ? StreamLength : size);
byte[] arr = Comm.GetBytes (loadlen, true);
// update the index and stream length
StreamIndex += loadlen + (fieldIndex - StreamIndex);
StreamLength -= loadlen;
arr.CopyTo (buffer, bufferIndex);
return arr.Length;
}
#endregion // Properties
#region Events
public event TdsInternalErrorMessageEventHandler TdsErrorMessage;
public event TdsInternalInfoMessageEventHandler TdsInfoMessage;
#endregion // Events
#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);
}
#endregion // Constructors
#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
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 ();
}
public void Cancel ()
{
if (queryInProgress) {
if (cancelsRequested == cancelsProcessed) {
comm.StartPacket (TdsPacketType.Cancel);
try {
Comm.SendPacket ();
} catch (IOException ex) {
connected = false;
throw new TdsInternalException ("Server closed the connection.", ex);
}
cancelsRequested += 1;
}
}
}
public abstract bool Connect (TdsConnectionParameters connectionParameters);
public static TdsTimeoutException CreateTimeoutException (string dataSource, string method)
{
string message = "Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.";
return new TdsTimeoutException (0, 0, message, -2, method, dataSource, "Mono TdsClient Data Provider", 0);
}
public virtual void Disconnect ()
{
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 ()
{
database = originalDatabase;
return true;
}
protected virtual bool IsValidRowCount (byte status, byte op)
{
return ((status & (0x10)) != 0) ;
}
public void Execute (string sql)
{
Execute (sql, null, 0, false);
}
public void ExecProc (string sql)
{
ExecProc (sql, null, 0, false);
}
public virtual void Execute (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
{
ExecuteQuery (sql, timeout, wantResults);
}
public virtual void ExecProc (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
{
ExecuteQuery (String.Format ("exec {0}", sql), timeout, wantResults);
}
public virtual void ExecPrepared (string sql, TdsMetaParameterCollection parameters, int timeout, bool wantResults)
{
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);
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,
int timeout, bool wantResults)
{
Comm.StartPacket (TdsPacketType.DBRPC);
byte [] rpcNameBytes = Comm.Encoder.GetBytes (rpcName);
byte rpcNameLength = (byte) rpcNameBytes.Length;
ushort mask = 0x0000;
ushort packetLength = (ushort) (sizeof (byte) + rpcNameLength +
sizeof (ushort));
Comm.Append (packetLength);
Comm.Append (rpcNameLength);
Comm.Append (rpcNameBytes);
Comm.Append (mask);
try {
Comm.SendPacket ();
CheckForData (timeout);
if (!wantResults)
SkipToEnd ();
} catch (IOException ex) {
connected = false;
throw new TdsInternalException ("Server closed the connection.", ex);
}
}
public bool NextResult ()
{
if (SequentialAccess) {
if (isResultRead) {
while (NextRow ()) {}
isRowRead = false;
isResultRead = false;
}
}
if (!moreResults)
return false;
TdsPacketSubType subType;
bool done = false;
bool outputParams = false;
while (!done) {
subType = ProcessSubPacket ();
if (outputParams) {
moreResults = false;
break;
}
switch (subType) {
case TdsPacketSubType.ColumnInfo:
case TdsPacketSubType.ColumnMetadata:
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;
break;
default:
done = !moreResults;
break;
}
}
return moreResults;
}
public bool NextRow ()
{
if (SequentialAccess) {
if (isRowRead) {
SkipRow ();
isRowRead = false;
}
}
TdsPacketSubType subType;
bool done = false;
bool result = false;
do {
subType = ProcessSubPacket ();
switch (subType) {
case TdsPacketSubType.Row:
result = true;
done = true;
break;
case TdsPacketSubType.Done:
case TdsPacketSubType.DoneProc:
case TdsPacketSubType.DoneInProc:
result = false;
done = true;
break;
}
} while (!done);
return result;
}
public virtual string Prepare (string sql, TdsMetaParameterCollection parameters)
{
throw new NotSupportedException ();
}
public void SkipToEnd ()
{
try {
while (NextResult ()) { /* DO NOTHING */ }
} catch (IOException ex) {
connected = false;
throw new TdsInternalException ("Server closed the connection.", ex);
}
}
public virtual void Unprepare (string statementId)
{
throw new NotSupportedException ();
}
#endregion // Public Methods
#region // Private Methods
[MonoTODO ("Is cancel enough, or do we need to drop the connection?")]
protected void CheckForData (int timeout)
{
if (timeout > 0 && !comm.Poll (timeout, SelectMode.SelectRead)) {
Cancel ();
throw CreateTimeoutException (dataSource, "CheckForData()");
}
}
protected TdsInternalInfoMessageEventArgs CreateTdsInfoMessageEvent (TdsInternalErrorCollection errors)
{
return new TdsInternalInfoMessageEventArgs (errors);
}
protected TdsInternalErrorMessageEventArgs CreateTdsErrorMessageEvent (byte theClass, int lineNumber, string message, int number, string procedure, string server, string source, byte state)
{
return new TdsInternalErrorMessageEventArgs (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
}
private Encoding GetEncodingFromColumnCollation (int lcid, int sortId)
{
if (sortId != 0)
return TdsCharset.GetEncodingFromSortOrder (sortId);
else
return TdsCharset.GetEncodingFromLCID (lcid);
}
protected object GetColumnValue (
TdsColumnType? colType,
bool outParam)
{
return GetColumnValue (colType, outParam, -1);
}
private object GetColumnValue (
TdsColumnType? colType,
bool outParam, int ordinal)
{
int len;
object element = null;
Encoding enc = null;
int lcid = 0, sortId = 0;
if (colType == null)
throw new ArgumentNullException ("colType");
if (ordinal > -1 && tdsVersion > TdsVersion.tds70) {
lcid = (int) columns[ordinal].LCID;
sortId = (int) columns[ordinal].SortOrder;
}
switch (colType) {
case TdsColumnType.IntN :
if (outParam)
comm.Skip (1);
element = GetIntValue (colType);
break;
case TdsColumnType.Int1 :
case TdsColumnType.Int2 :
case TdsColumnType.Int4 :
case TdsColumnType.BigInt :
element = GetIntValue (colType);
break;
case TdsColumnType.Image :
if (outParam)
comm.Skip (1);
element = GetImageValue ();
break;
case TdsColumnType.Text :
enc = GetEncodingFromColumnCollation (lcid, sortId);
if (outParam)
comm.Skip (1);
element = GetTextValue (false, enc);
break;
case TdsColumnType.NText :
enc = GetEncodingFromColumnCollation (lcid, sortId);
if (outParam)
comm.Skip (1);
element = GetTextValue (true, enc);
break;
case TdsColumnType.Char :
case TdsColumnType.VarChar :
enc = GetEncodingFromColumnCollation (lcid, sortId);
if (outParam)
comm.Skip (1);
element = GetStringValue (colType, false, outParam, enc);
break;
case TdsColumnType.BigVarBinary :
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 :
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 (colType, true, outParam, enc);
break;
case TdsColumnType.Real :
case TdsColumnType.Float8 :
element = GetFloatValue (colType);
break;
case TdsColumnType.FloatN :
if (outParam)
comm.Skip (1);
element = GetFloatValue (colType);
break;
case TdsColumnType.SmallMoney :
case TdsColumnType.Money :
element = GetMoneyValue (colType);
break;
case TdsColumnType.MoneyN :
if (outParam)
comm.Skip (1);
element = GetMoneyValue (colType);
break;
case TdsColumnType.Numeric :
case TdsColumnType.Decimal :
byte precision;
byte scale;
if (outParam) {
comm.Skip (1);
precision = comm.GetByte ();
scale = comm.GetByte ();
}
else {
precision = (byte) columns[ordinal].NumericPrecision;
scale = (byte) columns[ordinal].NumericScale;
}
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)
comm.Skip (1);
element = GetDateTimeValue (colType);
break;
case TdsColumnType.DateTime4 :
case TdsColumnType.DateTime :
element = GetDateTimeValue (colType);
break;
case TdsColumnType.VarBinary :
case TdsColumnType.Binary :
if (outParam)
comm.Skip (1);
element = GetBinaryValue ();
break;
case TdsColumnType.BitN :
if (outParam)
comm.Skip (1);
if (comm.GetByte () == 0)
element = DBNull.Value;
else
element = (comm.GetByte() != 0);
break;
case TdsColumnType.Bit :
int columnSize = comm.GetByte ();
element = (columnSize != 0);
break;
case TdsColumnType.UniqueIdentifier :
if (comm.Peek () != 16) { // If it's null, then what to do?
/*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);
if (!BitConverter.IsLittleEndian) {
byte[] swappedguidBytes = new byte[len];
for (int i = 0; i < 4; i++)
swappedguidBytes[i] = guidBytes[4-i-1];
for (int i = 4; i < 6; i++)
swappedguidBytes[i] = guidBytes[6-(i-4)-1];
for (int i = 6; i < 8; i++)
swappedguidBytes[i] = guidBytes[8-(i-6)-1];
for (int i = 8; i < 16; i++)
swappedguidBytes[i] = guidBytes[i];
Array.Copy(swappedguidBytes, 0, guidBytes, 0, len);
}
element = new Guid (guidBytes);
}
break;
case TdsColumnType.Variant :
if (outParam)
comm.Skip (4);
element = GetVariantValue();
break;
default :
return DBNull.Value;
}
return element;
}
private object GetBinaryValue ()
{
int len;
object result = DBNull.Value;
if (tdsVersion >= TdsVersion.tds70) {
len = comm.GetTdsShort ();
if (len != 0xffff && len >= 0)
result = comm.GetBytes (len, true);
} else {
len = (comm.GetByte () & 0xff);
if (len != 0)
result = comm.GetBytes (len, true);
}
return result;
}
private object GetVariantValue ()
{
uint len = (uint)comm.GetTdsInt ();
if (len == 0)
return DBNull.Value;
// VARIANT_BASETYPE
TdsColumnType colType = (TdsColumnType)comm.GetByte ();
// VARIANT_PROPBYTES
byte propbytes = comm.GetByte ();
if (propbytes != 0)
// VARIANT_PROPERTIES
comm.Skip (propbytes);
len -= (uint)propbytes + 2;
switch (colType)
{
case TdsColumnType.Int1 :
case TdsColumnType.Int2 :
case TdsColumnType.Int4 :
case TdsColumnType.BigInt :
return GetIntValue (colType);
default:
// The old code was ignoring variants
// and returning null. Should we
// throw an exception?
comm.Skip (len);
break;
}
return DBNull.Value;
}
private object GetDateTimeValue (
TdsColumnType? type
)
{
int len = 0;
object result;
if (type == null)
throw new ArgumentNullException ("type");
switch (type) {
case TdsColumnType.DateTime4:
len = 4;
break;
case TdsColumnType.DateTime:
len = 8;
break;
case TdsColumnType.DateTimeN:
byte tmp = comm.Peek ();
if (tmp != 0 && tmp != 4 && tmp != 8)
break;
len = comm.GetByte ();
break;
}
DateTime epoch = new DateTime (1900, 1, 1);
switch (len) {
case 8 :
result = epoch.AddDays (comm.GetTdsInt ());
int seconds = comm.GetTdsInt ();
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
result = epoch.AddDays ((ushort) comm.GetTdsShort ());
short minutes = comm.GetTdsShort ();
if (minutes != 0)
result = ((DateTime) result).AddMinutes ((int) minutes);
break;
default:
result = DBNull.Value;
break;
}
return result;
}
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)
{
int[] bits = new int[4] {0,0,0,0};
int len = (comm.GetByte() & 0xff) - 1;
if (len < 0)
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)
{
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[] easy=new byte[4];
bool positive = dec_bytes[0]==1;
if (len > 17)
throw new OverflowException ();
for (int i = 1, index = 0; i < len && i < 16; i +=
4, index += 1) {
for(int j=0; j<4; j++)
if(i+j<len)
easy[j]=dec_bytes[len-
(i+j)];
else
easy[j]=0;
if(!BitConverter.IsLittleEndian)
easy=comm.Swap(easy);
bits[index] = BitConverter.ToInt32(easy,0);
}
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 GetFloatValue (
TdsColumnType? columnType
)
{
if (columnType == null)
throw new ArgumentNullException ("columnType");
int columnSize = 0;
switch (columnType) {
case TdsColumnType.Real:
columnSize = 4;
break;
case TdsColumnType.Float8:
columnSize = 8;
break;
case TdsColumnType.FloatN:
columnSize = comm.GetByte ();
break;
}
switch (columnSize) {
case 8 :
return BitConverter.Int64BitsToDouble (comm.GetTdsInt64 ());
case 4 :
return BitConverter.ToSingle (BitConverter.GetBytes (comm.GetTdsInt ()), 0);
default :
return DBNull.Value;
}
}
private object GetImageValue ()
{
byte hasValue = comm.GetByte ();
if (hasValue == 0)
return DBNull.Value;
comm.Skip (24);
int len = comm.GetTdsInt ();
if (len < 0)
return DBNull.Value;
return (comm.GetBytes (len, true));
}
private object GetIntValue (
TdsColumnType? type
)
{
int len;
if (type == null)
throw new ArgumentNullException ("type");
switch (type) {
case TdsColumnType.BigInt :
len = 8;
break;
case TdsColumnType.IntN :
len = comm.GetByte ();
break;
case TdsColumnType.Int4 :
len = 4;
break;
case TdsColumnType.Int2 :
len = 2;
break;
case TdsColumnType.Int1 :
len = 1;
break;
default:
return DBNull.Value;
}
switch (len) {
case 8:
return (comm.GetTdsInt64 ());
case 4 :
return (comm.GetTdsInt ());
case 2 :
return (comm.GetTdsShort ());
case 1 :
return (comm.GetByte ());
default:
return DBNull.Value;
}
}
private object GetMoneyValue (
TdsColumnType? type
)
{
int len;
if (type == null)
throw new ArgumentNullException ("type");
switch (type) {
case TdsColumnType.SmallMoney :
case TdsColumnType.Money4 :
len = 4;
break;
case TdsColumnType.Money :
len = 8;
break;
case TdsColumnType.MoneyN :
len = comm.GetByte ();
break;
default:
return DBNull.Value;
}
switch (len) {
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 ();
bool negative = hi < 0;
if (negative) {
hi = ~hi;
lo = ~(lo - 1);
}
return new Decimal (lo, hi, 0, negative, 4);
}
default:
return DBNull.Value;
}
}
protected object GetStringValue (
TdsColumnType? colType,
bool wideChars, bool outputParam, Encoding encoder)
{
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, enc);
else
result = comm.GetString (len, false, enc);
if (tdsVersion < TdsVersion.tds70 && ((string) result).Equals (" "))
result = string.Empty;
return result;
}
else
return DBNull.Value;
}
protected int GetSubPacketLength ()
{
return comm.GetTdsShort ();
}
private object GetTextValue (bool wideChars, Encoding encoder)
{
string result = null;
byte hasValue = comm.GetByte ();
if (hasValue != 16)
return DBNull.Value;
// 16 Byte TEXTPTR, 8 Byte TIMESTAMP
comm.Skip (24);
int len = comm.GetTdsInt ();
//if the len is 0 , then the string can be a '' string
// this method is called only for Text and NText. Hence will
// return a empty string
if (len == 0)
return string.Empty;
if (wideChars)
result = comm.GetString (len / 2, encoder);
else
result = comm.GetString (len, false, encoder);
len /= 2;
if ((byte) tdsVersion < (byte) TdsVersion.tds70 && result == " ")
result = string.Empty;
return result;
}
internal bool IsBlobType (TdsColumnType columnType)
{
return (columnType == TdsColumnType.Text || columnType == TdsColumnType.Image || columnType == TdsColumnType.NText || columnType == TdsColumnType.Variant);
}
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)
{
switch (columnType) {
case TdsColumnType.Int1 :
case TdsColumnType.Int2 :
case TdsColumnType.Int4 :
case TdsColumnType.BigInt :
case TdsColumnType.Float8 :
case TdsColumnType.DateTime :
case TdsColumnType.Bit :
case TdsColumnType.Money :
case TdsColumnType.Money4 :
case TdsColumnType.SmallMoney :
case TdsColumnType.Real :
case TdsColumnType.DateTime4 :
/*
case TdsColumnType.Decimal:
case TdsColumnType.Numeric:
*/
return true;
default :
return false;
}
}
protected void LoadRow ()
{
if (SequentialAccess) {
if (isRowRead)
SkipRow ();
isRowRead = true;
isResultRead = true;
return;
}
currentRow = new TdsDataRow ();
int i = 0;
foreach (TdsDataColumn column in columns) {
object o = GetColumnValue (column.ColumnType, false, i);
currentRow.Add (o);
if (doneProc)
outputParameters.Add (o);
if (o is TdsBigDecimal && currentRow.BigDecimalIndex < 0)
currentRow.BigDecimalIndex = i;
i += 1;
}
}
internal static int LookupBufferSize (TdsColumnType columnType)
{
switch (columnType) {
case TdsColumnType.Int1 :
case TdsColumnType.Bit :
return 1;
case TdsColumnType.Int2 :
return 2;
case TdsColumnType.Int4 :
case TdsColumnType.Real :
case TdsColumnType.DateTime4 :
case TdsColumnType.Money4 :
case TdsColumnType.SmallMoney :
return 4;
case TdsColumnType.Float8 :
case TdsColumnType.DateTime :
case TdsColumnType.Money :
case TdsColumnType.BigInt :
return 8;
default :
return 0;
}
}
protected internal int ProcessAuthentication ()
{
int pdu_size = Comm.GetTdsShort ();
byte[] msg2 = Comm.GetBytes (pdu_size, true);
Type2Message t2 = new Type2Message (msg2);
// 0x0001 Negotiate Unicode
// 0x0200 Negotiate NTLM
// 0x8000 Negotiate Always Sign
Type3Message t3 = new Type3Message (t2);
t3.Domain = this.connectionParms.DefaultDomain;
t3.Host = this.connectionParms.Hostname;
t3.Username = this.connectionParms.User;
t3.Password = GetPlainPassword(this.connectionParms.Password);
Comm.StartPacket (TdsPacketType.SspAuth); // 0x11
Comm.Append (t3.GetBytes ());
try {
Comm.SendPacket ();
} catch (IOException ex) {
connected = false;
throw new TdsInternalException ("Server closed the connection.", ex);
}
return 1; // TDS_SUCCEED
}
protected void ProcessColumnDetail ()
{
int len = GetSubPacketLength ();
byte[] values = new byte[3];
int columnNameLength;
string baseColumnName = String.Empty;
int position = 0;
while (position < len) {
for (int j = 0; j < 3; j += 1)
values[j] = comm.GetByte ();
position += 3;
bool isAlias = ((values[2] & (byte) TdsColumnStatus.Rename) != 0);
if (isAlias) {
if (tdsVersion >= TdsVersion.tds70) {
columnNameLength = comm.GetByte ();
position += 2 * columnNameLength + 1;
}
else {
columnNameLength = comm.GetByte ();
position += columnNameLength + 1;
}
baseColumnName = comm.GetString (columnNameLength);
}
byte index = (byte) (values[0] - (byte) 1);
byte tableIndex = (byte) (values[1] - (byte) 1);
bool isExpression = ((values[2] & (byte) TdsColumnStatus.IsExpression) != 0);
TdsDataColumn column = columns [index];
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);
}
}
protected abstract void ProcessColumnInfo ();
protected void ProcessColumnNames ()
{
columnNames = new ArrayList ();
int totalLength = comm.GetTdsShort ();
int bytesRead = 0;
int i = 0;
while (bytesRead < totalLength) {
int columnNameLength = comm.GetByte ();
string columnName = comm.GetString (columnNameLength);
bytesRead = bytesRead + 1 + columnNameLength;
columnNames.Add (columnName);
i += 1;
}
}
[MonoTODO ("Make sure counting works right, especially with multiple resultsets.")]
protected void ProcessEndToken (TdsPacketSubType type)
{
byte status = Comm.GetByte ();
Comm.Skip (1);
byte op = comm.GetByte ();
Comm.Skip (1);
int rowCount = comm.GetTdsInt ();
bool validRowCount = IsValidRowCount (status,op);
moreResults = ((status & 0x01) != 0);
bool cancelled = ((status & 0x20) != 0);
switch (type) {
case TdsPacketSubType.DoneProc:
doneProc = true;
goto case TdsPacketSubType.Done;
case TdsPacketSubType.Done:
case TdsPacketSubType.DoneInProc:
if (validRowCount) {
if (recordsAffected == -1)
recordsAffected = rowCount;
else
recordsAffected += rowCount;
}
break;
}
if (moreResults)
queryInProgress = false;
if (cancelled)
cancelsProcessed += 1;
if (messages.Count > 0 && !moreResults)
OnTdsInfoMessage (CreateTdsInfoMessageEvent (messages));
}
protected void ProcessEnvironmentChange ()
{
// VARADHAN: TDS 8 Debugging
//Console.WriteLine ("In ProcessEnvironmentChange... entry");
int len = GetSubPacketLength ();
TdsEnvPacketSubType type = (TdsEnvPacketSubType) comm.GetByte ();
int cLen;
switch (type) {
case TdsEnvPacketSubType.BlockSize :
string blockSize;
cLen = comm.GetByte ();
blockSize = comm.GetString (cLen);
if (tdsVersion >= TdsVersion.tds70)
comm.Skip (len - 2 - cLen * 2);
else
comm.Skip (len - 2 - cLen);
packetSize = Int32.Parse (blockSize);
comm.ResizeOutBuf (packetSize);
break;
case TdsEnvPacketSubType.CharSet :
cLen = comm.GetByte ();
if (tdsVersion == TdsVersion.tds70) {
SetCharset (comm.GetString (cLen));
comm.Skip (len - 2 - cLen * 2);
}
else {
SetCharset (comm.GetString (cLen));
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 ();
string newDB = comm.GetString (cLen);
cLen = comm.GetByte () & 0xff;
comm.GetString (cLen);
if (originalDatabase == string.Empty)
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 ();
//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 {
comm.Skip (5);
short nameLength = comm.GetByte ();
databaseProductName = comm.GetString (nameLength);
comm.Skip (1);
databaseMajorVersion = comm.GetByte ();
databaseProductVersion = String.Format ("{0}.{1}", databaseMajorVersion, comm.GetByte ());
comm.Skip (1);
}
if (databaseProductName.Length > 1 && -1 != databaseProductName.IndexOf ('\0')) {
int last = databaseProductName.IndexOf ('\0');
databaseProductName = databaseProductName.Substring (0, last);
}
connected = true;
//Console.WriteLine ("databaseProductVersion:{0}", databaseProductVersion);
}
protected void OnTdsErrorMessage (TdsInternalErrorMessageEventArgs e)
{
if (TdsErrorMessage != null)
TdsErrorMessage (this, e);
}
protected void OnTdsInfoMessage (TdsInternalInfoMessageEventArgs e)
{
if (TdsInfoMessage != null)
TdsInfoMessage (this, e);
messages.Clear ();
}
protected void ProcessMessage (TdsPacketSubType subType)
{
GetSubPacketLength ();
int number = comm.GetTdsInt ();
byte state = comm.GetByte ();
byte theClass = comm.GetByte ();
string message;
string server;
string procedure;
byte lineNumber;
string source;
bool isError = false;
if (subType == TdsPacketSubType.EED) {
isError = (theClass > 10);
comm.Skip (comm.GetByte ()); // SQL State
comm.Skip (1); // Status
comm.Skip (2); // TranState
} else
isError = (subType == TdsPacketSubType.Error);
message = comm.GetString (comm.GetTdsShort ());
server = comm.GetString (comm.GetByte ());
procedure = comm.GetString (comm.GetByte ());
lineNumber = comm.GetByte ();
comm.Skip (1);
source = String.Empty; // FIXME
if (isError)
OnTdsErrorMessage (CreateTdsErrorMessageEvent (theClass, lineNumber, message, number, procedure, server, source, state));
else
messages.Add (new TdsInternalError (theClass, lineNumber, message, number, procedure, server, source, state));
}
protected virtual void ProcessOutputParam ()
{
GetSubPacketLength ();
/*string paramName = */comm.GetString (comm.GetByte () & 0xff);
comm.Skip (5);
TdsColumnType colType = (TdsColumnType) comm.GetByte ();
object value = GetColumnValue (colType, true);
outputParameters.Add (value);
}
protected void ProcessDynamic ()
{
Comm.Skip (2);
/*byte type =*/ Comm.GetByte ();
/*byte status =*/ Comm.GetByte ();
/*string id =*/ Comm.GetString (Comm.GetByte ());
}
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 ());
break;
case TdsPacketSubType.AltName:
case TdsPacketSubType.AltFormat:
case TdsPacketSubType.Capability:
case TdsPacketSubType.ParamFormat:
comm.Skip (comm.GetTdsShort ());
break;
case TdsPacketSubType.Dynamic:
ProcessDynamic ();
break;
case TdsPacketSubType.EnvironmentChange:
ProcessEnvironmentChange ();
break;
case TdsPacketSubType.Info: // TDS 4.2/7.0
case TdsPacketSubType.EED: // TDS 5.0
case TdsPacketSubType.Error: // TDS 4.2/7.0
ProcessMessage (subType);
break;
case TdsPacketSubType.Param:
ProcessOutputParam ();
break;
case TdsPacketSubType.LoginAck:
ProcessLoginAck ();
break;
case TdsPacketSubType.Authentication: // TDS 7.0
ProcessAuthentication ();
break;
case TdsPacketSubType.ReturnStatus :
ProcessReturnStatus ();
break;
case TdsPacketSubType.ProcId:
Comm.Skip (8);
break;
case TdsPacketSubType.Done:
case TdsPacketSubType.DoneProc:
case TdsPacketSubType.DoneInProc:
ProcessEndToken (subType);
break;
case TdsPacketSubType.ColumnName:
Comm.Skip (8);
ProcessColumnNames ();
break;
case TdsPacketSubType.ColumnInfo: // TDS 4.2
case TdsPacketSubType.ColumnMetadata: // TDS 7.0
case TdsPacketSubType.RowFormat: // TDS 5.0
Columns.Clear ();
ProcessColumnInfo ();
break;
case TdsPacketSubType.ColumnDetail:
ProcessColumnDetail ();
break;
case TdsPacketSubType.TableName:
ProcessTableName ();
break;
case TdsPacketSubType.ColumnOrder:
comm.Skip (comm.GetTdsShort ());
break;
case TdsPacketSubType.Control:
comm.Skip (comm.GetTdsShort ());
break;
case TdsPacketSubType.Row:
LoadRow ();
break;
}
// VARADHAN: TDS 8 Debugging
//Console.WriteLine ("In ProcessSubPacket... exit");
return subType;
}
protected void ProcessTableName ()
{
tableNames = new ArrayList ();
int totalLength = comm.GetTdsShort ();
int position = 0;
int len;
while (position < totalLength) {
if (tdsVersion >= TdsVersion.tds70) {
len = comm.GetTdsShort ();
position += 2 * (len + 1);
}
else {
len = comm.GetByte ();
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)
charset = "iso_1";
if (this.charset != null && this.charset == charset)
return;
if (charset.StartsWith ("cp")) {
encoder = Encoding.GetEncoding (Int32.Parse (charset.Substring (2)));
this.charset = charset;
}
else {
encoder = Encoding.GetEncoding ("iso-8859-1");
this.charset = "iso_1";
}
SetCharset (encoder);
}
protected void SetLanguage (string language)
{
if (language == null || language.Length > 30)
language = "us_english";
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
#region asynchronous methods
protected IAsyncResult BeginExecuteQueryInternal (string sql, bool wantResults,
AsyncCallback callback, object state)
{
InitExec ();
TdsAsyncResult ar = new TdsAsyncResult (callback, state);
ar.TdsAsyncState.WantResults = wantResults;
Comm.StartPacket (TdsPacketType.Query);
Comm.Append (sql);
try {
Comm.SendPacket ();
Comm.BeginReadPacket (new AsyncCallback(OnBeginExecuteQueryCallback),
ar);
} catch (IOException ex) {
connected = false;
throw new TdsInternalException ("Server closed the connection.", ex);
}
return ar;
}
protected void EndExecuteQueryInternal (IAsyncResult ar)
{
if (!ar.IsCompleted)
ar.AsyncWaitHandle.WaitOne ();
TdsAsyncResult result = (TdsAsyncResult) ar;
if (result.IsCompletedWithException)
throw result.Exception;
}
protected void OnBeginExecuteQueryCallback (IAsyncResult ar)
{
TdsAsyncResult result = (TdsAsyncResult) ar.AsyncState;
TdsAsyncState tdsState = (TdsAsyncState) result.TdsAsyncState;
try {
Comm.EndReadPacket (ar);
if (!tdsState.WantResults)
SkipToEnd ();
} catch (Exception e) {
result.MarkComplete (e);
return;
}
result.MarkComplete ();
}
public virtual IAsyncResult BeginExecuteNonQuery (string sql,
TdsMetaParameterCollection parameters,
AsyncCallback callback,
object state)
{
// abstract, kept to be backward compatiable.
throw new NotImplementedException ("should not be called!");
}
public virtual void EndExecuteNonQuery (IAsyncResult ar)
{
// abstract method
throw new NotImplementedException ("should not be called!");
}
public virtual IAsyncResult BeginExecuteQuery (string sql,
TdsMetaParameterCollection parameters,
AsyncCallback callback,
object state)
{
// abstract, kept to be backward compatiable.
throw new NotImplementedException ("should not be called!");
}
public virtual void EndExecuteQuery (IAsyncResult ar)
{
// abstract method
throw new NotImplementedException ("should not be called!");
}
public virtual IAsyncResult BeginExecuteProcedure (string prolog,
string epilog,
string cmdText,
bool IsNonQuery,
TdsMetaParameterCollection parameters,
AsyncCallback callback,
object state)
{
throw new NotImplementedException ("should not be called!");
}
public virtual void EndExecuteProcedure (IAsyncResult ar)
{
// abstract method
throw new NotImplementedException ("should not be called!");
}
public void WaitFor (IAsyncResult ar)
{
if (! ar.IsCompleted)
ar.AsyncWaitHandle.WaitOne ();
}
public void CheckAndThrowException (IAsyncResult ar)
{
TdsAsyncResult result = (TdsAsyncResult) ar;
if (result.IsCompleted && result.IsCompletedWithException)
throw result.Exception;
}
#endregion // asynchronous methods
}
}