Skip to content

refactor(jdbc): name savepoint state transitions - #231

Merged
magnusp merged 1 commit into
mainfrom
refactor/i210-savepoint-transitions
Oct 24, 2026
Merged

magnusp merged 1 commit into
mainfrom
refactor/i210-savepoint-transitions

Conversation

@magnusp

@magnusp magnusp commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Fixes #210

Names the savepoint state transitions in LibsqlConnection. The three fields
(autoCommit, transactionStartedBySavepoint, savepointNames) still encode
one state machine, but each transition now has a name and one caller.

What changed

  • beginSavepointTransaction — the savepoint sent in auto-commit opens the
    stream and owns the transaction; no BEGIN is sent, and a failed savepoint
    closes the fresh stream and leaves auto-commit on.
  • discardSavepointsAfter — a rollback cancels the savepoints created after
    the target; the target stays active.
  • closeTransactionOwnedBySavepoint — a release drops the released savepoint
    and its nested ones, and closes the stream only when that savepoint is the
    outermost one that started the transaction.
  • requireManualCommit(String action) — builds Cannot <action> when in autoCommit mode, so commit, rollback, rollback-to-savepoint, and
    release-savepoint each keep their distinct wording.
  • savepointSqlName moved to LibsqlSavepoint.sqlNameOf. Same messages, and
    the savepoint type now owns savepoint naming while the connection keeps the
    transitions.
  • beginTransaction folded into its only caller ensureTransactionStarted,
    and the redundant second lock acquisition around the setSavepoint(String)
    name guard dropped. Both are behavior-preserving.

Preserved

  • A savepoint in auto-commit starts a transaction without BEGIN; releasing
    the outermost savepoint commits and closes the stream.
  • A failed implicit savepoint leaves auto-commit on and closes the fresh stream.
  • Rollback and release in auto-commit send nothing and name the mode.
  • setSavepoint(null) throws; setSavepoint() does not.
  • Duplicate names resolve by lastIndexOf; the list is kept.
  • Request order, messages, and lock discipline (stateLock held by every
    transition) unchanged. No test file and no pom.xml change.

Diff

 libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlConnection.java | 166 ++++++++++-----------
 libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlSavepoint.java  |  27 ++++
 2 files changed, 103 insertions(+), 90 deletions(-)

Line counts: LibsqlConnection.java 748 → 734 (under the 740 budget);
LibsqlSavepoint.java 43 → 70.

Probe

Switched lastIndexOf to indexOf in closeTransactionOwnedBySavepoint
(the duplicate-name resolution), one line, reverted:

[ERROR] Tests run: 17, Failures: 1, Errors: 0, Skipped: 0
[ERROR] com.github.magnusp.libsql.jdbc.LibsqlSavepointTest.releasingTheLaterOfTwoSavepointsWithTheSameNameKeepsTheEarlierOneActive:225
Expecting actual:
  ...
but some elements were not expected: "BEGIN", (Close)

The later duplicate then looked outermost, so the release committed and closed
the stream and the next statement re-began. Reverted; LibsqlSavepointTest
17/17 green again.

Verification

Lane Command Result
unit ./mvnw -pl libsql-jdbc -am test 398 tests, 0 failures, 0 errors
spotless ./mvnw spotless:check clean
conformance ./mvnw -pl libsql-conformance -am verify 272 tests, 0 failures, 7 skipped
itests DOCKER_CONFIG=$PWD/libsql-conformance/src/test/docker-config ./mvnw -pl libsql-itests -am verify 43 tests, 0 failures, 0 errors

Fixes #210

Names the three savepoint state transitions in `LibsqlConnection` and
moves savepoint-name resolution next to the type that owns it.

- `beginSavepointTransaction` starts the transaction an auto-commit
  savepoint owns without sending `BEGIN`; a failed savepoint closes the
  fresh stream and leaves auto-commit on.
- `discardSavepointsAfter` cancels the savepoints created after a rollback
  target, which stays active.
- `closeTransactionOwnedBySavepoint` drops the released savepoint and its
  nested ones, and closes the stream only when the released savepoint is
  the outermost one that started the transaction.
- `requireManualCommit(String action)` builds `Cannot <action> when in
  autoCommit mode`, so commit, rollback, and savepoint rollback/release
  keep their distinct messages.
- `savepointSqlName` moved to `LibsqlSavepoint.sqlNameOf`; messages
  unchanged.
- `beginTransaction` folded into its only caller,
  `ensureTransactionStarted`, and the redundant second lock in
  `setSavepoint(String)` dropped.

Preserved: auto-commit savepoint opens the stream and its outermost
release commits and closes it; failed implicit savepoint leaves
auto-commit on; rollback/release in auto-commit send nothing; duplicate
names resolve by `lastIndexOf`. No test or `pom.xml` change.
@magnusp
magnusp merged commit 9c59f26 into main Oct 24, 2026
6 checks passed
@magnusp
magnusp deleted the refactor/i210-savepoint-transitions branch October 24, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Name the savepoint transitions in LibsqlConnection

1 participant