Skip to content

Commit

Permalink
Do not execute a query with an already cancelled token
Browse files Browse the repository at this point in the history
Fixes #3379
  • Loading branch information
vonzshik committed Nov 22, 2020
1 parent 52e6710 commit 4aad4c0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/Npgsql/NpgsqlCommand.cs
Expand Up @@ -1141,6 +1141,10 @@ internal async ValueTask<NpgsqlDataReader> ExecuteReader(CommandBehavior behavio
{
if (conn.TryGetBoundConnector(out var connector))
{
cancellationToken.ThrowIfCancellationRequested();
// We cannot pass a token here, as we'll cancel a non-send query
// Also, we don't pass the cancellation token to StartUserAction, since that would make it scope to the entire action (command execution)
// whereas it should only be scoped to the Execute method.
connector.StartUserAction(ConnectorState.Executing, this, CancellationToken.None);
Task? sendTask = null;
Expand Down
26 changes: 24 additions & 2 deletions test/Npgsql.Tests/CommandTests.cs
Expand Up @@ -288,6 +288,28 @@ public async Task Cancel()
await cancelTask;
}

[Test]
public async Task CancelAsyncImmediately()
{
if (IsMultiplexing)
return; // Multiplexing, cancellation

using var cts = new CancellationTokenSource();
cts.Cancel();

await using var conn = await OpenConnectionAsync();
using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT 1";

var t = cmd.ExecuteScalarAsync(cts.Token);
Assert.That(t.IsCompleted, Is.True); // checks, if a query has completed synchronously
Assert.That(t.Status, Is.EqualTo(TaskStatus.Canceled));
Assert.ThrowsAsync<OperationCanceledException>(async () => await t);

Assert.That(conn.FullState, Is.EqualTo(ConnectionState.Open));
Assert.That(await conn.ExecuteScalarAsync("SELECT 1"), Is.EqualTo(1));
}

[Test, Description("Cancels an async query with the cancellation token, with successful PG cancellation")]
public async Task CancelAsyncSoft()
{
Expand All @@ -296,7 +318,7 @@ public async Task CancelAsyncSoft()

await using var conn = await OpenConnectionAsync();
using var cmd = CreateSleepCommand(conn);
var cancellationSource = new CancellationTokenSource();
using var cancellationSource = new CancellationTokenSource();
var t = cmd.ExecuteNonQueryAsync(cancellationSource.Token);
cancellationSource.Cancel();

Expand All @@ -322,7 +344,7 @@ public async Task CancelAsyncHard()

var processId = conn.ProcessID;

var cancellationSource = new CancellationTokenSource();
using var cancellationSource = new CancellationTokenSource();
using var cmd = new NpgsqlCommand("SELECT 1", conn);
var t = cmd.ExecuteScalarAsync(cancellationSource.Token);
cancellationSource.Cancel();
Expand Down

0 comments on commit 4aad4c0

Please sign in to comment.