fix: a compiled query can be bound to a transaction - #194
Merged
Merged
Conversation
A compiled query captures the instance it was built from, which is what lets
it run itself. Inside a transaction that is the wrong instance — the
transaction checks out its own connection, so a query compiled outside sends
its statement down the pool and commits while the transaction around it
rolls back:
client (the transaction): ["BEGIN", "ROLLBACK"]
pool (outside it): ["INSERT INTO users …"] ← committed
pglite cannot show this: it has one connection, so the write appears to
participate and the rollback appears to take it back. It took a recording
pool to see, and it gets more likely the more the compiled path is used —
which this branch has been making the default.
`tx.prepared(query)` binds a query compiled elsewhere to that instance,
compiling the SQL again once rather than per call and leaving the original
alone. The test pins the trap as well as the fix, because a test that only
covers the correct usage would pass just as happily if the trap came back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HAwiBVhLmNhWjC9Ro6UMpb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A hazard the previous two PRs made more likely, found by asking what a compiled query does inside a transaction.
The trap
A compiled query captures the instance it was built from — that is what lets it run itself. A transaction checks out its own connection, so a query compiled outside one sends its statement down the pool:
The write commits while the transaction around it rolls back.
pglite cannot show this. It has one connection, so the write appears to participate and the rollback appears to take it back — the probe against pglite came back clean and the bug was still there. It took a recording pool to see.
This gets more likely the more the compiled path is used, which is exactly what #192 and #193 have been encouraging.
The fix
tx.prepared(...)compiles the SQL again for that instance — once, not per call — and leaves the original untouched.The tests pin the trap as well as the fix. A test that only covered the correct usage would pass just as happily if the trap came back.
pnpm testgreen: 3315 tests, lint clean, types clean.