Skip to content

Fix the statement after a failed one returning no rows - #469

Merged
isoos merged 1 commit into
isoos:masterfrom
xvrh:xha/statement-error-after-ready
Oct 1, 2026
Merged

isoos merged 1 commit into
isoos:masterfrom
xvrh:xha/statement-error-after-ready

Conversation

@xvrh

@xvrh xvrh commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

When a statement fails, the statement run right after it on the same connection can come back empty instead of with its real result. This needs it to run from another session (for example a new run call), and the server's end-of-statement message to arrive a little after the error.

Here SELECT 1/0 fails, then three statements follow on the same connection. The end-of-statement message after the error is delayed by 300 ms, as a busy machine or network can do:

try {
  await conn.run((s) => s.execute('SELECT 1/0'));
} catch (_) {}
for (final n in [2, 3, 4]) {
  print(await conn.run((s) => s.execute('SELECT $n')));
}
Before, asserts on (tests) Before, asserts off (production) After
SELECT 2 throws Previous operation ... did not clean up [] [[2]]
SELECT 3 throws [[3]] [[3]]
SELECT 4 throws [[4]] [[4]]

In production, a lookup right after a failed insert could report "not found". In tests, it shows up as a flaky assertion failure. Any error triggers it: a constraint violation, a division by zero, a timeout.

It's rare. The built-in Pool discards a connection after an error, so it isn't affected. The two server messages usually arrive together, and under load they sometimes don't. It affects a Connection used directly, and pools that keep their connections after an error.

The fix: the error is now thrown only once the connection has finished with the failed statement, so the next statement can't start too early.

Tests: test/error_before_ready_test.dart delays the end-of-statement message after an error, so the problem happens every time. It covers a failed statement in a run session, one on the connection itself, one in the simple protocol, and one cancelled by its timeout, each followed by a SELECT 2. All four fail before this change and pass after. The full suite passes.

… finished with it.

A statement run right after the error from another session (a `run`
after a failed `execute`, say) could start too early: it failed with
"Previous operation ... did not clean up" or, with asserts disabled,
returned no rows. The error is now thrown once the connection has
finished with the failed statement.
@isoos
isoos merged commit c151b33 into isoos:master Oct 1, 2026
1 check passed
@isoos

isoos commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants