Skip to content

Commit

Permalink
Fix "Can't replace active reader". Fixes #1469
Browse files Browse the repository at this point in the history
If an exception happens during MySqlConnection.Close(), the connection may be left in an invalid state, thinking it still has an open MySqlDataReader. Clear this field during cleanup to remove this invalid state.
  • Loading branch information
bgrainger committed Oct 20, 2024
1 parent 929dd40 commit 390d579
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/MySqlConnector/MySqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,9 @@ private async Task DoCloseAsync(bool changeState, IOBehavior ioBehavior)
{
if (m_session is not null)
{
// under exceptional circumstances, we may enter this code without having called FinishQuerying, which clears this field
m_activeReader = null;

if (GetInitializedConnectionSettings().Pooling)
{
await m_session.ReturnToPoolAsync(ioBehavior, this).ConfigureAwait(false);
Expand Down
43 changes: 43 additions & 0 deletions tests/MySqlConnector.Tests/ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,49 @@ public void ReadInfinity()
Assert.False(reader.Read());
}

[Fact]
public void ReplaceActiveReader()
{
var connection = new MySqlConnection(m_csb.ConnectionString);
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "select disconnect";
command.CommandTimeout = 600;
var reader = command.ExecuteReader();
Assert.True(reader.Read());
Assert.Equal(1, reader.GetInt32(0));
}

try
{
connection.Close();
}
catch (MySqlEndOfStreamException)
{
}

connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT 1;";
var reader = command.ExecuteReader();
Assert.True(reader.Read());
Assert.Equal(1, reader.GetInt32(0));
}
connection.Close();

connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT 2;";
var reader = command.ExecuteReader();
Assert.True(reader.Read());
Assert.Equal(2, reader.GetInt32(0));
}
connection.Close();
}

private static async Task WaitForConditionAsync<T>(T expected, Func<T> getValue)
{
var sw = Stopwatch.StartNew();
Expand Down
14 changes: 14 additions & 0 deletions tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,20 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
}
else if (query == "select disconnect")
{
var packets = new[]
{
new byte[] { 1 }, // one column
[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x41, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x01, 0, 0x1F, 0, 0], // column definition (int)
[0xFE, 0, 0, 2, 0], // EOF
[1, 0x31], // 1
};
for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
stream.Close();
client.Close();
}
else
{
await SendAsync(stream, 1, x => WriteError(x, "Unhandled query: " + query));
Expand Down

0 comments on commit 390d579

Please sign in to comment.