Skip to content

Commit 947c59f

Browse files
committed
Throw exception when a large packet is detected. Fixes #40
1 parent edc5cb4 commit 947c59f

File tree

2 files changed

+87
-37
lines changed

2 files changed

+87
-37
lines changed

src/MySqlConnector/MySqlClient/CommandExecutors/TextCommandExecutor.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
using System.Data;
1+
using System;
2+
using System.Data;
23
using System.Data.Common;
4+
using System.IO;
5+
using System.Net.Sockets;
36
using System.Threading;
47
using System.Threading.Tasks;
58
using MySql.Data.Protocol.Serialization;
@@ -55,8 +58,18 @@ public virtual async Task<DbDataReader> ExecuteReaderAsync(string commandText, M
5558
statementPreparerOptions |= StatementPreparerOptions.OldGuids;
5659
var preparer = new MySqlStatementPreparer(commandText, parameterCollection, statementPreparerOptions);
5760
var payload = new PayloadData(preparer.ParseAndBindParameters());
58-
await m_command.Connection.Session.SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
59-
return await MySqlDataReader.CreateAsync(m_command, behavior, ioBehavior, cancellationToken).ConfigureAwait(false);
61+
try
62+
{
63+
await m_command.Connection.Session.SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
64+
return await MySqlDataReader.CreateAsync(m_command, behavior, ioBehavior, cancellationToken).ConfigureAwait(false);
65+
}
66+
catch (Exception ex) when (payload.ArraySegment.Count > 4194304 && (ex is SocketException || ex is IOException))
67+
{
68+
// the default MySQL Server value for max_allowed_packet (in MySQL 5.7) is 4MiB: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_allowed_packet
69+
// use "decimal megabytes" (to round up) when creating the exception message
70+
int megabytes = payload.ArraySegment.Count / 1000000;
71+
throw new MySqlException("Error submitting {0}MB packet; ensure 'max_allowed_packet' is greater than {0}MB.".FormatInvariant(megabytes), ex);
72+
}
6073
}
6174

6275
readonly MySqlCommand m_command;

tests/SideBySide/DataTypes.cs

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -510,30 +510,50 @@ public void QueryBlob(string column, int padLength)
510510
[InlineData("TinyBlob", 255)]
511511
[InlineData("Blob", 65535)]
512512
[InlineData("MediumBlob", 16777215)]
513+
[InlineData("LongBlob", 33554432)]
513514
[InlineData("LongBlob", 67108864)]
515+
[InlineData("LongBlob", 134217728)]
514516
public async Task InsertLargeBlobAsync(string column, int size)
515517
{
516-
// verify that this amount of data can be sent to MySQL successfully
517-
var maxAllowedPacket = (await m_database.Connection.QueryAsync<int>("select @@max_allowed_packet").ConfigureAwait(false)).Single();
518-
if (maxAllowedPacket < size)
519-
return; // TODO: Use [SkippableFact]
518+
// NOTE: MySQL Server will reset the connection when it receives an oversize packet, so we need to create a test-specific connection here
519+
using (var connection = new MySqlConnection(AppConfig.CreateConnectionStringBuilder().ConnectionString))
520+
{
521+
await connection.OpenAsync();
520522

521-
var data = CreateByteArray(size);
523+
// verify that this amount of data can be sent to MySQL successfully
524+
var maxAllowedPacket = (await connection.QueryAsync<int>("select @@max_allowed_packet").ConfigureAwait(false)).Single();
525+
var shouldFail = maxAllowedPacket < size + 100;
522526

523-
long lastInsertId;
524-
using (var cmd = new MySqlCommand(Invariant($"insert into datatypes_blobs(`{column}`) values(?)"), m_database.Connection)
525-
{
526-
Parameters = { new MySqlParameter { Value = data } }
527-
})
528-
{
529-
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
530-
lastInsertId = cmd.LastInsertedId;
531-
}
527+
var data = CreateByteArray(size);
528+
529+
long lastInsertId;
530+
using (var cmd = new MySqlCommand(Invariant($"insert into datatypes_blobs(`{column}`) values(?)"), connection)
531+
{
532+
Parameters = { new MySqlParameter { Value = data } }
533+
})
534+
{
535+
try
536+
{
537+
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
538+
lastInsertId = cmd.LastInsertedId;
539+
Assert.False(shouldFail);
540+
}
541+
catch (MySqlException ex)
542+
{
543+
lastInsertId = -1;
544+
Assert.True(shouldFail);
545+
Assert.Contains("packet", ex.Message);
546+
}
547+
}
532548

533-
var queryResult = (await m_database.Connection.QueryAsync<byte[]>(Invariant($"select `{column}` from datatypes_blobs where rowid = {lastInsertId}")).ConfigureAwait(false)).Single();
534-
TestUtilities.AssertEqual(data, queryResult);
549+
if (!shouldFail)
550+
{
551+
var queryResult = (await connection.QueryAsync<byte[]>(Invariant($"select `{column}` from datatypes_blobs where rowid = {lastInsertId}")).ConfigureAwait(false)).Single();
552+
TestUtilities.AssertEqual(data, queryResult);
535553

536-
await m_database.Connection.ExecuteAsync(Invariant($"delete from datatypes_blobs where rowid = {lastInsertId}")).ConfigureAwait(false);
554+
await connection.ExecuteAsync(Invariant($"delete from datatypes_blobs where rowid = {lastInsertId}")).ConfigureAwait(false);
555+
}
556+
}
537557
}
538558

539559
[Theory]
@@ -543,27 +563,44 @@ public async Task InsertLargeBlobAsync(string column, int size)
543563
[InlineData("LongBlob", 67108864)]
544564
public void InsertLargeBlobSync(string column, int size)
545565
{
546-
// verify that this amount of data can be sent to MySQL successfully
547-
var maxAllowedPacket = m_database.Connection.Query<int>("select @@max_allowed_packet").Single();
548-
if (maxAllowedPacket < size)
549-
return; // TODO: Use [SkippableFact]
566+
// NOTE: MySQL Server will reset the connection when it receives an oversize packet, so we need to create a test-specific connection here
567+
using (var connection = new MySqlConnection(AppConfig.CreateConnectionStringBuilder().ConnectionString))
568+
{
569+
connection.Open();
550570

551-
var data = CreateByteArray(size);
571+
// verify that this amount of data can be sent to MySQL successfully
572+
var maxAllowedPacket = m_database.Connection.Query<int>("select @@max_allowed_packet").Single();
573+
var shouldFail = maxAllowedPacket < size + 100;
552574

553-
long lastInsertId;
554-
using (var cmd = new MySqlCommand(Invariant($"insert into datatypes_blobs(`{column}`) values(?)"), m_database.Connection)
555-
{
556-
Parameters = { new MySqlParameter { Value = data } }
557-
})
558-
{
559-
cmd.ExecuteNonQuery();
560-
lastInsertId = cmd.LastInsertedId;
561-
}
575+
var data = CreateByteArray(size);
562576

563-
var queryResult = m_database.Connection.Query<byte[]>(Invariant($"select `{column}` from datatypes_blobs where rowid = {lastInsertId}")).Single();
564-
TestUtilities.AssertEqual(data, queryResult);
577+
long lastInsertId;
578+
using (var cmd = new MySqlCommand(Invariant($"insert into datatypes_blobs(`{column}`) values(?)"), connection)
579+
{
580+
Parameters = { new MySqlParameter { Value = data } }
581+
})
582+
{
583+
try
584+
{
585+
cmd.ExecuteNonQuery();
586+
lastInsertId = cmd.LastInsertedId;
587+
}
588+
catch (MySqlException ex)
589+
{
590+
lastInsertId = -1;
591+
Assert.True(shouldFail);
592+
Assert.Contains("packet", ex.Message);
593+
}
594+
}
565595

566-
m_database.Connection.Execute(Invariant($"delete from datatypes_blobs where rowid = {lastInsertId}"));
596+
if (!shouldFail)
597+
{
598+
var queryResult = connection.Query<byte[]>(Invariant($"select `{column}` from datatypes_blobs where rowid = {lastInsertId}")).Single();
599+
TestUtilities.AssertEqual(data, queryResult);
600+
601+
connection.Execute(Invariant($"delete from datatypes_blobs where rowid = {lastInsertId}"));
602+
}
603+
}
567604
}
568605

569606
private static byte[] CreateByteArray(int size)

0 commit comments

Comments
 (0)