Skip to content

fix(jdbc): reject unsupported type maps and report holdability - #95

Merged
magnusp merged 1 commit into
mainfrom
fix/48-typed-settings
Oct 22, 2026
Merged

magnusp merged 1 commit into
mainfrom
fix/48-typed-settings

Conversation

@magnusp

@magnusp magnusp commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Closes #48. Part of #54.

What changed

  • LibsqlConnection.getTypeMap and setTypeMap now throw SQLFeatureNotSupportedException instead of returning an immutable empty map or discarding the argument.
  • LibsqlConnection.getHoldability reports ResultSet.CLOSE_CURSORS_AT_COMMIT; setHoldability rejects every other value. The three-argument createStatement, prepareStatement, and prepareCall overloads validate the holdability argument the same way.
  • LibsqlStatement.getResultSetHoldability, LibsqlResultSet.getHoldability, and LibsqlDatabaseMetaData.getResultSetHoldability all report CLOSE_CURSORS_AT_COMMIT; supportsResultSetHoldability accepts only that value.
  • LibsqlStatement and LibsqlResultSet now store fetch size and reject negative values instead of returning a constant zero.
  • Added LibsqlDriverSettingsTest covering set-and-read for every setting, negative fetch size, rejected holdability, and metadata agreement.

Contract decision

  • TypeMap: rejected with SQLFeatureNotSupportedException, per Steering.
  • Holdability: matches xerial 3.53.4.0 (CLOSE_CURSORS_AT_COMMIT; other values rejected) in the connection, statement, result set, and metadata.
  • Fetch size: matches xerial 3.53.4.0: the value is stored and readable, and negative values are rejected. This is the one deviation from the Steering wording; see Deviations.

Evidence

Failing before the fix

./mvnw -q -pl libsql-jdbc -am -Dtest=LibsqlDriverSettingsTest -Dsurefire.failIfNoSpecifiedTests=false test

Result: Tests run: 8, Failures: 8, Errors: 0, Skipped: 0. Holdability was 1 instead of 2, type-map calls did not throw, and fetch size stayed 0 after being set.

Passing after the fix

./mvnw -pl libsql-jdbc -am test

Result: Tests run: 178, Failures: 0, Errors: 0, Skipped: 0, BUILD SUCCESS.

./mvnw -pl libsql-conformance -am verify

Result: Tests run: 115, Failures: 0, Errors: 0, Skipped: 1, BUILD SUCCESS. This includes the selected FetchSizeTest against a live sqld container.

./mvnw spotless:apply

Result: BUILD SUCCESS.

libsql-itests was not run because Steering does not ask for live-sqld integration tests for this issue; the conformance run was included because it exercises the fetch-size behavior changed here.

Deviations

  • Fetch size is implemented rather than rejected. Steering says to throw SQLFeatureNotSupportedException where neither driver implements fetch size. The pinned xerial 3.53.4.0 does implement it (JDBC3ResultSet.getFetchSize/setFetchSize store a row hint and reject negative values), and the vendored, selected conformance test org.sqlite.FetchSizeTest calls ResultSet.setFetchSize(2) and requires it to succeed. Throwing would break the required conformance suite, so this PR matches the pinned driver instead. No silent no-op remains: the value round-trips and negative values are rejected.
  • Stale conformance manifest reason (not changed). upstream-xerial_sqlite-jdbc.manifest still says TypeMapTest is removed because "libSQL accepts but ignores type maps". The driver now rejects type maps, so that reason is stale. The manifest belongs to the conformance lane, so it is left untouched.

@magnusp
magnusp merged commit 277ef8d into main Oct 22, 2026
5 checks passed
@magnusp
magnusp deleted the fix/48-typed-settings branch October 22, 2026 00:27
magnusp added a commit that referenced this pull request Oct 23, 2026
… getters (#146)

Fixes #114
Closes #27

## Contract

Hrana 3 is the source of truth for transport, values, metadata,
batching, transactions, and error reporting. `java.sql` is the source of
truth for the JDBC surface. xerial/sqlite-jdbc is a behavioral reference
and conformance foundation, never a copy target.

Protocol basis: `Value.Blob` is base64 on the wire, so streams and blobs
materialize locally from the decoded bytes. Unsupported optional
features throw `SQLFeatureNotSupportedException` per JDBC.

## What changed

- `LibsqlResultSet`: every remaining `UnsupportedOperationException`
replaced with `SQLFeatureNotSupportedException` (all `update*`,
`getRef`, `getArray`, `getURL`, `getRowId`, `getNClob`, `getSQLXML`,
`getAsciiStream`, `getUnicodeStream`, `getNCharacterStream`).
- `getBinaryStream(int/String)` over `getBytes` and
`getCharacterStream(int/String)` over `getString`; SQL NULL returns
null.
- Read-only `getBlob(int/String)` and `getClob(int/String)` over the
materialized value, with the full read side of `java.sql.Blob`/`Clob`;
write methods throw `SQLFeatureNotSupportedException`.
- `getObject(int, Map)` and `getObject(String, Map)` reject a non-empty
map with `SQLFeatureNotSupportedException("Type maps are not
supported")`; null and empty maps keep the existing behavior.
- `findColumn(null)` throws `SQLException("column label cannot be
null")` instead of `NullPointerException`.
- `LibsqlDatabaseMetaData`: 14 unsupported metadata methods now throw
`SQLFeatureNotSupportedException`.

## #27 answer

Read side supported: `getBlob`/`getClob` and the full read surface of
`Blob`/`Clob` (length, `getBytes`/`getSubString`, streams, position,
free), plus `getBinaryStream` and `getCharacterStream`.

Write side not supported: `PreparedStatement.setBlob`/`setClob` and the
`Blob`/`Clob` mutators are outside this PR's Files block and still throw
`SQLFeatureNotSupportedException`. `getAsciiStream`, `getUnicodeStream`,
and `getNCharacterStream` also remain unsupported (see divergences).

## Divergences

- xerial supports `getAsciiStream`, `getUnicodeStream`, and
`getNCharacterStream`; this PR leaves them unsupported because the
plan's Build limits stream implementation to `getBinaryStream` and
`getCharacterStream`. They now throw the checked JDBC exception, so
`catch (SQLException)` callers survive.
- xerial returns unsupported for `getBlob`; this driver implements it
because Hrana already carries the decoded blob bytes and #27 asks for
blob support. It is read-only.
- xerial throws unsupported for `getObject(Map)` unconditionally; this
driver accepts null and empty maps and rejects only a non-empty map,
matching #95's `setTypeMap` contract.

## Evidence

Red (trunk `1c240cd` with these test files,
`/tmp/xerial-parity-i114/lane-1-regression.log`): 67 tests, 4 failures
and 4 errors. The new stream, blob, map, findColumn, and sweep cases
fail on `UnsupportedOperationException`, the ignored type map, and the
null-label NPE.

Green (head `47ea3f2`, same log): 67 tests, 0 failures, BUILD SUCCESS.

Lanes (logs under `/tmp/xerial-parity-i114/`):

1. Regression, trunk red to head green: pass.
2. Byte-for-byte stream content: 4 tests pass.
3. Unchecked-exception sweep over both classes: 2 tests pass, no
`UnsupportedOperationException`.
4. `./mvnw -pl libsql-jdbc test`: 273 tests, BUILD SUCCESS.
5. `./mvnw -pl libsql-conformance -am verify`: 123 tests, 1 skipped,
BUILD SUCCESS, no new divergence.
6. `./mvnw -pl libsql-conformance -am -Pxerial-control verify`: 100
tests, 1 skipped, BUILD SUCCESS.
7. `./mvnw spotless:check`: BUILD SUCCESS.
8. `./mvnw test`: BUILD SUCCESS.
9. #27 surface: recorded in `lane-9-specific.log`.
10. `./mvnw -pl libsql-jdbc -am package -DskipTests`: jar built.

Perf: `./mvnw -pl libsql-jdbc test` medians over three interleaved runs,
trunk 2.159 s versus head 2.167 s, +0.4 percent, within the 10 percent
rule.

Files touched: only the four files in the plan's Files block. Base
`1c240cd`, head `47ea3f2`.
magnusp added a commit that referenced this pull request Oct 23, 2026
…ocs (#159)

Fixes #116
Closes #77
Closes #96

## What changed

- Re-triaged every rejected class against the current driver. Four
classes changed state.
- `TypeMapTest` keeps its upstream rejection, but the reason is now
true: #95 rejects connection type maps with
`SQLFeatureNotSupportedException` because java.sql makes them optional,
and xerial stores a map it never applies. The control run executes the
same vendored test as the libSQL run, so a single assertion cannot pass
on both. The stale "accepts but ignores" text is gone (#96).
- `DBMetaDataTest`, `StatementTest`, and `PrepStmtTest` still fail
against the current driver. The plan's triage rule says a real driver
gap becomes a new issue and an exclusion, never a protocol divergence,
and the plan's Files block is manifest plus docs only. They stay
upstream with the new issues as their reason:
- #156 `DBMetaDataTest`: 45 of 56 pass. Seven metadata methods throw
where xerial returns a java.sql-shaped empty result set; `getColumns`,
`getCrossReference`, and `getPrimaryKeys` disagree on filtering and
order; sqld rejects `CREATE TEMP TABLE`.
- #158 `StatementTest`: 38 of 46 pass. `getDate('')`, `setDate` millis,
`setFetchDirection(999)`, and `executeBatch` with a SELECT are driver
gaps. Multi-statement SQL, `CREATE TEMP TABLE`, and the open-result-set
DROP lock are remote-model cases that need divergences when the class
selects.
- #157 `PrepStmtTest`: `getParameterMetaData`, the CLOB/BLOB/stream
setters, `clearParameters`, `setDate` millis, and
supplementary-character round trips are driver gaps. Hrana has no
describe statement, so pre-execution metadata and prepare-time
validation are impossible-parity cases.
- Regenerated `UPSTREAM.md` with the vendoring tool, so its `Paths` row
now matches the vendored tree (#77). Added a Divergences section that
names the four files with recorded edits.
- Refreshed the stale `QueryTest` rationale found in the same triage. It
imports sqlite-jdbc's `SQLiteConfig` and `FastDateFormat` classes.
- Updated the #54 tracker: all 25 sub-issue boxes are checked, and the
status note records the four re-triaged classes.

No new divergence line is added. The four existing ones remain:

- `CachedRowSetTest`: Hrana holds a namespace until its last connection
closes, so the conformance copy closes the leaked connection.
- `InsertQueryTest`: upstream misuses the connection factory, so the
copy reuses the transaction connection and asserts rollback, commit
visibility, and the final count (#51).
- `JSON1Test`: Hrana holds a namespace until its last connection closes,
so the copy closes the leaked per-test connections.
- `PreparedStatementThreadTest`: upstream swallows every
`ExecutionException`; Hrana serializes requests per stream, so the copy
awaits both futures and fails on any runtime error (#51, #118).

## Contract

Hrana 3 is the source of truth for transport, values, metadata,
batching, transactions, and error reporting. `java.sql` is the source of
truth for the JDBC surface. xerial/sqlite-jdbc 3.53.4.0 is the
behavioral reference and conformance foundation, never a copy target.
Every exclusion above cites the protocol, sqld, java.sql, or a tracked
driver issue, with no bug recorded as a parity divergence.

## Red

The four candidate suites at the PR base, fetched with
`tools/export-upstream 3.53.4.0` and run against libSQL:

```
./mvnw -pl libsql-conformance -am -Dit.test=DBMetaDataTest verify
Tests run: 56, Failures: 3, Errors: 8, Skipped: 1

./mvnw -pl libsql-conformance -am -Dit.test=StatementTest verify
Tests run: 46, Failures: 4, Errors: 4, Skipped: 1

./mvnw -pl libsql-conformance -am -Dit.test=PrepStmtTest verify
Tests run: 43, Failures: 7, Errors: 8, Skipped: 0

./mvnw -pl libsql-conformance -am -Dit.test=TypeMapTest verify
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0
```

The failing method lists are in #156, #157, and #158.

## Green

At `f36dcfcefc49545ef52cbe9608677380875c8dfb`:

```
tools/export-upstream --verify
Verified upstream-xerial_sqlite-jdbc against upstream-xerial_sqlite-jdbc.manifest

tools/export-upstream --verify-pinned
Verified upstream hashes against cab7981c19ce04d691f0675f0b2586afc2bbf803

./mvnw -pl libsql-conformance -am verify
Tests run: 123, Failures: 0, Errors: 0, Skipped: 1, BUILD SUCCESS

./mvnw -pl libsql-conformance -am -Pxerial-control verify
Tests run: 100, Failures: 0, Errors: 0, Skipped: 1, BUILD SUCCESS

./mvnw -pl libsql-conformance -am test        BUILD SUCCESS
./mvnw -pl libsql-jdbc test                   BUILD SUCCESS
./mvnw test                                   BUILD SUCCESS
./mvnw spotless:check                         BUILD SUCCESS
./mvnw -pl libsql-jdbc -am package -DskipTests BUILD SUCCESS
```

Trunk lane: `./mvnw -pl libsql-conformance -am verify` at `7c7d321`
gives 123 run, 0 failures, 1 skipped. The head passes every test trunk
passes.

Perf, wall clock of `./mvnw -pl libsql-jdbc test`, interleaved three
times per side: trunk median 2.45 s, head median 2.49 s, 1.6 percent
over baseline.

## Files

Only `upstream-xerial_sqlite-jdbc.manifest` and
`upstream-xerial_sqlite-jdbc/UPSTREAM.md` change. No driver or test
source changes. No files outside the plan's Files block.

Base `7c7d32102e4b9e59b8e04d02ff095274ca76fbf2`, head
`f36dcfcefc49545ef52cbe9608677380875c8dfb`.
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.

TypeMap, fetch size, and holdability are silently accepted no-ops

1 participant