Sitelet https://github.com/npgsql/npgsql/pull/6161/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Npgsql/NpgsqlDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,13 @@ async Task Consume(bool async, Exception? firstException = null)

// Skip over the other result sets. Note that this does tally records affected from CommandComplete messages, and properly sets
// state for auto-prepared statements
while (true)
//
// The only exception is when the connector is broken (which can happen in the middle of consuming)
// As then there is no point in going forward
//
// While we can also check our local state (State == Closed)
// It's probably better to rely on connector since it's private and its state can't be changed
while (!Connector.IsBroken)
{
try
{
Expand Down
35 changes: 35 additions & 0 deletions test/Npgsql.Tests/ReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2371,6 +2371,41 @@ await pgMock
Assert.That(conn.Connector!.State, Is.EqualTo(ConnectorState.Ready));
}

[Test, IssueLink("https://github.com/npgsql/npgsql/issues/6160")]
[Description("Consuming result set shouldn't go infinite in case connection is broken")]
public async Task Bug6160()
{
var csb = new NpgsqlConnectionStringBuilder(ConnectionString)
{
// Set to -1 to trigger immediate connection break on timeout
CancellationTimeout = -1,
CommandTimeout = 1
};
await using var postmasterMock = PgPostmasterMock.Start(csb.ConnectionString);
await using var dataSource = CreateDataSource(postmasterMock.ConnectionString);
await using var conn = await dataSource.OpenConnectionAsync();

var pgMock = await postmasterMock.WaitForServerConnection();
await pgMock
.WriteParseComplete()
.WriteBindComplete()
.WriteRowDescription(new FieldDescription(Int4Oid))
.WriteDataRow(new byte[4])
.FlushAsync();

await using var cmd = new NpgsqlCommand("SELECT 1", conn);
await using (var reader = await cmd.ExecuteReaderAsync(Behavior | CommandBehavior.SingleRow))
{
await reader.ReadAsync();
// The second read will try to consume the whole resultset due to CommandBehavior.SingleRow
// Which will fail with timeout (and immediate connection break) since we didn't send anything else beside the first row
var ex = Assert.ThrowsAsync<NpgsqlException>(async () => await reader.ReadAsync())!;
Assert.That(ex.InnerException, Is.TypeOf<TimeoutException>());

Assert.That(conn.State, Is.EqualTo(ConnectionState.Closed));
}
}

#endregion

#region Initialization / setup / teardown
Expand Down