Skip to content

feat(jdbc): add the missing getters and LOB wrappers - #242

Closed
magnusp wants to merge 4 commits into
task/238-pr-2-target-typesfrom
task/238-pr-3-wrappers
Closed

magnusp wants to merge 4 commits into
task/238-pr-2-target-typesfrom
task/238-pr-3-wrappers

Conversation

@magnusp

@magnusp magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Why

JDBC 4.3 Appendix B lists native getters for DATALINK, NCLOB, SQLXML,
and ROWID, plus the ASCII and national character streams, and this driver
threw SQLFeatureNotSupportedException for all of them. PR-3 adds those
read paths and the two missing parameter setters so the oracle's B-1, B-3,
and the listed cells of B-6 can go green.

What changed

  • New LibsqlNClob, LibsqlSQLXML, and LibsqlRowId wrappers in the
    driver package. Each holds the materialized value of one column and every
    read after free() throws SQLException.
  • getURL parses DATALINK text through URI.toURL() and throws
    SQLException (with MalformedURLException as the cause) when the text is
    not an absolute URL.
  • getNClob, getSQLXML, and getRowId build the new wrappers over the
    stored text or bytes. getObject routes NCLOB, SQLXML, ROWID, and
    DATALINK to the same wrappers, so the object type and the typed getter
    agree.
  • getAsciiStream reads the text as US-ASCII bytes and
    getNCharacterStream delegates to getNString.
  • setSQLXML binds the SQLXML content as text. setRowId binds the bytes as
    a BLOB, the one representation getRowId reads back unchanged.
  • SQLXML and ROWID join the declared-type registry so
    ResultSetMetaData.getColumnType, getColumnClassName, and getObject
    resolve them instead of falling back to the storage class.
  • LibsqlClob is no longer final, so LibsqlNClob reuses it: SQLite
    stores national and regular text identically.

setArray and setRef still throw; PR-5 decides their status. No Appendix B
assertion was relaxed.

Scope

Read and bind only. No change to the declared-type model beyond the two new
registry rows, no change to JdbcValueConverter, no wiring change, no new
dependency, no interaction change.

Tradeoffs

  • SQLXML.getSource and setResult throw SQLFeatureNotSupportedException.
    The wrapper is a read-only view over stored text; callers read it through
    getString or getCharacterStream. Parsing XML is left to the caller.
  • setRowId binds a BLOB rather than text because text would lossily decode
    bytes that are not valid UTF-8, and the round trip is the contract.
  • B-6 total failures rise (461 -> 627) even though 25 listed cells go green.
    The six new getters now succeed on columns whose Table B-6 row lists no
    conversion, and PR-6 adds the per-getter supported set that rejects them.

Blast Radius

  • getObject return type changes for four declared types: DATALINK now
    returns URL, NCLOB NClob, SQLXML SQLXML, ROWID RowId. All four
    previously returned the storage-class value (String/Integer/byte[]).
  • ResultSetMetaData now reports Types.SQLXML/Types.ROWID and the matching
    class names for SQLXML- and ROWID-declared columns instead of the
    storage fallback.
  • Six ResultSet getters and two PreparedStatement setters stop throwing.
    LibsqlResultSetTest no longer lists them as unsupported.
  • getAsciiStream replaces non-ASCII characters with ?, which is the
    US-ASCII encoder's contract; it is not a UTF-8 alias.
  • LibsqlClob loses final; no public API change (package-private class).

Verification

Unit, at head 4067d42:

  1. ./mvnw -q -pl libsql-jdbc -Dtest='Jdbc4AppendixBReadTest#b1NaturalGetterReturnsDeclaredJavaType' -Dsurefire.failIfNoSpecifiedTests=false test
    -> Tests run: 34, Failures: 3. Down from 7; the three left
    (ARRAY, STRUCT, REF) belong to PR-5.
  2. ./mvnw -q -pl libsql-jdbc -Dtest='Jdbc4AppendixBReadTest#b6GetterHonoursSupportedJdbcTypes' -Dsurefire.failIfNoSpecifiedTests=false test
    -> Tests run: 972, Failures: 627. Listed-cell ("must support") failures
    are 2, down from 27: getArray on ARRAY and getRef on REF, both
    PR-5. The rest are the "must reject" cells PR-6 owns.
  3. ./mvnw -q -pl libsql-jdbc test
    -> Tests run: 1503, Failures: 634, Errors: 0. Every failure is in
    Jdbc4AppendixBReadTest; all 17 other test classes pass, including
    Jdbc4AppendixBSetObjectTest (B-5) at 33/0. B-3 fell from 7 to 3
    (ARRAY, STRUCT, REF).
  4. ./mvnw -q spotless:check -> exit 0.

New tests in Jdbc4AppendixBReadTest: b1UrlGetterParsesAbsoluteTextAndRejectsTheRest,
b3NewWrappersReadBackTheirContent (length, substring, both streams),
b3FreedWrappersRejectFurtherReads (NClob, SQLXML, Clob, Blob), and
b3RowIdComparesAndCopiesItsBytes. LibsqlPreparedStatementTest adds
rowIdAndSqlXmlParametersBindTheirContent and
rowIdBoundAsBlobReadsBackTheSameBytes.

Live lanes and the perf probe are not run here; this PR stops at STACK-READY
for the root's swarm.

Each wrapper holds the materialized value of one result column and
rejects every read once freed. NClob reuses the Clob text holder because
SQLite stores national and regular text identically; SQLXML and RowId are
read-only views over the same text and bytes.
getURL parses DATALINK text and throws on text that is not a URL.
getNClob, getSQLXML, and getRowId wrap the stored text or bytes, and
getObject routes NCLOB, SQLXML, ROWID, and DATALINK to the same wrappers.
getAsciiStream reads text as US-ASCII and getNCharacterStream delegates to
getNString.

SQLXML and ROWID join the declared-type registry so the metadata and
getObject agree on their object type.
setSQLXML binds the SQLXML content as text. setRowId binds the bytes as a
BLOB, which is the only representation getRowId reads back unchanged.
setArray and setRef still throw.
B-1 and B-3 now check the URL, NClob, SQLXML, RowId, ASCII, and NCHAR
getters end to end. A freed wrapper must reject every further read.
@magnusp

magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Verdict: CLEAN at 4067d42. Base 0ea436c, branch task/238-pr-3-wrappers, parent PR #241.

Ten lanes PASS at the verdict head. Transcripts are under /tmp/swarm-pr-3/.

Lane Check Result
1 Regression against trunk trunk throws SQLFeatureNotSupportedException from getURL; head returns the URL
2 Malformed DATALINK SQLException: Bad value for type URL: not a url
3 getNClob length 3, text abc
4 getSQLXML text <a/>
5 getRowId bytes rowid-1
6 getAsciiStream reader abc
7 getNCharacterStream reader abc
8 setSQLXML round trip stored text <a/>
9 setRowId round trip stored blob, bytes match
10 Wrapper free reads after free throw SQLException for NClob and SQLXML

Units are 1,503 tests, 634 failures, 0 errors. B-1 is down from 7 to 3 and B-3 from 7 to 3, both leaving only the PR-5 types. The B-6 listed-cell failures are down from 27 to 2. The B-6 total rose to 627 because the new getters now answer unlisted cells that PR-5's supported-set gate must reject, which is the plan's sequence. Spotless is clean.

The integration suite at this head is 146 tests, 26 failures, down from 45 at PR-2 and 63 at the baseline. The remaining rows are the ARRAY, STRUCT, and REF types owned by PR-5.

Perf is PASS. Over 100,000 calls, getURL p99 is 1.6 microseconds and the other three new getters are under 0.1 microseconds, all far below the 50 microsecond budget. The timestamp control is unchanged within measurement noise across three interleaved rounds.

CI is red only where Appendix B is red, by design. No review-bot comments to triage.

STACK-READY. PR-4 branches from this head.

@magnusp

magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Verdict correction, evidence. The stale-jar run's output was not preserved. The corrected run is the worktree failsafe report at /tmp/opencode/libsql-238/pr-3/libsql-itests/target/failsafe-reports, 146 tests and 26 failures, after installing the head. The owner's report assigned the B-6 supported-set gate to PR-6; the plan and the shipped code assign it to PR-5. That is a report wording error, not a verdict change.

@magnusp
magnusp added this pull request to stack #246 October 25, 2026 09:09
magnusp added a commit that referenced this pull request Oct 25, 2026
## Why

The driver mapped result values by storage class, so the declared type
did not control metadata or `getObject`, and `setObject` ignored the
target type. JDBC 4.3 Appendix B is the oracle. Seven stacked pull
requests implement it. The stack cannot land link by link because the
`main` ruleset requires the full build green at every head, and the
Appendix B oracle is red until the last link. This PR lands the verified
tip as one commit.

## What changed

- Add the Appendix B oracle suite and the vendor table, then resolve one
JDBC type per declared column for metadata and `getObject`.
- Honor the `setObject` target type, add the missing getters and LOB
wrappers, and support the offset temporal types.
- Mark `ARRAY`, `STRUCT`, `REF`, `JAVA_OBJECT`, and `DISTINCT` N/A and
reject unlisted Table B-6 conversions.
- Publish the compliance profile, quarantine the four xerial tests that
conflict with the spec, and record the policy.

## Scope

The seven stacked PRs are #239, #240, #241, #242, #243, #244, and #245.
No unrelated changes. `vendor/JDBC4-TYPE-MAPPING.md` is the oracle.

## Tradeoffs

- The five unrepresentable types throw instead of emulating them.
- The conformance quarantine is method-level. The lost checks are named
in `libsql-conformance/README.md`.
- An expression column with no `decltype` keeps the storage fallback,
documented as a profile exception.

## Blast Radius

Metadata and `getObject` now follow the declared type, so a `BIGINT`
holding 42 returns `Long`, and an unlisted getter conversion throws. The
default build was red from #239 through #245 by design and is green at
this tip.

## Verification

- `clean verify` at `0861960`: 2,006 tests, 0 failures, 7 skipped, about
63 seconds.
- Appendix B unit 1,050 of 1,050, integration 148 of 148, conformance
270 of 270 with 7 skipped.
- Clean clone at the same SHA: unit 1,510 of 1,510, integration 148 of
148.
- Each link has a verdict and ten live lanes. The verdict comments are
on #239 through #245.
@magnusp

magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #247. The verified stack tip at 0861960 landed as one commit on main (e41cac1) because the main ruleset requires green required checks at every head, and the Appendix B oracle is red until the last link. The verdict and review record on this PR are unchanged.

@magnusp magnusp closed this Oct 25, 2026
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.

1 participant