Skip to content

Track a client's parameter changes on the server connection - #1299

Open
IgorOhrimenko wants to merge 2 commits into
pgdogdev:mainfrom
IgorOhrimenko:fix-reset-in-transaction
Open

Track a client's parameter changes on the server connection#1299
IgorOhrimenko wants to merge 2 commits into
pgdogdev:mainfrom
IgorOhrimenko:fix-reset-in-transaction

Conversation

@IgorOhrimenko

@IgorOhrimenko IgorOhrimenko commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Problem

A SET or RESET the client sends once a server is attached changes that server's session, but nothing wrote it down. What we did instead was clear the whole parameter cache whenever a CommandComplete said RESET — which trades one problem for another, because that cache is exactly what tells us to undo the change for the next client.

pg_dump -t <table> walks straight into it:

SET search_path TO '';
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY;
RESET search_path;
ROLLBACK;

The ROLLBACK undoes the RESET, so the empty search_path is back on the server, but the cache no longer mentions it. The next client gets that connection and every unqualified name fails with 42P01.

A SET has the same hole in the other direction, and it needs no pg_dump at all:

BEGIN;
SELECT 1;                          -- a server is ours from here on
SET statement_timeout TO '5s';
COMMIT;

On main the next client sees statement_timeout = 5s. Nothing resets it, because the connection's parameter cache never learned about it — link_client only ever recorded what the client had at checkout time.

Fix

Record the change where it happens, instead of reacting to a tag afterwards.

RESET gets the transaction handling SET always had — reset vs reset_transaction, next to insert and insert_transaction — so a rollback restores what it cleared and a commit makes it permanent. That also fixes a smaller thing on the way: an autocommit RESET used to leave its old value in reset_params forever, so an unrelated transaction rolling back later would resurrect it.

The server connection then keeps the same record its client does, and the existing parameter diff does the rest. The next client is handed a precise RESET "search_path" for what it doesn't want, rather than a connection nobody dares reuse.

The CommandComplete fallback stays for RESETs we don't see coming: with the query parser off, that is still all we have.

Testing

  • Parameters: rollback restores an in-transaction reset, a commit makes it permanent, and a reset outside a transaction is not resurrected by a later rollback.
  • Server: a recorded reset survives ROLLBACK and still produces RESET "search_path" for the next client; a committed one produces nothing; a recorded SET is undone for the next client; resetting one parameter no longer forgets the others; an untracked RESET still clears the cache.
  • integration/python/test_session_params_leak.py: the pg_dump sequence, a SET committed after connecting, and a committed RESET. Verified to fail on main (the first two) and pass here.
  • On a live pool with one server connection, the wire shows exactly one RESET "search_path" when the connection is handed over — no RESET ALL, no churn.

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.01878% with 17 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pgdog/src/backend/pool/connection/binding.rs 43.75% 9 Missing ⚠️
pgdog/src/backend/server.rs 95.89% 6 Missing ⚠️
pgdog/src/frontend/client/query_engine/set.rs 83.33% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@IgorOhrimenko

Copy link
Copy Markdown
Contributor Author

Related: #1302 fixes a third way session state survives checkin — a client's SQL-level PREPARE (which pg_dump relies on) stayed on the connection and collided with the next client's statement of the same name.

@IgorOhrimenko
IgorOhrimenko force-pushed the fix-reset-in-transaction branch 2 times, most recently from 014ce39 to 5316acd Compare August 3, 2026 07:27
@levkk

levkk commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

I don't think this is the right solution. We should implement handling RESET in Parameters just like we do with SET inside transactions. Marking a connection "dirty" is a trick of last resort: it causes connection churn and in production would be quite disruptive. We do it when we literally can't do anything else to cleanup the connection state, but in this case, I think we can.

A SET or RESET the client sends once a server is attached changes that
server's session, but we never wrote it down. What we did instead was
clear the whole parameter cache whenever a CommandComplete said RESET,
which trades one problem for another: the cache is what tells us to undo
the change for the next client, and a ROLLBACK undoes the RESET anyway.

pg_dump -t <table> walks straight into it: SET search_path TO '', then
RESET search_path inside its transaction, then ROLLBACK. Postgres brings
the empty search_path back, the cache no longer mentions it, and the next
client gets the connection with unqualified names silently broken.

A SET has the same hole in the other direction: BEGIN, a query, SET
statement_timeout, COMMIT — the setting stays on the server and nothing
resets it for whoever comes next.

So record the change where it happens. RESET now has the transaction
handling SET always had (reset vs reset_transaction), so a rollback
restores what it cleared and a commit makes it permanent, and the server
connection keeps the same record its client does. The existing parameter
diff then does the rest: the next client is handed a precise RESET for
what it doesn't want, instead of a connection nobody dares reuse.

The CommandComplete fallback stays for RESETs we don't see coming — with
the query parser off, that is still all we have.
Runs against a database with a single server connection, so the next
client always gets the connection the previous one used.
@IgorOhrimenko
IgorOhrimenko force-pushed the fix-reset-in-transaction branch from 5316acd to 934edbd Compare August 4, 2026 19:57
@IgorOhrimenko IgorOhrimenko changed the title Reset the session after a RESET Track a client's parameter changes on the server connection Aug 4, 2026
@IgorOhrimenko

Copy link
Copy Markdown
Contributor Author

Reworked along those lines — no dirty flag, the connection keeps track of its parameters.

RESET now has the transaction handling SET always had (reset vs reset_transaction), so a rollback restores what it cleared and a commit makes it permanent. The server connection records the same change its client does, and the existing link_client diff does the rest: the next client is handed a precise RESET "search_path", which is what the wire shows on a one-connection pool.

Digging into it turned up a second hole the same mechanism closes, with no pg_dump involved:

BEGIN;
SELECT 1;                          -- a server is ours from here on
SET statement_timeout TO '5s';
COMMIT;

On main the next client sees statement_timeout = 5s. The cache is only written at checkout, so a SET that lands after that is never undone. There's an integration test for it.

The CommandComplete fallback is still there for RESETs we don't see coming — with the query parser off it's all we have — but it no longer fires when we already know what changed, which is what made it destructive before.

One thing I did not do: an autocommit RESET used to stash its old value forever, so an unrelated transaction rolling back later resurrected it. reset no longer stashes and reset_transaction does, which changes two existing tests to use the transaction variant.

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