Savepoints - #1666
Savepoints#1666doxlik wants to merge 13 commits into
Conversation
|
Can you please rebase the PR? The unrelated CI failures should be fixed now |
|
@tsegismont rebasing done! CI still have some failure but seems like it couldn't build in action with Postgres14, not any test problem. |
|
The CI has been stabilized hopefully, would you mind rebasing again? Thank you |
|
@tsegismont Hi, there were no new commits since the last rebase, so I amended my latest commit to trigger a push. It seems CI is passing now. Looking forward to your review! |
|
@vietj @tsegismont Hi. I added support for other DBs. Please have a look once you have time. |
Signed-off-by: doxlik <doxlikx@gmail.com>
Signed-off-by: doxlik <doxlikx@gmail.com>
Signed-off-by: doxlik <doxlikx@gmail.com>
Savepoints were implemented for PostgreSQL only. The command itself is plain SQL, so the remaining drivers mostly need to route SavepointCommand to a simple query, but they do not all agree on the syntax and two of them cannot release a savepoint at all. Driver gains supportsSavepointRelease(). Microsoft SQL Server and Oracle create savepoints but have no statement that discards one, so releasing on those drivers fails with an UnsupportedOperationException rather than pretending to succeed, and the savepoint stays usable for a rollback. MySQL uses the standard syntax. DB2 requires the mandatory ON ROLLBACK RETAIN CURSORS clause when a savepoint is created. Transact-SQL names the statements SAVE TRANSACTION and ROLLBACK TRANSACTION. Oracle runs the statement on its JDBC connection through a new OracleSavepointCommand. Generated savepoint names are now VX_SP_<n>: an unquoted Oracle identifier cannot start with an underscore, so the previous __vx_sp_<n> was invalid there. The behaviour every database agrees on moved to TransactionTestBase, gated on the two capabilities, so each driver inherits it. What the databases do not agree on stays in the driver tests: PostgreSQL fails the whole transaction when a statement fails, whereas MySQL leaves the transaction usable, and MySQLTransactionTest now covers that difference. Verified against PostgreSQL, MySQL and Oracle. SQL Server and DB2 could not be started locally, both for reasons unrelated to this change. Signed-off-by: doxlik <doxlikx@gmail.com>
Removing the duplicated helpers from PgTransactionTest left three imports behind, and MySQLTransactionTest imported Future without using it, both of which fail spotless:check. SQL Server drops a savepoint once the transaction has been rolled back to it and answers a second rollback with "No transaction or savepoint of that name was found", so rolling back twice to the same savepoint is now a capability the drivers report, and SQL Server opts out. Signed-off-by: doxlik <doxlikx@gmail.com>
Whether a failed statement also fails the surrounding transaction was only covered for PostgreSQL and MySQL, and only MySQL stated it. It is now a capability the drivers report, statementErrorFailsTransaction(), with a test for each answer and a third test showing that rolling back to a savepoint recovers the transaction either way. The drivers that had no savepoint test of their own get one for the part that is specific to them: SQL Server that a savepoint does not nest the transaction, so @@TRANCOUNT stays at one across SAVE TRANSACTION and the rollback; DB2 that a cursor opened before the savepoint survives the rollback, which is what ON ROLLBACK RETAIN CURSORS is for; Oracle that a savepoint leaves the autocommit handling around commit and rollback alone. Signed-off-by: doxlik <doxlikx@gmail.com>
The connector hands out a pooled connection that is released when the transaction ends, so closing it again failed the test with "Connection released twice". Acquire the connection from the pool instead, which is what proves it went back, and assert on the rows the transaction committed. Signed-off-by: doxlik <doxlikx@gmail.com>
7db0751 to
28500fd
Compare
tsegismont
left a comment
There was a problem hiding this comment.
Thank you @doxlik
Overall, this looks good to me, but we're missing documentation for this feature.
The transactions documentation now has a savepoint section covering rollback, release and the databases that cannot release a savepoint, with an example for each of the two operations in every client module. SavepointImpl carried a private functional interface only to defer the two transaction calls. A BiFunction does the same, so rollback and release are method references now. Signed-off-by: doxlik <doxlikx@gmail.com>
The transactions documentation is included by every client, so the section describing what PostgreSQL, SQL Server and Oracle do would have appeared on all of the pages. The shared section only states what the API does now, and each client contributes its own savepoint_note.adoc, the way cursor.adoc already lets MySQL add its ProxySQL warning. Signed-off-by: doxlik <doxlikx@gmail.com>
createSavepoint(String) creates a savepoint under a name the application chooses, which is what shows up when the database mentions the savepoint in an error message. The no-argument method keeps generating VX_SP_<n>. The name is written to the statement as an unquoted identifier. Quoting it would have to be done per database and would make the name case sensitive, so a name is instead restricted to what all the supported databases accept unquoted: a letter followed by letters, digits or underscores. Anything else is rejected with an IllegalArgumentException before the statement is built, leaving the transaction untouched. Signed-off-by: doxlik <doxlikx@gmail.com>
|
@tsegismont thank you for review. I added docs, refactor to BiFunction and added API for user supplied savepoint name, can you please have a look! |
A savepoint name was restricted to a letter followed by letters, digits or underscores so it could be written to the statement unquoted. The driver already knows its dialect, it decides how to write a parameter placeholder, so let it write a delimited identifier too: appendQuotedIdentifier uses the standard double quote by default, MySQL overrides it with backticks and Transact-SQL with square brackets, each escaping its own delimiter. The name is quoted once when the savepoint is created and the rollback and release statements reuse it. What a name may hold is no longer restricted, only its length: SQL Server keeps the first 32 characters of a savepoint name, so longer names would silently collide there. Signed-off-by: doxlik <doxlikx@gmail.com>
Oracle rejects a savepoint identifier that holds a double quote even when it is escaped, ORA-25716, while the other databases accept it. A name that works on one database should work on all of them, so the double quote is refused everywhere rather than on Oracle alone. Signed-off-by: doxlik <doxlikx@gmail.com>
The comment claimed length was the only restriction, which stopped being true when the double quote was refused. Signed-off-by: doxlik <doxlikx@gmail.com>
|
@tsegismont done, can you please check |
This PR adds API for Savepoints and implementation of it for Postgres client.
Closes #1369