Skip to content

Commit ebc2c2f

Browse files
committed
Fix exception when string value crosses a packet boundary.
1 parent 5ac222f commit ebc2c2f

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

src/MySqlConnector/MySql.Data.MySqlClient/MySqlBulkCopy.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,13 @@ static bool WriteString(string value, Span<byte> output, out int bytesWritten)
369369
var nextIndex = value.IndexOfAny(s_specialCharacters, index);
370370
if (nextIndex == -1)
371371
nextIndex = value.Length;
372-
var encodedBytesWritten = Encoding.UTF8.GetBytes(value.AsSpan(index, nextIndex - index), output);
373-
if (encodedBytesWritten == output.Length)
372+
var encodedSize = Encoding.UTF8.GetByteCount(value.AsSpan(index, nextIndex - index));
373+
if (encodedSize > output.Length)
374374
{
375-
// TODO: this isn't strictly an error
376375
bytesWritten = 0;
377376
return false;
378377
}
379-
378+
var encodedBytesWritten = Encoding.UTF8.GetBytes(value.AsSpan(index, nextIndex - index), output);
380379
bytesWritten += encodedBytesWritten;
381380
output = output.Slice(encodedBytesWritten);
382381
index = nextIndex;

src/MySqlConnector/Utilities/Utility.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ public static string GetString(this Encoding encoding, ReadOnlySpan<byte> span)
4444
#endif
4545
}
4646

47+
public static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan<char> chars)
48+
{
49+
if (chars.Length == 0)
50+
return 0;
51+
52+
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
53+
{
54+
return encoding.GetByteCount(charsPtr, chars.Length);
55+
}
56+
}
57+
4758
public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, Span<byte> bytes)
4859
{
4960
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))

tests/SideBySide/BulkLoaderSync.cs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,8 @@ public void BulkLoadDataReader()
529529
}
530530

531531
#if !NETCOREAPP1_1_2
532-
[Fact]
533-
public void BulkLoadDataTableWithLongData()
532+
[SkippableFact(ServerFeatures.LargePackets)]
533+
public void BulkLoadDataTableWithLongBlob()
534534
{
535535
var dataTable = new DataTable()
536536
{
@@ -559,6 +559,48 @@ public void BulkLoadDataTableWithLongData()
559559
DestinationTableName = "bulk_load_data_table",
560560
};
561561
bulkCopy.WriteToServer(dataTable);
562+
563+
using (var cmd = new MySqlCommand(@"select sum(length(b)) from bulk_load_data_table;", connection))
564+
{
565+
Assert.Equal(16_777_000m, cmd.ExecuteScalar());
566+
}
567+
}
568+
569+
[SkippableFact(ServerFeatures.LargePackets)]
570+
public void BulkLoadDataTableWithLongString()
571+
{
572+
var dataTable = new DataTable()
573+
{
574+
Columns =
575+
{
576+
new DataColumn("id", typeof(int)),
577+
new DataColumn("data", typeof(string)),
578+
},
579+
Rows =
580+
{
581+
new object[] { 1, new string('a', 16_777_000) },
582+
new object[] { 2, new string('b', 16_777_000) },
583+
},
584+
};
585+
586+
using var connection = new MySqlConnection(GetLocalConnectionString());
587+
connection.Open();
588+
using (var cmd = new MySqlCommand(@"drop table if exists bulk_load_data_table;
589+
create table bulk_load_data_table(a int, b longtext);", connection))
590+
{
591+
cmd.ExecuteNonQuery();
592+
}
593+
594+
var bulkCopy = new MySqlBulkCopy(connection)
595+
{
596+
DestinationTableName = "bulk_load_data_table",
597+
};
598+
bulkCopy.WriteToServer(dataTable);
599+
600+
using (var cmd = new MySqlCommand(@"select sum(length(b)) from bulk_load_data_table;", connection))
601+
{
602+
Assert.Equal(33_554_000m, cmd.ExecuteScalar());
603+
}
562604
}
563605

564606
[Fact]
@@ -605,7 +647,7 @@ public void BulkLoadDataTableWithSpecialCharacters()
605647
}
606648

607649
[Fact]
608-
public void BulkLoadDataTableWithTooLongData()
650+
public void BulkLoadDataTableWithTooLongBlob()
609651
{
610652
var dataTable = new DataTable()
611653
{
@@ -633,6 +675,36 @@ public void BulkLoadDataTableWithTooLongData()
633675
};
634676
Assert.Throws<MySqlException>(() => bulkCopy.WriteToServer(dataTable));
635677
}
678+
679+
[Fact]
680+
public void BulkLoadDataTableWithTooLongString()
681+
{
682+
var dataTable = new DataTable()
683+
{
684+
Columns =
685+
{
686+
new DataColumn("data", typeof(string)),
687+
},
688+
Rows =
689+
{
690+
new object[] { new string('a', 16_777_400) },
691+
}
692+
};
693+
694+
using var connection = new MySqlConnection(GetLocalConnectionString());
695+
connection.Open();
696+
using (var cmd = new MySqlCommand(@"drop table if exists bulk_load_data_table;
697+
create table bulk_load_data_table(a int, b longblob);", connection))
698+
{
699+
cmd.ExecuteNonQuery();
700+
}
701+
702+
var bulkCopy = new MySqlBulkCopy(connection)
703+
{
704+
DestinationTableName = "bulk_load_data_table",
705+
};
706+
Assert.Throws<MySqlException>(() => bulkCopy.WriteToServer(dataTable));
707+
}
636708
#endif
637709
#endif
638710

0 commit comments

Comments
 (0)