Skip to content

fix(jdbc): report SQLite metadata and the JDBC URL - #268

Merged
magnusp merged 4 commits into
mainfrom
fix/261-sqlite-product-name
Oct 31, 2026
Merged

magnusp merged 4 commits into
mainfrom
fix/261-sqlite-product-name

Conversation

@magnusp

@magnusp magnusp commented Oct 31, 2026 •

Copy link
Copy Markdown
Owner

Part 1 of the 4-PR stack (#268 → #269 → #270 → #271). Closes #261 and #274.

What changed

  • DatabaseMetaData.getDatabaseProductName() now returns exactly SQLite. The libSQL identity stays in getDriverName() (libSQL JDBC Driver) and getDatabaseProductVersion().
  • DatabaseMetaData.getURL() now returns the JDBC URL the caller connected with (jdbc:libsql:... or jdbc:sqlite:...) instead of the transport endpoint (http://... / ws://...). Credential query parameters (authToken, jwt, password, token) and user info are redacted.
  • Liquibase now resolves SQLiteDatabase through the normal DatabaseFactory path instead of falling back to UnsupportedDatabase.
  • jOOQ 3.21 derives its dialect from DatabaseMetaData.getURL(). With the URL fix, the jdbc:sqlite: alias resolves to SQLDialect.SQLITE; jdbc:libsql: still returns SQLDialect.DEFAULT because jOOQ has no libsql prefix, so that path needs an explicit dialect.
  • Updated the conformance guard, the WebSocket metadata assertion, and the Liquibase/jOOQ probes to pin the new metadata.

Verification

  • ./mvnw -pl libsql-jdbc -am test
  • ./mvnw -pl libsql-conformance -am -Dtest=LibsqlConformanceGuardTest -Dsurefire.failIfNoSpecifiedTests=false test
  • Targeted JooqIT against the pinned sqld container (libSQL → DEFAULT, SQLite alias → SQLITE)
  • Targeted WebSocketTransportIT against the pinned sqld container
  • ./mvnw spotless:check
  • CI (Java 25, Java 26, Xerial Control) is green.

Liquibase selects its SQLite dialect only when the product name equals
"SQLite", and jOOQ maps the same value to SQLDialect.SQLITE. Keep the
libSQL identity in the driver name and the product version.

Update the conformance guard, the integration assertions, and the
Liquibase and jOOQ probes to pin the exact-match behavior. Closes #261.
jOOQ 3.21 resolves JDBCUtils.dialect(Connection) from
DatabaseMetaData.getURL(), not from the database product name. The driver
reports its transport endpoint, so jOOQ still returns SQLDialect.DEFAULT
for a jdbc:libsql: connection and callers must pass SQLDialect.SQLITE
explicitly. Correct the previous pin, which assumed the product name
drove dialect detection.
Preserve the URL passed to the driver and return it from getURL() instead
of the transport endpoint. URL-based consumers can now see the jdbc:...
prefix: jOOQ detects SQLDialect.SQLITE for a jdbc:sqlite: connection,
while jdbc:libsql: still needs an explicit dialect because jOOQ has no
libsql prefix. Closes #274.
@magnusp magnusp changed the title fix(jdbc): report the database product name as SQLite fix(jdbc): report SQLite metadata and the JDBC URL Oct 31, 2026
DatabaseMetaData.getURL() must not expose auth tokens or user info from
the JDBC URL. Strip credential query parameters and user info before
storing the URL that getURL() reports.
@magnusp
magnusp merged commit bda077b into main Oct 31, 2026
6 checks passed
@magnusp
magnusp deleted the fix/261-sqlite-product-name branch October 31, 2026 02:15
magnusp added a commit that referenced this pull request Oct 31, 2026
Part 2 of the 4-PR stack (#268 → #269 → #270 → #271). Closes #262.
Builds on #261.

## What changed

- Added a Liquibase integration section to `README.md` covering the
three supported bootstraps:
- `DatabaseFactory.openDatabase(...)` with the `jdbc:sqlite:` prefix and
`LibsqlDriver` named explicitly.
- Liquibase Maven plugin / `liquibase.properties` with
`driver=io.github.magnusp.libsql.jdbc.LibsqlDriver`.
  - Spring Boot or a pool with an explicit `DataSource` driver class.
- The `jdbc:sqlite:` prefix is the one Liquibase's SQLite implementation
claims, and the product name is exactly `SQLite` (#261), so Liquibase
selects its SQLite dialect through the normal `DatabaseFactory` path.
The driver maps the alias to sqld.
- Added
`LiquibaseIT#databaseFactoryOpensASqliteAliasUrlWithAnExplicitDriver`,
which uses the eight-argument `openDatabase` overload that names the
driver and asserts it resolves a `SQLiteDatabase`.
- Kept the negative pin that shows why a `jdbc:libsql:` URL cannot
resolve a driver without an explicit class.

## Verification

- Targeted `LiquibaseIT` against the pinned sqld container
- `./mvnw spotless:check`
- CI (Java 25, Java 26, Xerial Control) is green.
magnusp added a commit that referenced this pull request Oct 31, 2026
)

Part 3 of the 4-PR stack (#268 → #269 → #270 → #271). Closes #258.

## What changed

Auto-commit now keeps one lazily opened Hrana stream per JDBC connection
instead of running every statement one-shot.

- `last_insert_rowid()`, `PRAGMA` settings, and other session state
survive between auto-commit statements on the same connection and do not
cross connections.
- A manual transaction begin is tracked explicitly, so an already-open
auto-commit stream cannot make the driver skip `BEGIN`. If that stream
expired while idle, the begin is retried once on a fresh stream.
- `Connection.close()` closes the session stream.
- An expired auto-commit stream is dropped and the statement is retried
once on a fresh stream. Session state set before expiry is gone, and
that reset is documented.
- Added `SqlScript`, a SQLite-aware statement splitter that handles
quoted text, comments, and trigger bodies. Multi-statement scripts run
in order on the same stream, sum update counts, expose the final result
set, and stop at the first failure.
- Multi-statement input with parameters is rejected with
`SQLFeatureNotSupportedException`.
- Updated the Flyway, Hibernate, jOOQ, and conformance probes:
  - The two xerial multi-statement cases are un-gated.
  - Hibernate runs with auto-commit at its default.
  - jOOQ auto-commit identity reads expect the generated value.
- Flyway `clean()` runs when `initSql` disables foreign keys on the
connection. Flyway wraps clean in a transaction, and SQLite ignores
`PRAGMA foreign_keys` inside one, so the pragma has to run at connection
initialization.
- Replaced the “auto-commit holds no stream” section in the conformance
README with the connection-scoped stream contract.

## Verification

- `./mvnw -pl libsql-jdbc -am test`
- Targeted `JooqIT`, `LiquibaseIT`, `FlywayKnownGapsIT`,
`AutoCommitSessionIT`, and `TransactionLifecycleIT` against the pinned
sqld container
- `./mvnw spotless:check`
- `./tools/export-upstream --verify` and `--self-test`
- CI (Java 25, Java 26, Xerial Control) is green.
magnusp added a commit that referenced this pull request Oct 31, 2026
Part 4 of the 4-PR stack (#268 → #269 → #270 → #271). Closes #263.
Builds on #258.

## What changed

- Added a Flyway integration section to `README.md` covering:
- A `DataSource` with either `jdbc:libsql:` or `jdbc:sqlite:` (the
recommended path).
- URL-based configuration with the `jdbc:sqlite:` alias and an explicit
`LibsqlDriver` class, for the Maven plugin or `flyway.properties`.
- Why `jdbc:libsql:` cannot be used with URL-based configuration at all.
- Documented the session-state behavior after #258: auto-commit
statements on one connection share a stream, so `initSql` /
`afterConnect` PRAGMAs apply to later migrations until the idle stream
expires.
- Documented the one Flyway `clean()` requirement: set `PRAGMA
foreign_keys = OFF` through `initSql` (or `afterConnect`), because
Flyway wraps clean in a transaction and SQLite ignores that pragma
inside a transaction. The driver's multi-statement splitting then runs
Flyway's PRAGMA/DROP scripts on one stream.

The two negative pins for URL-based plugin/driver resolution stay in
`FlywayKnownGapsIT`.

## Verification

- `./mvnw -pl libsql-jdbc -am test`
- `./mvnw spotless:check`
- CI (Java 25, Java 26, Xerial Control) is green.
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.

Report database product name as exactly SQLite so dialect detection works

1 participant