Skip to content

fix(jdbc): close the product-version statement (#92) - #128

Merged
magnusp merged 1 commit into
mainfrom
fix/i92-product-version-statement
Oct 22, 2026
Merged

magnusp merged 1 commit into
mainfrom
fix/i92-product-version-statement

Conversation

@magnusp

@magnusp magnusp commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Summary

LibsqlDatabaseMetaData.resolveDatabaseProductVersion() ran connection.createStatement().executeQuery(...) in a try-with-resources that wrapped only the result set. The statement stayed open after every uncached version lookup. The statement and its result set now open in one try-with-resources, so both close, the result set first.

The new leak-count case counts statements created through the test connection. The second case pins the cache, so a future uncached second lookup shows up as a second statement.

Contract basis. Hrana 3 is the source of truth for transport, values, and metadata, java.sql is the source of truth for the JDBC surface, and xerial/sqlite-jdbc is a behavioral reference only. This change is protocol-neutral. The product version still comes from select sqlite_version() through execute, and JDBC owns statement cleanup.

Red and green

Red at base 8754013 with only the new tests applied. The leak-count case fails for the intended reason. The cache case passes at base, because caching already exists; it is a guard, not a red case.

[ERROR] Tests run: 25, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.115 s <<< FAILURE! -- in com.github.magnusp.libsql.jdbc.LibsqlDatabaseMetaDataTest
[ERROR]   LibsqlDatabaseMetaDataTest.getDatabaseProductVersionClosesItsStatement:51 [the product-version lookup must close its statement]
[INFO] BUILD FAILURE

Green at head 0b33b7a.

[INFO] Tests run: 25, Failures: 0, Errors: 0, Skipped: 0 -- in com.github.magnusp.libsql.jdbc.LibsqlDatabaseMetaDataTest
[INFO] BUILD SUCCESS

Cache observation

getDatabaseProductVersion() twice through one metadata instance creates one statement. The first call opens, reads, and closes it. The second call reads cachedDatabaseProductVersion and opens nothing. getDatabaseProductVersionOpensOneStatementAcrossTwoCalls asserts the count stays at one.

Verification

Base 8754013c90b0299f7e87147702663eaa9646c12, head 0b33b7a93b292a9159bba1086a9078a34be80633.

Lane Command Result
1. Regression ./mvnw -pl libsql-jdbc test -Dtest=LibsqlDatabaseMetaDataTest at base and head Base 1 failure for the intended reason, head 25 pass
2. Cached-version cases ./mvnw -pl libsql-jdbc test -Dtest='LibsqlDatabaseMetaDataTest#getDatabaseProductVersion*' 2 pass
3. Metadata cache cases ./mvnw -pl libsql-jdbc test -Dtest=LibsqlDatabaseMetaDataTest 25 pass
4. Module suite ./mvnw -pl libsql-jdbc test 223 pass, BUILD SUCCESS
5. Conformance ./mvnw -pl libsql-conformance -am verify 115 pass, 1 skipped, BUILD SUCCESS, no new divergence
6. xerial control ./mvnw -pl libsql-conformance -am -Pxerial-control verify 100 pass, 1 skipped, BUILD SUCCESS, no differences to record
7. Format ./mvnw spotless:check BUILD SUCCESS, no violations
8. Reactor ./mvnw test 223 pass, BUILD SUCCESS
9. Vendored leak test ./mvnw -pl libsql-conformance -am verify -Dit.test=MetadataLeakTest 1 pass
10. Package ./mvnw -pl libsql-jdbc -am package -DskipTests BUILD SUCCESS
Perf ./mvnw -pl libsql-jdbc test three times per side, interleaved Base median 2211 ms, head median 2247 ms, +1.6 percent, within the 10 percent rule

Divergences and scope

No protocol divergence. The protocol has no statement lifecycle field, so JDBC defines it.

Caching semantics are unchanged. Issue #112 owns the failed-lookup cache and the closed-connection behavior. This PR changes statement ownership only.

Files

libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlDatabaseMetaData.java, libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/LibsqlDatabaseMetaDataTest.java.

Fixes #92

resolveDatabaseProductVersion wrapped only the result set in
try-with-resources, so the statement stayed open after the version was
read. Wrap the statement too, and pin the leak with a statement-count
test that also confirms the cached second call opens nothing.
@magnusp
magnusp merged commit 8e9aaf6 into main Oct 22, 2026
5 checks passed
@magnusp
magnusp deleted the fix/i92-product-version-statement branch October 22, 2026 21:28
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.

resolveDatabaseProductVersion leaks its Statement

1 participant