diff --git a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs index 6f46112..47e3212 100644 --- a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs +++ b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs @@ -7,16 +7,22 @@ namespace TDSClient.TDS.Client { using System; + using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Security.Authentication; + using System.Threading; using TDSClient.TDS.Comms; using TDSClient.TDS.Header; using TDSClient.TDS.Login7; using TDSClient.TDS.PreLogin; + using TDSClient.TDS.Query; using TDSClient.TDS.Tokens; + using TDSClient.TDS.Tokens.Cols; + using TDSClient.TDS.Tranasction.Header.Headers; + using TDSClient.TDS.Tranasction.Headers; using TDSClient.TDS.Utilities; /// @@ -212,46 +218,7 @@ public void ReceiveLogin7Response() { foreach (var token in response.Tokens) { - if (token is TDSEnvChangeToken) - { - var envChangeToken = token as TDSEnvChangeToken; - if (envChangeToken.Type == Tokens.EnvChange.TDSEnvChangeType.Routing) - { - LoggingUtilities.WriteLog($" Client received EnvChange routing token, client is being routed."); - this.Server = envChangeToken.Values["AlternateServer"]; - this.Port = int.Parse(envChangeToken.Values["ProtocolProperty"]); - this.reconnect = true; - LoggingUtilities.WriteLog($" Redirect to {this.Server}:{this.Port}", writeToSummaryLog: true, writeToVerboseLog: false); - } - } - else if (token is TDSErrorToken) - { - var errorToken = token as TDSErrorToken; - LoggingUtilities.WriteLog($" Client received Error token, Number: {errorToken.Number}, State: {errorToken.State}", writeToSummaryLog: true); ; - LoggingUtilities.WriteLog($" MsgText: {errorToken.MsgText}"); - LoggingUtilities.WriteLog($" Class: {errorToken.Class}"); - LoggingUtilities.WriteLog($" ServerName: {errorToken.ServerName}"); - LoggingUtilities.WriteLog($" ProcName: {errorToken.ProcName}"); - LoggingUtilities.WriteLog($" LineNumber: {errorToken.LineNumber}"); - - if (errorToken.Number == 18456) - { - throw new Exception("Login failure."); - } - } - else if (token is TDSInfoToken) - { - var infoToken = token as TDSInfoToken; - LoggingUtilities.WriteLog($" Client received Info token:"); - - LoggingUtilities.WriteLog($" Number: {infoToken.Number}"); - LoggingUtilities.WriteLog($" State: {infoToken.State}"); - LoggingUtilities.WriteLog($" Class: {infoToken.Class}"); - LoggingUtilities.WriteLog($" MsgText: {infoToken.MsgText}"); - LoggingUtilities.WriteLog($" ServerName: {infoToken.ServerName}"); - LoggingUtilities.WriteLog($" ProcName: {infoToken.ProcName}"); - LoggingUtilities.WriteLog($" LineNumber: {infoToken.LineNumber}"); - } + PrintTdsToken(token); } } else @@ -262,6 +229,72 @@ public void ReceiveLogin7Response() LoggingUtilities.WriteLog($" Login7 response received."); } + private void PrintTdsToken(TDSToken token) + { + if (token is TDSEnvChangeToken) + { + var envChangeToken = token as TDSEnvChangeToken; + if (envChangeToken.Type == Tokens.EnvChange.TDSEnvChangeType.Routing) + { + LoggingUtilities.WriteLog($" Client received EnvChange routing token, client is being routed."); + this.Server = envChangeToken.Values["AlternateServer"]; + this.Port = int.Parse(envChangeToken.Values["ProtocolProperty"]); + this.reconnect = true; + LoggingUtilities.WriteLog($" Redirect to {this.Server}:{this.Port}", writeToSummaryLog: true, writeToVerboseLog: false); + } + } + else if (token is TDSErrorToken) + { + var errorToken = token as TDSErrorToken; + LoggingUtilities.WriteLog($" Client received Error token, Number: {errorToken.Number}, State: {errorToken.State}", writeToSummaryLog: true); ; + LoggingUtilities.WriteLog($" MsgText: {errorToken.MsgText}"); + LoggingUtilities.WriteLog($" Class: {errorToken.Class}"); + LoggingUtilities.WriteLog($" ServerName: {errorToken.ServerName}"); + LoggingUtilities.WriteLog($" ProcName: {errorToken.ProcName}"); + LoggingUtilities.WriteLog($" LineNumber: {errorToken.LineNumber}"); + + if (errorToken.Number == 18456) + { + throw new Exception("Login failure."); + } + } + else if (token is TDSInfoToken) + { + var infoToken = token as TDSInfoToken; + LoggingUtilities.WriteLog($" Client received Info token:"); + + LoggingUtilities.WriteLog($" Number: {infoToken.Number}"); + LoggingUtilities.WriteLog($" State: {infoToken.State}"); + LoggingUtilities.WriteLog($" Class: {infoToken.Class}"); + LoggingUtilities.WriteLog($" MsgText: {infoToken.MsgText}"); + LoggingUtilities.WriteLog($" ServerName: {infoToken.ServerName}"); + LoggingUtilities.WriteLog($" ProcName: {infoToken.ProcName}"); + LoggingUtilities.WriteLog($" LineNumber: {infoToken.LineNumber}"); + } + else if (token is TDSColMetadataToken colMetadataToken) + { + LoggingUtilities.WriteLog($" Client Column Metadata Info token:"); + + LoggingUtilities.WriteLog($" Columns: {colMetadataToken.Count}"); + for (int i = 0; i < colMetadataToken.Metadata.Length; i++) + { + var metadata = colMetadataToken.Metadata[i]; + LoggingUtilities.WriteLog($" Index: {i}"); + LoggingUtilities.WriteLog($" Name: {metadata.ColumnName}"); + LoggingUtilities.WriteLog($" Type: {metadata.Type.Type}"); + LoggingUtilities.WriteLog(""); + } + } + else if (token is TDSRowToken rowToken) + { + LoggingUtilities.WriteLog($" Client Row data token:"); + for(int i = 0; i < rowToken.Values.Length; i++) + { + LoggingUtilities.WriteLog($" Row [{i}]: {rowToken.Values[i]?.ToString() ?? "NUll"}"); + } + } + } + /// /// Connect to the server. /// @@ -332,6 +365,59 @@ public void Connect() } } + public int queryCount = 0; + public (string[], object[][]) Query(string query) + { + var id = Interlocked.Increment(ref queryCount); + string[] colNames = Array.Empty(); + List result = new List(); + try + { + var request = new TDSSqlBatchPacketData(query); + var header = new SqlHeaderPacketData(new TransactionDescriptorHeaderData((uint)id)); + request.AllHeaders.Headers.Add(header); + + TdsCommunicator.SendTDSMessage(request); + + if (this.TdsCommunicator.ReceiveTDSMessage() is TDSTokenStreamPacketData response) + { + foreach (var token in response.Tokens) + { + PrintTdsToken(token); + + if (token is TDSColMetadataToken colMetadataToken) + { + colNames = new string[colMetadataToken.Count]; + for (int i = 0; i < colMetadataToken.Metadata.Length; i++) + { + var metadata = colMetadataToken.Metadata[i]; + colNames[i] = metadata.ColumnName; + } + } + else if (token is TDSRowToken rowToken) + { + LoggingUtilities.WriteLog($" Client Row data token:"); + result.Add(rowToken.Values); + } + } + } + else + { + throw new InvalidOperationException(); + } + } + catch(Exception ex) + { + LoggingUtilities.WriteLog($"Exception:"); + LoggingUtilities.WriteLog($"{ex.Message}"); + if (ex.InnerException != null) + { + LoggingUtilities.WriteLog($"InnerException: {ex.InnerException.Message}"); + } + } + return (colNames, result.ToArray()); + } + private void MeasureDNSResolutionTime() { try diff --git a/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs b/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs index 5fcbb2e..0310ed5 100644 --- a/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs +++ b/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs @@ -17,6 +17,7 @@ namespace TDSClient.TDS.Comms using TDSClient.TDS.Interfaces; using TDSClient.TDS.Login7; using TDSClient.TDS.PreLogin; + using TDSClient.TDS.Query; using TDSClient.TDS.Tokens; using TDSClient.TDS.Utilities; using static System.Net.Mime.MediaTypeNames; @@ -221,9 +222,28 @@ public ITDSPacketData ReceiveTDSMessage() } case TDSCommunicatorState.SentLogin7RecordWithCompleteAuthToken: + { + var tokenStream = new TDSTokenStreamPacketData(); + result = tokenStream; + result.Unpack(new MemoryStream(resultBuffer)); + if (tokenStream.Tokens.Any( t=> t is TDSErrorToken err)) + { + this.communicatorState = TDSCommunicatorState.LoginError; + } + else + { + this.communicatorState = TDSCommunicatorState.LoggedIn; + } + break; + } + + case TDSCommunicatorState.SentSqlBatch: { result = new TDSTokenStreamPacketData(); result.Unpack(new MemoryStream(resultBuffer)); + + //Restore to base state + this.communicatorState = TDSCommunicatorState.LoggedIn; break; } @@ -268,9 +288,14 @@ public void SendTDSMessage(ITDSPacketData data) case TDSCommunicatorState.LoggedIn: { - throw new NotSupportedException(); - } + if (!(data is TDSSqlBatchPacketData)) + { + throw new InvalidDataException(); + } + this.innerTdsStream.CurrentOutboundMessageType = TDSMessageType.SQLBatch; + break; + } default: { throw new InvalidOperationException(); @@ -278,9 +303,11 @@ public void SendTDSMessage(ITDSPacketData data) } var buffer = new byte[data.Length()]; - data.Pack(new MemoryStream(buffer)); + var memStream = new MemoryStream(buffer); + data.Pack(memStream); this.innerStream.Write(buffer, 0, buffer.Length); + var hex = BitConverter.ToString(buffer).Replace("-", " "); switch (this.communicatorState) { @@ -295,6 +322,11 @@ public void SendTDSMessage(ITDSPacketData data) this.communicatorState = TDSCommunicatorState.SentLogin7RecordWithCompleteAuthToken; break; } + case TDSCommunicatorState.LoggedIn: + { + this.communicatorState = TDSCommunicatorState.SentSqlBatch; + break; + } } } } diff --git a/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs b/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs index 44cb7bd..15146c3 100644 --- a/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs +++ b/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs @@ -29,6 +29,16 @@ public enum TDSCommunicatorState /// /// LoggedIn Communicator State /// - LoggedIn + LoggedIn, + + /// + /// LoginError Communicator State + /// + LoginError, + + /// + /// Sent sql batch + /// + SentSqlBatch, } } diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs new file mode 100644 index 0000000..eefbd1a --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Cols +{ + public class ColMetadata : TDSToken + { + public uint UserType { get; set; } + public byte Flags1 { get; set; } + public byte Flags2 { get; set; } + public SqlTypeInfo Type { get; set; } + + //#[Nullable] + public string ColumnName { get; set; } = null; + + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + //TODO: IsColumnEncryptionSupported read ushort + + UserType = BigEndianUtilities.ReadUInt(stream); + + Flags1 = Convert.ToByte(stream.ReadByte()); + Flags2 = Convert.ToByte(stream.ReadByte()); + + var type = new SqlTypeInfo(); + if (!type.Unpack(stream)) + { + return false; + } + Type = type; + + var colNameLen = stream.ReadByte(); + + var colName = BigEndianUtilities.ReadUnicodeStreamLE(stream, colNameLen); + ColumnName = new string(colName); + + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs new file mode 100644 index 0000000..0e84686 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + public class ColumnData : IPackageable + { + internal ColumnData() { } + + public ulong UserType { get; set; } + public ushort Flags { get; set; } + + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs new file mode 100644 index 0000000..8bb4e3a --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSAltRowToken : TDSToken + { + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs new file mode 100644 index 0000000..286787f --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSColMetadataToken : TDSToken + { + + public ushort Count { get; set; } + + public ColMetadata[] Metadata { get; set; } + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + Count = BigEndianUtilities.ReadUShortLE(stream); + + var metadata = new ColMetadata[Count]; + for (int i = 0; i < Count;i++) + { + var colMetadata = new ColMetadata(); + if(!colMetadata.Unpack(stream)) + { + return false; + } + + metadata[i] = colMetadata; + } + + Metadata = metadata; + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs new file mode 100644 index 0000000..f5e5ce8 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSRowToken : TDSToken + { + public TDSRowToken(TDSColMetadataToken colMetadata) + { + ColMetadata = colMetadata; + } + + public object[] Values { get; set; } = null; + public TDSColMetadataToken ColMetadata { get; } + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + Values = new object[ColMetadata.Count]; + for (int i = 0; i < ColMetadata.Count; i++) + { + var type = ColMetadata.Metadata[i].Type; + Values[i] = SqlTypeValueFactory.ReadVaue(stream, type); + } + + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs new file mode 100644 index 0000000..aa6dc52 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSSqlReturnValueToken : TDSToken + { + public ushort Index { get; set; } + //[Nullable] + public string ParameterName { get; set; } = null; + public byte Status { get; set; } + public uint UserType { get; set; } + public byte Flags1 { get; set; } + public byte Flags2 { get; set; } + public SqlTypeInfo Type { get; set; } + + public object Value { get; set; } = null; + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + Index = BigEndianUtilities.ReadUShortLE(stream); + var paraeterNameLen = stream.ReadByte(); + var paraeterName = BigEndianUtilities.ReadUnicodeStreamLE(stream, paraeterNameLen); + ParameterName = new string(paraeterName); + Status = Convert.ToByte(stream.ReadByte()); + + UserType = BigEndianUtilities.ReadUInt(stream); + Flags1 = Convert.ToByte(stream.ReadByte()); + Flags2 = Convert.ToByte(stream.ReadByte()); + + var type = new SqlTypeInfo(); + if (!type.Unpack(stream)) + { + return false; + } + Type = type; + + + Value = SqlTypeValueFactory.ReadVaue(stream, type); + + return true; + } + + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs b/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs index dc0e2b9..153f144 100644 --- a/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs +++ b/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs @@ -7,7 +7,10 @@ namespace TDSClient.TDS.Tokens { using System; + using System.Collections.Generic; using System.IO; + using System.Linq; + using TDSClient.TDS.Tokens.Cols; using TDSClient.TDS.Utilities; /// @@ -20,7 +23,7 @@ public static class TDSTokenFactory /// /// Stream that contains the token /// Returns read TDS Token - public static TDSToken ReadTokenFromStream(MemoryStream stream) + public static TDSToken ReadTokenFromStream(MemoryStream stream, List oldTokens) { var tokenType = (TDSTokenType)stream.ReadByte(); @@ -50,6 +53,52 @@ public static TDSToken ReadTokenFromStream(MemoryStream stream) return token; } + case TDSTokenType.ColMetadata: + { + var token = new TDSColMetadataToken(); + token.Unpack(stream); + + return token; + } + case TDSTokenType.ReturnValue: + { + var token = new TDSSqlReturnValueToken(); + token.Unpack(stream); + + return token; + } + + case TDSTokenType.Row: + { + if(oldTokens.SingleOrDefault(t => t is TDSColMetadataToken) is TDSColMetadataToken colMetadata) + { + var token = new TDSRowToken(colMetadata); + token.Unpack(stream); + + return token; + } + + throw new Exception("No Column metadata found"); + } + /* + case TDSTokenType.AltRow: + { + var token = new TDSAltRowToken(); + token.Unpack(stream); + + return token; + } + + */ + + /* case TDSTokenType.Done: + { + return null; + //var token = new TDSRowToken(); + //token.Unpack(stream); + + //return token; + }*/ default: { diff --git a/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs b/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs index 3c969e9..e1e4093 100644 --- a/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs +++ b/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs @@ -80,7 +80,7 @@ public bool Unpack(MemoryStream stream) { while (stream.Length > stream.Position) { - TDSToken token = TDSTokenFactory.ReadTokenFromStream(stream); + TDSToken token = TDSTokenFactory.ReadTokenFromStream(stream, this.Tokens); if (token != null) { this.Tokens.Add(token); diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs new file mode 100644 index 0000000..721e075 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Type +{ + public class SqlTypeInfo : IPackageable + { + public SqlTypes Type { get; set; } + + public bool IsNullLen => Type == SqlTypes.NULLTYPE; + + public bool IsFixedLen => SqlTypes.FIXEDLENTYPE.HasFlag(Type); + + public bool IsVarLenType => SqlTypes.VARLENTYPE.HasFlag(Type); + + public bool IsPartLenType => SqlTypes.PARTLENTYPE.HasFlag(Type); + + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + Type = (SqlTypes)stream.ReadByte(); + return true; + } + + public int GetFixedLen() + { + switch (Type) + { + case SqlTypes.INT1TYPE: + case SqlTypes.BITTYPE: + return 1; + + case SqlTypes.INT2TYPE: + return 2; + + case SqlTypes.INT4TYPE: + case SqlTypes.DATETIM4TYPE: + case SqlTypes.FLT4TYPE: + case SqlTypes.MONEY4TYPE: + return 4; + + case SqlTypes.MONEYTYPE: + case SqlTypes.DATETIMETYPE: + case SqlTypes.FLT8TYPE: + case SqlTypes.INT8TYPE: + return 8; + + default: + throw new ArgumentException("Unknown fixed length type"); + } + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs new file mode 100644 index 0000000..2ea1902 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Type +{ + public static class SqlTypeValueFactory + { + public static object ReadVaue(MemoryStream stream, SqlTypeInfo type) + { + if (type.IsNullLen) + { + return null; + } + + if(type.IsFixedLen) + { + return ReadFixedTypeValue(stream, type); + } + + throw new NotSupportedException(); + } + + private static object ReadFixedTypeValue(MemoryStream stream, SqlTypeInfo type) + { + byte[] buffer; + switch (type.Type) + { + case SqlTypes.INT1TYPE: + return (sbyte)stream.ReadByte(); + + case SqlTypes.BITTYPE: + return (byte)stream.ReadByte(); + + case SqlTypes.INT2TYPE: + return (short)BigEndianUtilities.ReadUShortLE(stream); + + case SqlTypes.INT4TYPE: + return (int)BigEndianUtilities.ReadUIntLE(stream); + + case SqlTypes.DATETIM4TYPE: + buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return ReadSmalldatetimeFromBytes(buffer); + + case SqlTypes.FLT4TYPE: + buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return BitConverter.ToSingle(buffer, 0); + + case SqlTypes.MONEY4TYPE: + buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return ReadMoney4FromBytes(buffer); + + case SqlTypes.DATETIMETYPE: + buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return ReadDatetimeFromBytes(buffer); + + case SqlTypes.FLT8TYPE: + buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return BitConverter.ToDouble(buffer, 0); + + case SqlTypes.INT8TYPE: + return (long)BigEndianUtilities.ReadULongLE(stream); + + case SqlTypes.MONEYTYPE: + buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return ReadMoneyFromBytes(buffer); + + default: + throw new ArgumentException("Unknown fixed length type"); + } + } + + private static DateTime ReadSmalldatetimeFromBytes(byte[] bytes) + { + if (bytes.Length != 4) + throw new ArgumentException("Invalid byte array length."); + + // First two bytes represent days since 1900-01-01 + short days = BitConverter.ToInt16(bytes, 0); + + // Next two bytes represent minutes since midnight + short minutes = BitConverter.ToInt16(bytes, 2); + + DateTime baseDate = new DateTime(1900, 1, 1); + return baseDate.AddDays(days).AddMinutes(minutes); + } + + private static decimal ReadMoneyFromBytes(byte[] bytes) + { + if (bytes.Length != 8) + throw new ArgumentException("Invalid byte array length for MONEYTYPE."); + + long value = BitConverter.ToInt64(bytes, 0); + return value / 10000m; // scaled by 10,000 for 4-digit precision + } + + private static decimal ReadMoney4FromBytes(byte[] bytes) + { + if (bytes.Length != 4) + throw new ArgumentException("Invalid byte array length for MONEY4TYPE."); + + int value = BitConverter.ToInt32(bytes, 0); + return value / 10000m; // scaled by 10,000 for 4-digit precision + } + + private static DateTime ReadDatetimeFromBytes(byte[] bytes) + { + if (bytes.Length != 8) + throw new ArgumentException("Invalid byte array length for DATETIMETYPE."); + + int days = BitConverter.ToInt32(bytes, 0); + int timeUnits = BitConverter.ToInt32(bytes, 4); // number of 300th of a second units + + DateTime baseDate = new DateTime(1900, 1, 1); + return baseDate.AddDays(days).AddMilliseconds(timeUnits * 3.333333); // Convert 300th of a second units to milliseconds + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypes.cs b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypes.cs new file mode 100644 index 0000000..1f7f833 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypes.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TDSClient.TDS.Tokens.Type +{ + [Flags] + public enum SqlTypes: byte + { + //Zero length data types + NULLTYPE = 0x1F, + + //Fixed lenght data types + INT1TYPE = 0x30, + BITTYPE = 0x32, + INT2TYPE = 0x34, + INT4TYPE = 0x38, + DATETIM4TYPE = 0x3A, + FLT4TYPE = 0x3B, + MONEYTYPE = 0x3C, + DATETIMETYPE = 0x3D, + FLT8TYPE = 0x3E, + MONEY4TYPE = 0x7A, + INT8TYPE = 0x7F, + DECIMALTYPE = 0x37, + NUMERICTYPE = 0x3F, + + FIXEDLENTYPE = INT1TYPE + | BITTYPE + | INT2TYPE + | INT4TYPE + | DATETIM4TYPE + | FLT4TYPE + | MONEYTYPE + | DATETIMETYPE + | FLT8TYPE + | MONEY4TYPE + | INT8TYPE, + + //Variable length data types + GUIDTYPE = 0x24, + INTNTYPE = 0x26, + BITNTYPE = 0x68, + DECIMALNTYPE = 0x6A, + NUMERICNTYPE = 0x6C, + FLTNTYPE = 0x6D, + MONEYNTYPE = 0x6E, + DATETIMNTYPE = 0x6F, + DATENTYPE = 0x28, + TIMENTYPE = 0x29, + DATETIME2NTYPE = 0x2A, + DATETIMEOFFSETNTYPE = 0x2B, + CHARTYPE = 0x2F, + VARCHARTYPE = 0x27, + BINARYTYPE = 0x2D, + VARBINARYTYPE = 0x25, + BIGVARBINARYTYPE = 0xA5, + BIGVARCHARTYPE = 0xA7, + BIGBINARYTYPE = 0xAD, + BIGCHARTYPE = 0xAF, + NVARCHARTYPE = 0xE7, + NCHARTYPE = 0xEF, + XMLTYPE = 0xF1, + UDTTYPE = 0xF0, + TEXTTYPE = 0x23, + IMAGETYPE = 0x22, + NTEXTTYPE = 0x63, + SSVARIANTTYPE = 0x62, + + BYTELEN_TYPE = GUIDTYPE + | INTNTYPE + | DECIMALTYPE + | NUMERICTYPE + | BITNTYPE + | DECIMALNTYPE + | NUMERICNTYPE + | FLTNTYPE + | MONEYNTYPE + | DATETIMNTYPE + | DATENTYPE + | TIMENTYPE + | DATETIME2NTYPE + | DATETIMEOFFSETNTYPE + | CHARTYPE + | VARCHARTYPE + | BINARYTYPE + | VARBINARYTYPE, + + USHORTLEN_TYPE = BIGVARBINARYTYPE + | BIGVARCHARTYPE + | BIGBINARYTYPE + | BIGCHARTYPE + | NVARCHARTYPE + | NCHARTYPE, + + LONGLEN_TYPE = IMAGETYPE + | NTEXTTYPE + | SSVARIANTTYPE + | TEXTTYPE + | XMLTYPE, + + VARLENTYPE = BYTELEN_TYPE + | USHORTLEN_TYPE + | LONGLEN_TYPE, + + //Partially length type + PARTLENTYPE = XMLTYPE + | BIGVARCHARTYPE + | BIGVARBINARYTYPE + | NVARCHARTYPE + | UDTTYPE, + + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs new file mode 100644 index 0000000..321aa7f --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TDSClient.TDS.Tranasction.Header +{ + public enum HeaderType : ushort + { + QueryNotifications = 0x1, + TransactionDescriptor = 0x2, + TraceActivity = 0x3, + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs new file mode 100644 index 0000000..fc368cd --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs @@ -0,0 +1,38 @@ +using System.IO; +using TDSClient.TDS.Tranasction.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Header.Headers +{ + public class QueryNotificationsHeaderData : ISqlHeaderData + { + public HeaderType HeaderType => HeaderType.QueryNotifications; + public uint Size => sizeof(ushort) + (uint)NotifyId.Length * 2 + sizeof(ushort) + (uint)SSBDeployment.Length * 2 + sizeof(ulong); + public string NotifyId { get; set; } + public string SSBDeployment { get; set; } + public ulong NotifyTimeout { get; set; } + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteUShort(stream, (ushort)NotifyId.Length); + BigEndianUtilities.WriteUnicodeStream(stream, NotifyId); + BigEndianUtilities.WriteUShort(stream, (ushort)SSBDeployment.Length); + BigEndianUtilities.WriteUnicodeStream(stream, SSBDeployment); + BigEndianUtilities.WriteULong(stream, NotifyTimeout); + } + + public bool Unpack(MemoryStream stream) + { + var notifyIdLen = BigEndianUtilities.ReadUShort(stream); + var notifyIdChars = BigEndianUtilities.ReadUnicodeStreamLE(stream, notifyIdLen); + NotifyId = new string(notifyIdChars); + + var ssbDeploymentLen = BigEndianUtilities.ReadUShort(stream); + var ssbDeploymentChars = BigEndianUtilities.ReadUnicodeStreamLE(stream, ssbDeploymentLen); + SSBDeployment = new string(ssbDeploymentChars); + + NotifyTimeout = BigEndianUtilities.ReadULong(stream); + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs new file mode 100644 index 0000000..c21d6f1 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Tranasction.Headers; + +namespace TDSClient.TDS.Tranasction.Header.Headers +{ + public class TraceActivityHeaderData : ISqlHeaderData + { + public HeaderType HeaderType => HeaderType.TraceActivity; + + public Guid ActivityId { get; set; } + public uint Size => 16; + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs new file mode 100644 index 0000000..3161ed7 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tranasction.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Header.Headers +{ + public class TransactionDescriptorHeaderData : ISqlHeaderData + { + internal TransactionDescriptorHeaderData() + { + + } + + public TransactionDescriptorHeaderData(uint id) + { + TransactionDescriptor = 0; + OutstandingRequestCount = id; + } + + public ulong TransactionDescriptor { get; set; } + public uint OutstandingRequestCount { get; set; } = 0; + public uint Size => sizeof(ulong) + sizeof(uint); + public HeaderType HeaderType => HeaderType.TransactionDescriptor; + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteULongLE(stream, TransactionDescriptor); + BigEndianUtilities.WriteUIntLE(stream, OutstandingRequestCount); + } + + public bool Unpack(MemoryStream stream) + { + TransactionDescriptor = BigEndianUtilities.ReadULong(stream); + OutstandingRequestCount = BigEndianUtilities.ReadUInt(stream); + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs new file mode 100644 index 0000000..171acd4 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tranasction.Header; + +namespace TDSClient.TDS.Tranasction.Headers +{ + public interface ISqlHeaderData : IPackageable + { + HeaderType HeaderType { get; } + uint Size { get; } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs new file mode 100644 index 0000000..46cad24 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Headers +{ + public class SqlAllHeadersPacketData : IPackageable + { + public List Headers { get; set; } = new List(); + public uint TotalLength => sizeof(uint) + (uint)Headers.Sum(x => x.Size); + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteUIntLE(stream, TotalLength); + foreach (var header in Headers) + { + header.Pack(stream); + } + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs new file mode 100644 index 0000000..dc9930b --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs @@ -0,0 +1,60 @@ +using System.IO; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tranasction.Header; +using TDSClient.TDS.Tranasction.Header.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Headers +{ + public class SqlHeaderPacketData : IPackageable + { + internal SqlHeaderPacketData() + { + + } + + public SqlHeaderPacketData(ISqlHeaderData data) + { + Data = data; + } + + public ISqlHeaderData Data { get; set; } + + public uint Size => Data.Size + sizeof(ushort) + sizeof(uint); + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteUIntLE(stream, Size); + BigEndianUtilities.WriteUShortLE(stream, (ushort)Data.HeaderType); + Data.Pack(stream); + } + + public bool Unpack(MemoryStream stream) + { + var headerLength = BigEndianUtilities.ReadUInt(stream); + var headerType = (HeaderType)BigEndianUtilities.ReadUShort(stream); + ISqlHeaderData data; + switch(headerType) + { + case HeaderType.QueryNotifications: + data = new QueryNotificationsHeaderData(); + break; + case HeaderType.TransactionDescriptor: + data = new TransactionDescriptorHeaderData(); + break; + case HeaderType.TraceActivity: + data = new TraceActivityHeaderData(); + break; + default: + stream.Seek(headerLength - (sizeof(ushort) + sizeof(uint)), SeekOrigin.Current); + return false; + } + if (!data.Unpack(stream)) + { + return false; + } + Data = data; + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs new file mode 100644 index 0000000..ed96055 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using TDSClient.TDS.Client; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.PreLogin; +using TDSClient.TDS.Tranasction.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Query +{ +#pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() + public class TDSSqlBatchPacketData : ITDSPacketData, IEquatable +#pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() + { + + private string _query = string.Empty; + + public TDSSqlBatchPacketData(string query) + { + _query = query; + } + + public SqlAllHeadersPacketData AllHeaders { get; set; } = new SqlAllHeadersPacketData(); + + public string SqlText => _query; + + public bool Equals(TDSSqlBatchPacketData other) + { + throw new NotImplementedException(); + } + + public ushort Length() => (ushort)(AllHeaders.TotalLength + SqlText.Length * sizeof(char) + sizeof(ulong)); + + public void Pack(MemoryStream stream) + { + AllHeaders.Pack(stream); + //Enclave can be empty stream + //BigEndianUtilities.WriteULong(stream, 0UL); + BigEndianUtilities.WriteUnicodeStream(stream, SqlText); + } + + public bool Unpack(MemoryStream stream) + { + //TODO: Get original length from tds packet size + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs index 0b29b13..7b2bf04 100644 --- a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs +++ b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs @@ -7,6 +7,7 @@ namespace TDSClient.TDS.Utilities { using System; + using System.Collections.Generic; using System.IO; /// @@ -25,6 +26,17 @@ public static void WriteUShort(MemoryStream stream, ushort value) stream.WriteByte((byte)value); } + /// + /// Used to write value to stream in big endian order. + /// + /// MemoryStream to to write the value to. + /// Value to write to MemoryStream. + public static void WriteUShortLE(MemoryStream stream, ushort value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + } + /// /// Used to write value to stream in big endian order. /// @@ -38,6 +50,19 @@ public static void WriteUInt(MemoryStream stream, uint value) stream.WriteByte((byte)value); } + /// + /// Used to write value to stream in big endian order. + /// + /// MemoryStream to to write the value to. + /// Value to write to MemoryStream. + public static void WriteUIntLE(MemoryStream stream, uint value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)(value >> 16)); + stream.WriteByte((byte)(value >> 24)); + } + /// /// Used to write value to stream in big endian order. /// @@ -55,6 +80,23 @@ public static void WriteULong(MemoryStream stream, ulong value) stream.WriteByte((byte)value); } + /// + /// Used to write value to stream in little endian order. + /// + /// MemoryStream to to write the value to. + /// Value to write to MemoryStream. + public static void WriteULongLE(MemoryStream stream, ulong value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)(value >> 16)); + stream.WriteByte((byte)(value >> 24)); + stream.WriteByte((byte)(value >> 32)); + stream.WriteByte((byte)(value >> 40)); + stream.WriteByte((byte)(value >> 48)); + stream.WriteByte((byte)(value >> 56)); + } + /// /// Used to write byte array to stream in big endian order. /// @@ -68,6 +110,20 @@ public static void WriteByteArray(MemoryStream stream, byte[] array) } } + /// + /// Used to write string to stream in big endian order. + /// + /// MemoryStream to to write the value to. + /// Unicode chars to write to MemoryStream. + public static void WriteUnicodeStream(MemoryStream stream, IEnumerable chars) + { + foreach (char c in chars) + { + WriteUShortLE(stream, c); + } + } + + /// /// Used to read a UShort value from stream in big endian order. /// @@ -85,6 +141,22 @@ public static ushort ReadUShort(MemoryStream stream) return result; } + /// + /// Used to read a UShort value from stream in little endian order. + /// + /// MemoryStream from which to read the value. + /// UShort value read from the stream. + public static ushort ReadUShortLE(MemoryStream stream) + { + ushort result = 0; + for (int i = 0; i < 2; i++) + { + result |= (byte)(Convert.ToByte(stream.ReadByte()) << 8 * i); + } + + return result; + } + /// /// Used to read a UInt value from stream in big endian order. /// @@ -102,6 +174,22 @@ public static uint ReadUInt(MemoryStream stream) return result; } + /// + /// Used to read a UInt value from stream in little endian order. + /// + /// MemoryStream from which to read the value. + /// UInt value read from the stream. + public static uint ReadUIntLE(MemoryStream stream) + { + uint result = 0; + for (int i = 0; i < 4; i++) + { + result |= (byte)(Convert.ToByte(stream.ReadByte()) << 8 * i); + } + + return result; + } + /// /// Used to read a ULong value from stream in big endian order. /// @@ -119,6 +207,22 @@ public static ulong ReadULong(MemoryStream stream) return result; } + /// + /// Used to read a ULong value from stream in little endian order. + /// + /// MemoryStream from which to read the value. + /// ULong value read from the stream. + public static ulong ReadULongLE(MemoryStream stream) + { + ulong result = 0; + for (int i = 0; i < 8; i++) + { + result |= (byte)(Convert.ToByte(stream.ReadByte()) << 8 * i); + } + + return result; + } + /// /// Used to read a byte array from stream in big endian order. /// @@ -135,5 +239,23 @@ public static byte[] ReadByteArray(MemoryStream stream, uint length) return result; } + + /// + /// Used to read a Unicode char array from stream in little endian order. + /// + /// MemoryStream from which to read the unicode char array. + /// Length of the unicode array to read. + /// Unicode char array read from the stream. + public static char[] ReadUnicodeStreamLE(MemoryStream stream, int length) + { + char[] result = new char[length]; + //TODO: Check order to read in + for (int i = 0; i < length; i++) + { + result[i] = (char)ReadUShortLE(stream); + } + + return result; + } } } diff --git a/TDSClient/TDSClientLiveTest/Program.cs b/TDSClient/TDSClientLiveTest/Program.cs index 611020d..f778a66 100644 --- a/TDSClient/TDSClientLiveTest/Program.cs +++ b/TDSClient/TDSClientLiveTest/Program.cs @@ -1,4 +1,6 @@ -using System; +using System; +using System.Security.Authentication; +using System.Text; using System.Threading; using TDSClient.TDS.Client; @@ -12,16 +14,15 @@ class Program public static string Password = ""; public static string Database = ""; - static void Main(string[] args) { - TDSSQLTestClient tdsClient = new TDSSQLTestClient(Server, Port, Username, Password, Database); + TDSSQLTestClient tdsClient = new TDSSQLTestClient(Server, Port, Username, Password, Database, SslProtocols.Tls12); TDSClient.TDS.Utilities.LoggingUtilities.SetVerboseLog(Console.Out); - //TDSClient.TDS.Utilities.LoggingUtilities.SetSummaryLog(Console.Out); for (int i = 0; i < 1; i++) { tdsClient.Connect(); + tdsClient.Query("SELECT GETUTCDATE() AS CurrentUTCDateTime"); tdsClient.Disconnect(); Console.WriteLine(); Thread.Sleep(1000); diff --git a/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj b/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj index b223a2c..6d137a3 100644 --- a/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj +++ b/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj @@ -2,7 +2,8 @@ Exe - netcoreapp3.1 + net60 + latest