fix(plugin-oracle): commit Oracle statements the way every other engine does - #3001
Merged
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
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.
What was wrong
Oracle has no autocommit of its own. Every write opens a transaction that stays open until something commits it, and oracle-nio sends no commit unless the statement asks for one (
StatementOptions.autoCommit, defaultfalse,OracleTask.swift:268at the pinnedf09d088). TablePro never asked. So on Oracle nothing committed unless the app happened to wrap the work in its own transaction and send aCOMMIT:Cmd+Enter)execute_queryCOMMIT, together with any earlier uncommitted statementCOMMITfrom the PluginKit default)executeStreaming)executeWrite)WriteTransactionPolicyopens no transaction for one statement when the state is.unknownAn uncommitted write is invisible to every other session, keeps its row locks, and is decided later by whatever runs next on that session: a later Run All or grid save commits it silently, a failed Run All's
ROLLBACKthrows it away, a graceful disconnect commits it, and a dropped network loses it. Neither app has a Commit button or a transaction indicator, and every other engine in TablePro commits each statement as it runs.Measured (Oracle AI Database 26ai Free 23.26, pinned oracle-nio
f09d088)Before, through
OracleCoreConnectionexactly as the plugin and the iOS driver call it, with a second session reading:streamQuery("INSERT …")(iOS editor path): session A counts 1, session B counts 0.executeQuery("INSERT …")(macOS single statement, MCP, iOSexecute): A counts 2, B counts 0.SELECT … FOR UPDATE NOWAITon a pending row cannot see it at all.ROLLBACK): A's count went from 2 to 0, the two earlier single-statement inserts gone.COMMITalso committed an earlier single-statement insert (B 0 to 2).oracle-nio's commit flag, raw:
autoCommit: trueon anINSERTis visible to another session at once; on a PL/SQL block that inserts 1000 rows, the same; a streamedUPDATEstill reports its count (3).SELECTof 1001 rows withautoCommit: true: fine.SELECT … FOR UPDATEof 1001 rows withautoCommit: true:ORA-01002: fetch out of sequence, because the commit ends the transaction the cursor belongs to.SAVEPOINTwithautoCommit: truethenROLLBACK TO:ORA-01086.SET TRANSACTION READ ONLYwithautoCommit: true:DBMS_TRANSACTION.LOCAL_TRANSACTION_IDis NULL right after, so the transaction is gone.SET TRANSACTION,SAVEPOINTalone, andLOCK TABLEeach leaveLOCAL_TRANSACTION_IDset;ROLLBACK TOkeeps it;COMMIT WORKandROLLBACK WORKclear it; a failed DML with the flag leaves it NULL;CREATE TABLEcommits the open transaction.ALTER SESSION SET CURRENT_SCHEMAhangs on this build with and without the flag (2 of 2 each, 10 s cap). That is thenioPR's defect and the flag does not change it.What changed
One owner, in the package both apps compile:
TableProOracleCore.OracleSessionTransactiondecides per statement whether oracle-nio's commit flag is sent. Outside a transaction every statement carries it and the server commits in the same round trip, as every other engine in the app behaves. A query never carries it, because the flag breaks a multi-fetchSELECT … FOR UPDATE.OracleCoreConnection.beginTransaction(), which is whatbeginTransaction()on both drivers now calls (it was an empty body on both), or with a statement that only means something inside one:SET TRANSACTION,SAVEPOINT,LOCK TABLE(OracleTransactionRole). It ends withCOMMITor a fullROLLBACK, the app's or the user's, socommitTransaction()androllbackTransaction()keep sending the plain statements.transactionLosterror and runs nothing, instead of carrying on in a fresh session holding none of the earlier work and letting the app'sCOMMITreport the half it saw as the whole.sessionTransactionState()from it (.inTransaction/.explicitTransactionor.idle), so a batch, a grid save or an iOS row edit joins a transaction the user or an MCP client opened instead of committing it, as they already do on PostgreSQL and MySQL.BatchTransactionPolicy:SET TRANSACTIONin an Oracle script makes it a script that manages its own transaction, the wayBEGINdoes elsewhere (TransactionEngineFamily.oracle,setTransactionOpensTransaction).savepointOpensTransactionnow includes Oracle, measured.No PluginKit change.
sessionTransactionState()already exists in the pending kit 33 with a default, and the Oracle plugin is already stamped 33.Tests
OracleTransactionRoleTests,OracleSessionTransactionTests(package,swift test): which statements carry the flag, open, end, and when a replaced connection reports the transaction lost.TransactionEngineFamilyTests,BatchTransactionPolicyTests,AutocommitOnlyStatementTests: the Oracle family andSET TRANSACTIONas script transaction control.OracleDriverTransactionStateTests(iOS, no server) and three newOracleDriverTestsintegration cases (iOS, skipped withoutORACLE_TEST_HOST): an editor write and a row edit visible to a second session, an opened transaction held until rollback.scripts/check-oracle-autocommit.sh: 15 rules against a live server throughOracleCoreConnection, read from a second session. All 15 pass on 23ai. With the commit flag turned off in a copy of the package, 7 fail, so it catches the bug it exists for.No UI automation: the flow needs a live Oracle server, which the UI test runner does not have.
Not done
close()sends a logoff, and Oracle commits on logoff. A query timeout or iOS Stop inside an app transaction closes the connection that way, so the earlier statements of a failing batch are committed while the app reports a rollback. Measured on the timeout path. Fixing it means rolling back before the logoff inside oracle-nio's close path; that belongs with thenioPR.SET TRANSACTIONsemantics were not measured.