Skip to content

Fix BEGIN/COMMIT/ROLLBACK silently failing under server cursor mode - #10321

Open
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/8991-servercursor-commit-rollback
Open

Fix BEGIN/COMMIT/ROLLBACK silently failing under server cursor mode#10321
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/8991-servercursor-commit-rollback

Conversation

@dpage

@dpage dpage commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Reported as a UI glitch (the result grid stays visible instead of
switching to the Messages tab after Commit/Rollback with "server
cursor" mode on), but the root cause is more serious: under server
cursor mode, BEGIN/COMMIT/ROLLBACK never actually reached the database.

execute_void() reuses whatever cursor is cached on the connection,
which under server cursor mode is the named/server-side cursor left
over from the last SELECT. A named cursor's execute() always wraps
the statement as DECLARE ... CURSOR FOR <query>, which can't express
a transaction-control statement (DECLARE ... CURSOR FOR COMMIT is a
syntax error) — and it actually failed one step earlier still, on a
prepare= keyword the server-side cursor's execute() doesn't accept
at all (TypeError: keyword not supported: prepare). That exception
was swallowed by a blanket except Exception in the background query
thread, so the statement silently never ran, leaving the transaction
open with no error shown to the user. The empty grid was just the
visible fallout: the next /poll picked up the previous query's
leftover column info instead of reporting "no result set".

This routes BEGIN/COMMIT/ROLLBACK through a throwaway plain cursor
instead of the cached server-side one when server cursor mode is
active, and clears the stale column info so poll() correctly reports
no result set afterwards.

Fixes #8991.

Test plan

  • Verified the failure mode directly against a live PostgreSQL 18
    connection (both the prepare TypeError and the underlying
    DECLARE ... CURSOR FOR COMMIT syntax error), and confirmed the fix's
    approach (a plain connection.cursor() alongside an open named
    cursor) commits correctly and reports description is None
    afterwards.
  • Added test_execute_void_server_cursor.py, covering COMMIT and
    ROLLBACK with a cached server-side cursor: asserts the statement runs
    on a plain cursor (not the cached server one) and that stale column
    info/row count are cleared. Confirmed it fails without the fix and
    passes with it.
  • python regression/runtests.py --pkg utils.driver.psycopg3.tests.test_execute_void_server_cursor
    passes.

…mode (pgadmin-org#8991)

execute_void() blindly reused whatever cursor was cached for the
connection, which under "server cursor" mode is the named/server-side
AsyncDictServerCursor left over from the last SELECT. A named cursor's
execute() always wraps the statement as `DECLARE ... CURSOR FOR
<query>`, which cannot express a transaction-control statement, so
BEGIN/COMMIT/ROLLBACK silently failed (failing one step earlier still,
on a `prepare` keyword the server-side cursor's execute() doesn't
accept at all) and the exception was swallowed by the background query
thread. The transaction was therefore never actually committed or
rolled back, and the next poll() picked up the previous query's
leftover column info, which is what made the result grid appear
instead of the Messages tab.

Run the statement through a throwaway plain cursor instead, leaving
the cached server-side cursor untouched, and clear the stale column
info so poll() correctly reports no result set.
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@dpage, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 6 minutes

Limit details: You’ve used all 8 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 21db9906-066f-4308-8fd9-b29a0ab96e3d

📥 Commits

Reviewing files that changed from the base of the PR and between 0ebefaf and bd95683.

📒 Files selected for processing (2)
  • web/pgadmin/utils/driver/psycopg3/connection.py
  • web/pgadmin/utils/driver/psycopg3/tests/test_execute_void_server_cursor.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

Result grid does not move messages tab when commit/rollback button is clicked with server cursor on.

1 participant