Repository navigation
JDBC driver production readiness #54
Description
Goal
Track the work needed before this driver can serve production JDBC workloads. The goal is a driver whose data conversions, metadata, transaction behavior, and conformance evidence are trustworthy enough to replace sqlite-jdbc in an application without silent behavior changes. Each item below is independent.
Value conversion
- #31 Numeric getters do not follow SQLite text and blob coercion
- #32 getBoolean uses custom truthiness instead of numeric conversion
- #33 REAL values stringify with Double.toString instead of SQLite canonical text
- #34 getObject(Class) ignores storage type for numeric targets
ResultSetMetaData
- #35 getColumnType ignores runtime storage class
- #36 getColumnClassName is wrong for empty and mixed-type result sets
- #37 Table metadata fails for aliases, joins, subqueries, RETURNING, and views
- #38 isAutoIncrement false-positives on identifiers containing AUTOINCREMENT
- #46 Scalar metadata flags and type-name parsing diverge
Statement and transactions
- #29 PreparedStatement executes SQL before validating result kind
- #30 rollback() swallows failures and reports success
- #39 getGeneratedKeys is unimplemented
- #40 Transaction begin mode and commit/rollback lifecycle differ from sqlite-jdbc
- #45 ResultSet navigation differs on duplicate labels and cursor state
- #47 Statement lifecycle and getMoreResults argument handling differ
- #48 TypeMap, fetch size, and holdability are silently accepted no-ops
- #49 Concurrent close and execute can orphan a result set
DatabaseMetaData
- #41 DatabaseMetaData.getTables ignores the types filter and returns non-table rows
- #42 DatabaseMetaData.getColumns reports wrong types and sizes
- #50 getTableTypes is incomplete and several metadata methods leak statements
Conformance setup
- #43 Conformance file URLs do not persist across close and reopen
- #44 Conformance suite does not verify it is connected to libSQL
- #51 Conformance tests include weak assertions and an undocumented selection
- #52 Namespace cleanup ignores failures and can leak or reuse state
Vendoring and CI
- #53 Vendoring checksums and manifest are not enforced
Definition of done
- All P0 and P1 issues are closed. The Conformance manifest still rejects tests for reasons that later PRs removed #116 re-triage filed DBMetaDataTest exposes DatabaseMetaData gaps beyond the #41/#42/#50 fixes #156, PrepStmtTest exposes prepared-statement binding and metadata gaps #157, and StatementTest exposes statement lifecycle and conversion gaps #158.
- The conformance suite proves driver identity and file persistence.
- A control run against xerial at the pinned tag passes.
Sequencing
Advisory schedule for parallel execution. Each subissue carries a Steering section with its lane, dependencies, corrections, and evidence rules. The dependency graph is authoritative; the waves below follow from it.
Rules for every lane
- Contract. Match xerial 3.53.4.0 at the pinned tag, vendored under
upstream-xerial_sqlite-jdbc/. The goal is a drop-in replacement, so parity is the default answer to every "which contract wins" question in these issues. Deviations only where the Hrana transport or sqld makes parity impossible, recorded with their reason in the PR body. - File boundaries. One agent per lane at a time. Lanes own disjoint files. Cross-lane edits are limited to additive, read-only accessors (the one known case is noted in getObject(Class) ignores storage type for numeric targets #34 and getColumnType ignores runtime storage class #35).
- Evidence. Every fix lands with tests that fail on the current code and pass after it. Behavior tests, not implementation tests.
- Priority. P0 first, then P1, then P2. All P0 and P1 items land by wave 4.
- PR shape. One PR per issue, except getTableTypes is incomplete and several metadata methods leak statements #50, which delivers four sequenced PRs.
Lanes by file ownership
- Lane A
LibsqlResultSet.java: Numeric getters do not follow SQLite text and blob coercion #31, getBoolean uses custom truthiness instead of numeric conversion #32, REAL values stringify with Double.toString instead of SQLite canonical text #33, getObject(Class) ignores storage type for numeric targets #34, ResultSet navigation differs on duplicate labels and cursor state #45 - Lane B
LibsqlResultSetMetaData.java,SqlTableReference.java: isAutoIncrement false-positives on identifiers containing AUTOINCREMENT #38, getColumnType ignores runtime storage class #35, getColumnClassName is wrong for empty and mixed-type result sets #36, Table metadata fails for aliases, joins, subqueries, RETURNING, and views #37, Scalar metadata flags and type-name parsing diverge #46 - Lane C
LibsqlStatement.java,LibsqlPreparedStatement.java: PreparedStatement executes SQL before validating result kind #29, getGeneratedKeys is unimplemented #39, Statement lifecycle and getMoreResults argument handling differ #47, Concurrent close and execute can orphan a result set #49 - Lane D
LibsqlConnection.java: rollback() swallows failures and reports success #30, Transaction begin mode and commit/rollback lifecycle differ from sqlite-jdbc #40 - Lane E
LibsqlDatabaseMetaData.java: DatabaseMetaData.getTables ignores the types filter and returns non-table rows #41, DatabaseMetaData.getColumns reports wrong types and sizes #42, getTableTypes is incomplete and several metadata methods leak statements #50 (starts after getGeneratedKeys is unimplemented #39 merges) - Lane F conformance harness and vendoring, test-only: Conformance suite does not verify it is connected to libSQL #44 and Vendoring checksums and manifest are not enforced #53 in parallel, then Namespace cleanup ignores failures and can leak or reuse state #52, Conformance file URLs do not persist across close and reopen #43, Conformance tests include weak assertions and an undocumented selection #51
- Cross-cutting: TypeMap, fetch size, and holdability are silently accepted no-ops #48 runs last, after every file-owning lane completes
Waves
- Wave 1: PreparedStatement executes SQL before validating result kind #29, rollback() swallows failures and reports success #30, Numeric getters do not follow SQLite text and blob coercion #31, isAutoIncrement false-positives on identifiers containing AUTOINCREMENT #38, Conformance suite does not verify it is connected to libSQL #44, Vendoring checksums and manifest are not enforced #53
- Wave 2: getBoolean uses custom truthiness instead of numeric conversion #32, getColumnType ignores runtime storage class #35, getGeneratedKeys is unimplemented #39, Transaction begin mode and commit/rollback lifecycle differ from sqlite-jdbc #40, Namespace cleanup ignores failures and can leak or reuse state #52
- Wave 3: REAL values stringify with Double.toString instead of SQLite canonical text #33, getColumnClassName is wrong for empty and mixed-type result sets #36, Statement lifecycle and getMoreResults argument handling differ #47, DatabaseMetaData.getTables ignores the types filter and returns non-table rows #41, Conformance file URLs do not persist across close and reopen #43
- Wave 4: getObject(Class) ignores storage type for numeric targets #34, Table metadata fails for aliases, joins, subqueries, RETURNING, and views #37, Concurrent close and execute can orphan a result set #49, DatabaseMetaData.getColumns reports wrong types and sizes #42, Conformance tests include weak assertions and an undocumented selection #51
- Wave 5: ResultSet navigation differs on duplicate labels and cursor state #45, Scalar metadata flags and type-name parsing diverge #46, getTableTypes is incomplete and several metadata methods leak statements #50
- Wave 6: TypeMap, fetch size, and holdability are silently accepted no-ops #48
Corrections folded into steering
The #28 review corrections are carried in the steering sections of #32 (misstated libSQL probe results), #40 (scope limited to begin timing, mode, and re-begin; reproduction step 1 fixed), #45 (getRow behavior corrected; remaining xerial claims need re-probing), and #50 (four bundled defects delivered as four PRs).
Status after the #163 selection
All sub-issues are closed. The vendored selection now covers 13 xerial test classes plus Utils, and the control profile passes at the pinned tag with the same selection. The libSQL conformance lane runs 268 cases; the xerial control lane runs 245.
DBMetaDataTest, StatementTest, and PrepStmtTest are selected. Their driver gaps closed in #156, #158, and #157; each conformance copy records the cases Hrana or sqld cannot supply:
DBMetaDataTest:CREATE TEMP TABLEis skipped on libSQL because sqld rejects it withSQL_PARSE_ERROR.StatementTest: multi-statement strings, TEMP tables, and DROP while a materialized result set is open are skipped or asserted per driver; the DATE-millis round trip is compared as a date-only value.PrepStmtTest: the no-describe cases (parameter types, pre-execution metadata, pre-execution SQL validation) assert the libSQL contract or skip; the reused result set is reassigned per java.sql; DATE-millis is compared as a date-only value.
TypeMapTest stays upstream. #95 rejects type maps with SQLFeatureNotSupportedException because java.sql makes them optional, and the control run cannot assert the rejection and xerial's accepted map at once.
Activity
feat(jdbc): add xerial conformance suite and harden metadata (#28)
fix(jdbc): only report isAutoIncrement for a real AUTOINCREMENT prima…
fix(jdbc): validate prepared statement kind before execution (#57)
fix(jdbc): coerce numeric getters like sqlite-jdbc (#55)
fix(jdbc): propagate rollback failures and keep transaction state (#58)
fix(conformance): enforce vendored upstream checksums (#60)
fix(jdbc): convert getBoolean through integer coercion (#62)
fix(jdbc): match xerial transaction begin and re-begin lifecycle
fix(jdbc): report runtime storage class from getColumnType (#63)
fix(jdbc): match xerial transaction begin and re-begin lifecycle
fix(jdbc): match xerial transaction begin and re-begin lifecycle (#64)
fix(conformance): make namespace cleanup failures visible (#65)
fix(jdbc): make getColumnClassName follow the current row
3 remaining items
fix(jdbc): resolve table metadata for aliases, joins, subqueries, RET…
test(conformance): strengthen weak assertions and add xerial control …
fix(jdbc): match xerial result set navigation boundaries (#72)
fix(jdbc): copy xerial metadata flags and type-name parsing (#73)
feat(jdbc): implement getGeneratedKeys for inserts (#82)
fix(jdbc): honor table types in getTables metadata (#84)
fix(jdbc): match xerial statement lifecycle transitions (#85)
fix(jdbc): report declared types and sizes from getColumns (#88)
fix(jdbc): serialize statement close and execute (#89)
fix(jdbc): cache database metadata per connection (#94)
fix(jdbc): reject unsupported type maps and report holdability (#95)
fix(conformance): close leaked namespaces in the harness (#134)
docs(conformance): re-triage the vendored selection and refresh its d…
magnusp commentedon Oct 23, 2026
Closing the readiness tracker. The 35 review issues in the queue are merged. The three follow-up P1s surfaced by the conformance re-triage (#156, #157, #158) are fixed, and the three previously rejected suites are selected in #164. Final ./mvnw clean verify on main is green with 268 conformance tests and 7 skips, the itests module green, and the xerial control profile green at 245 tests with 2 skips. The only remaining follow-up is #155, a P2 tokenizer consolidation.