Repository navigation
Conversation
When a statement fails inside a transaction and the error is caught without rolling back to a savepoint, PostgreSQL answers the following COMMIT with the ROLLBACK command tag and discards the transaction. Transaction.commit() and `async with con.transaction()` ignored the tag, so they returned normally and marked the transaction as committed even though its changes were lost. Check the command tag and raise InFailedSQLTransactionError (25P02) in that case, and mark the transaction as rolled back. Fixes MagicStack#1400.
This branch has not been deployed
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.
Fixes #1400.
When a statement fails inside a transaction and the application catches the error without rolling back to a savepoint, PostgreSQL answers the later
COMMITwith the command tagROLLBACKand discards the transaction.Transaction.commit()ignored the tag, so bothawait tr.commit()andasync with con.transaction():returned normally and the transaction was marked as committed, although its changes were lost. A latertr.rollback()then failed with "the transaction is already committed".__commit()now checks the command tag of a top-levelCOMMIT. If it isROLLBACK, the transaction is marked as rolled back andInFailedSQLTransactionError(SQLSTATE 25P02) is raised, which is the error PostgreSQL itself uses for statements sent to an aborted transaction. pgjdbc reports this case with the same SQLSTATE. Savepoints are not affected:RELEASE SAVEPOINTin an aborted transaction already raises on the server.This changes behaviour for code that catches an error inside a transaction and still expects the block to commit. That code was already losing its writes silently, so it now gets an exception instead.
Testing
test_transaction_commit_rolled_back_by_servercovers both the explicitcommit()and theasync withblock, and checks that the rows are gone and the transaction state isrolledback. It fails without the change (InFailedSQLTransactionError not raised) and passes with it. I rantest_transaction,test_pool,test_executeandtest_cursoragainst PostgreSQL 16 on Windows; all pass.