Skip to content

Commit b9ff243

Browse files
committed
Fixed issue where TDS_BLOB was not being terminated correctly, and was being used for strings greater than 4096 characters instead of 8192.
1 parent 0088eb4 commit b9ff243

File tree

5 files changed

+84
-11
lines changed

5 files changed

+84
-11
lines changed

src/AdoNetCore.AseClient/Internal/StreamWriteExtensions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,17 @@ public static void WriteNullableUShortPrefixedByteArray(this Stream stream, byte
125125
public static void WriteBlobSpecificIntPrefixedByteArray(this Stream stream, byte[] value)
126126
{
127127
var len = value.Length;
128-
var endOfBlobLen = (uint) len | 0x80000000; //highest-order bit set means end of blob data
128+
var endOfBlobLen = (uint) len | 0x80000000; //highest-order bit set means there is more data
129129
stream.WriteUInt(endOfBlobLen);
130130
stream.Write(value, 0, len);
131131
}
132132

133+
public static void WriteBlobSpecificTerminator(this Stream stream)
134+
{
135+
var endOfBlobLen = (uint)0; //highest-order bit unset means end of blob data
136+
stream.WriteUInt(endOfBlobLen);
137+
}
138+
133139
public static void WriteIntPrefixedByteArray(this Stream stream, byte[] value)
134140
{
135141
var len = value.Length;

src/AdoNetCore.AseClient/Internal/TypeMap.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace AdoNetCore.AseClient.Internal
99
internal static class TypeMap
1010
{
1111
private const int VarLongBoundary = 255;
12-
//Above this length, send strings as TDS_BLOBs
13-
private const int StringAsBlobBoundary = 8192;
12+
//Above this length in bytes, send strings as TDS_BLOBs
13+
private const int StringAsBlobBoundary = 16384;
1414
private const int BinaryAsBlobBoundary = 16384;
1515

1616
private static readonly Dictionary<DbType, Func<object, int, TdsDataType>> DbToTdsMap = new Dictionary<DbType, Func<object, int, TdsDataType>>

src/AdoNetCore.AseClient/Internal/ValueWriter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,11 +314,13 @@ private static void WriteTDS_BLOB(object value, Stream stream, FormatItem format
314314
stream.WriteByte((byte)SerializationType.SER_DEFAULT);
315315
stream.WriteNullableUShortPrefixedByteArray(format.ClassId);
316316
stream.WriteBlobSpecificIntPrefixedByteArray(Encoding.Unicode.GetBytes(s));
317+
stream.WriteBlobSpecificTerminator();
317318
break;
318319
case byte[] ba:
319320
stream.WriteByte((byte)SerializationType.SER_DEFAULT);
320321
stream.WriteNullableUShortPrefixedByteArray(format.ClassId);
321322
stream.WriteBlobSpecificIntPrefixedByteArray(ba);
323+
stream.WriteBlobSpecificTerminator();
322324
break;
323325
default:
324326
throw new AseException($"TDS_BLOB support for {value.GetType().Name} not yet implemented");

test/AdoNetCore.AseClient.Tests/Integration/Insert/BinaryTests.cs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,19 @@ private DbConnection GetConnection()
2121
return Activator.CreateInstance<T>().GetConnection(ConnectionStrings.Pooled);
2222
}
2323

24-
private const string SetUpSql = @"create table [dbo].[insert_image_tests] (image_field image null)";
25-
private const string CleanUpSql = @"IF EXISTS(SELECT 1 FROM sysobjects WHERE name = 'insert_image_tests')
24+
private const string SetUpSql = @"
25+
create table [dbo].[insert_image_tests] (image_field image null)
26+
create table [dbo].[insert_image_date_tests] (image_field image null, date_field datetime null)";
27+
28+
private const string CleanUpSql = @"
29+
IF EXISTS(SELECT 1 FROM sysobjects WHERE name = 'insert_image_tests')
2630
BEGIN
2731
drop table [dbo].[insert_image_tests]
32+
END
33+
34+
IF EXISTS(SELECT 1 FROM sysobjects WHERE name = 'insert_image_date_tests')
35+
BEGIN
36+
drop table [dbo].[insert_image_date_tests]
2837
END";
2938

3039
[SetUp]
@@ -55,6 +64,8 @@ public static IEnumerable<TestCaseData> Insert_Parameter_Cases()
5564
yield return new TestCaseData(100);
5665
yield return new TestCaseData(127);
5766
yield return new TestCaseData(1000);
67+
yield return new TestCaseData(4096);
68+
yield return new TestCaseData(4097);
5869
yield return new TestCaseData(8192);
5970
yield return new TestCaseData(8193);
6071
yield return new TestCaseData(10000);
@@ -81,11 +92,33 @@ public void Insert_Parameter_Dapper(int count)
8192
Insert_Parameter_VerifyResult(GetConnection, "insert_image_tests", "image_field", value);
8293
}
8394

84-
private void Insert_Parameter_VerifyResult(Func<DbConnection> getConnection, string table, string field, byte[] expected)
95+
[TestCaseSource(nameof(Insert_Parameter_Cases))]
96+
public void Insert_Parameter_With_Date_Dapper(int count)
97+
{
98+
var value = new byte[count];
99+
var date = new DateTime(2001, 2, 3, 4, 5, 6);
100+
101+
102+
using (var connection = GetConnection())
103+
{
104+
connection.Execute("set textsize 1000000");
105+
var p = new DynamicParameters();
106+
p.Add("@image_field", value, DbType.Binary);
107+
p.Add("@date_field", date, DbType.DateTime);
108+
connection.Execute("insert into [dbo].[insert_image_date_tests] (image_field, date_field) values (@image_field, @date_field)", p);
109+
var insertedLength = connection.QuerySingle<int>("select top 1 datalength(image_field) from [dbo].[insert_image_date_tests]");
110+
Assert.AreEqual(value.Length, insertedLength);
111+
}
112+
113+
Insert_Parameter_VerifyResult(GetConnection, "insert_image_date_tests", "image_field", value);
114+
Insert_Parameter_VerifyResult(GetConnection, "insert_image_date_tests", "date_field", date);
115+
}
116+
117+
private void Insert_Parameter_VerifyResult<TColumn>(Func<DbConnection> getConnection, string table, string field, TColumn expected)
85118
{
86119
using (var connection = getConnection())
87120
{
88-
Assert.AreEqual(expected, connection.QuerySingle<byte[]>($"select top 1 {field} from [dbo].[{table}]"));
121+
Assert.AreEqual(expected, connection.QuerySingle<TColumn>($"select top 1 {field} from [dbo].[{table}]"));
89122
}
90123
}
91124
}

test/AdoNetCore.AseClient.Tests/Integration/Insert/TextTests.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,19 @@ private DbConnection GetConnection()
2121
return Activator.CreateInstance<T>().GetConnection(ConnectionStrings.Pooled);
2222
}
2323

24-
private const string SetUpSql = @"create table [dbo].[insert_text_tests] (text_field text null)";
25-
private const string CleanUpSql = @"IF EXISTS(SELECT 1 FROM sysobjects WHERE name = 'insert_text_tests')
24+
private const string SetUpSql = @"
25+
create table [dbo].[insert_text_tests] (text_field text null)
26+
create table [dbo].[insert_text_date_tests] (text_field text null, date_field datetime null)";
27+
28+
private const string CleanUpSql = @"
29+
IF EXISTS(SELECT 1 FROM sysobjects WHERE name = 'insert_text_tests')
2630
BEGIN
2731
drop table [dbo].[insert_text_tests]
32+
END
33+
34+
IF EXISTS(SELECT 1 FROM sysobjects WHERE name = 'insert_text_date_tests')
35+
BEGIN
36+
drop table [dbo].[insert_text_date_tests]
2837
END";
2938

3039
[SetUp]
@@ -55,6 +64,8 @@ public static IEnumerable<TestCaseData> Insert_Parameter_Cases()
5564
yield return new TestCaseData(100);
5665
yield return new TestCaseData(127);
5766
yield return new TestCaseData(1000);
67+
yield return new TestCaseData(4096);
68+
yield return new TestCaseData(4097);
5869
yield return new TestCaseData(8192);
5970
yield return new TestCaseData(8193);
6071
yield return new TestCaseData(10000);
@@ -81,11 +92,32 @@ public void Insert_Parameter_Dapper(int count)
8192
Insert_Parameter_VerifyResult(GetConnection, "insert_text_tests", "text_field", value);
8293
}
8394

84-
private void Insert_Parameter_VerifyResult(Func<DbConnection> getConnection, string table, string field, string expected)
95+
[TestCaseSource(nameof(Insert_Parameter_Cases))]
96+
public void Insert_Parameter_With_Date_Dapper(int count)
97+
{
98+
var value = new string('1', count);
99+
var date = new DateTime(2001, 2, 3, 4, 5, 6);
100+
101+
using (var connection = GetConnection())
102+
{
103+
connection.Execute("set textsize 1000000");
104+
var p = new DynamicParameters();
105+
p.Add("@text_field", value, DbType.String);
106+
p.Add("@date_field", date, DbType.DateTime);
107+
connection.Execute("insert into [dbo].[insert_text_date_tests] (text_field, date_field) values (@text_field, @date_field)", p);
108+
var insertedLength = connection.QuerySingle<int>("select top 1 len(text_field) from [dbo].[insert_text_date_tests]");
109+
Assert.AreEqual(value.Length, insertedLength);
110+
}
111+
112+
Insert_Parameter_VerifyResult(GetConnection, "insert_text_date_tests", "text_field", value);
113+
Insert_Parameter_VerifyResult(GetConnection, "insert_text_date_tests", "date_field", date);
114+
}
115+
116+
private void Insert_Parameter_VerifyResult<TColumn>(Func<DbConnection> getConnection, string table, string field, TColumn expected)
85117
{
86118
using (var connection = getConnection())
87119
{
88-
Assert.AreEqual(expected, connection.QuerySingle<string>($"select top 1 {field} from [dbo].[{table}]"));
120+
Assert.AreEqual(expected, connection.QuerySingle<TColumn>($"select top 1 {field} from [dbo].[{table}]"));
89121
}
90122
}
91123
}

0 commit comments

Comments
 (0)