Query resolved prematurely on CommandComplete in implicit transactions, ignoring subsequent ErrorResponse
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 8.7k
- Forks
- 374
- Avg merge
- 11d 16h
- Merged PRs (30d)
- 1
Description
Description
postgres.js resolves query promises immediately upon receiving a CommandComplete message, but this can cause issues in implicit transactions where validation errors occur during the sync phase. According to the PostgreSQL protocol, implicit transactions are only committed when the server receives a Sync message, meaning errors can still occur after CommandComplete but before ReadyForQuery.
Current Behavior
In the extended query protocol with implicit transactions:
- Execute → Server buffers DML and sends CommandComplete
- Sync → Server attempts to commit the implicit transaction
- If validation fails during commit → ErrorResponse sent
- ReadyForQuery → Protocol cycle completes
postgres.js resolves the query promise at step 1, ignoring any errors that occur during the actual transaction commit in steps 2-3.
Expected Behavior
For implicit transactions, the query should not be resolved until the Sync/commit phase completes successfully. If validation fails during commit, the query should be rejected with the appropriate error.
In the extended query protocol:
- Explicit transactions: CommandComplete can safely indicate success since the transaction isn't committed until an explicit COMMIT
- Implicit transactions: CommandComplete only means the command was buffered; the transaction commits during Sync, where validation errors can still occur
Reproduction
This occurs when connecting to databases that:
- Use implicit transactions for single DML statements
- Send CommandComplete immediately after buffering operations
- Perform validation during the sync/commit phase
- Send ErrorResponse after CommandComplete if validation fails during commit
Example Protocol Trace
→ Parse: "INSERT INTO ..."
← ParseComplete
→ Bind
← BindComplete
→ Execute
← CommandComplete ← Query resolved here (but transaction not committed yet!)
→ Sync ← Implicit transaction commits here
← ErrorResponse ← Validation error during commit (ignored - promise already resolved)
← ReadyForQuery
Potential Root Cause and Fix?
In src/connection.js, the CommandComplete() function calls query.resolve(result) immediately, regardless of whether we're in an implicit transaction that hasn't committed yet:
function CommandComplete(x) {
// ... parsing logic ...
query.resolve(result) // ← Promise resolved before implicit transaction commits
}
For implicit transactions in extended query protocol, delay query resolution until ReadyForQuery is received, which indicates the implicit transaction has been successfully committed. The fix should:
- Detect when we're in an implicit transaction
- Store the result from CommandComplete but don't resolve yet
- Only resolve with the stored result if ReadyForQuery is received without an intervening error
- If ErrorResponse occurs, reject the query instead
Impact
This affects any PostgreSQL-compatible database that performs validation during the implicit transaction commit phase rather than during command preparation, which is a valid implementation choice.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/connection.js by tracing CommandComplete, ErrorResponse, and ReadyForQuery handling, with attention to how implicit and explicit transactions are distinguished. Done means a CommandComplete result is retained for implicit transactions, successful ReadyForQuery resolves it, and an intervening ErrorResponse rejects it instead.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, postgresql
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100