Skip to content

feat(jdbc): reject unsupported types and unlisted conversions - #244

Closed
magnusp wants to merge 4 commits into
task/238-pr-4-offset-typesfrom
task/238-pr-5-unsupported-types
Closed

magnusp wants to merge 4 commits into
task/238-pr-4-offset-typesfrom
task/238-pr-5-unsupported-types

Conversation

@magnusp

@magnusp magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Why

SQLite has no interoperable representation for ARRAY, STRUCT, REF, JAVA_OBJECT, or DISTINCT. Returning a string or an array-shaped value for them hides the gap and disagrees with JDBC 4.3 Appendix B. Under the operator's policy for issue 238, these five types are N/A and must throw SQLFeatureNotSupportedException. Emulation is rejected.

The other half of the gap is Table B-6: before this PR the driver returned a value from almost every getter regardless of the declared type. A declared column now rejects a getter that B-6 does not list for it. A column with no decltype keeps the permissive storage fallback because Hrana reports no declared type for expressions.

What changed

  • JdbcTypeMapping records ARRAY, STRUCT, REF, JAVA_OBJECT, and DISTINCT with Support.UNSUPPORTED, and stores the Table B-6 supported set for each getter as a bitmask on every supported declared type.
  • LibsqlResultSet rejects an N/A declared type through every getter, and rejects an unlisted getter/declared-type pair with SQLFeatureNotSupportedException. getObject, getArray, and getRef throw for the N/A types.
  • The numeric getters parse text and blob values strictly on a declared column, so getInt on a declared VARCHAR containing not-a-number throws instead of returning 0. The expression fallback still returns 0.
  • JdbcValueConverter rejects setObject with an N/A target type. With no target, Array, Struct, Ref, and every other Java class throw because Table B-4 maps them to an N/A type. The existing setArray and setRef rejection is unchanged.
  • The Appendix B read suite asserts SQLFeatureNotSupportedException for the five N/A types instead of a wrong Java type. The set-object suite keeps the Table B-5 rows and adds an explicit N/A rejection test. The new Jdbc4AppendixBProfileTest is the compliance claim: it asserts the exact N/A and supported lists and the N/A behavior.
  • The integration suite asserts the N/A rows against real sqld DDL and keeps the listed rows as round trips.

Scope

Changed:

  • libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/JdbcTypeMapping.java
  • libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java
  • libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/JdbcValueConverter.java
  • libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/Jdbc4AppendixBReadTest.java
  • libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/Jdbc4AppendixBSetObjectTest.java
  • libsql-jdbc/src/test/java/com/github/magnusp/libsql/jdbc/Jdbc4AppendixBProfileTest.java (new)
  • libsql-itests/src/test/java/com/github/magnusp/libsql/itest/Jdbc4AppendixBTypeMappingIT.java

Not touched: .audit/, the conformance module, docs, and the offset types from PR-4.

N/A list: ARRAY, STRUCT, REF, JAVA_OBJECT, DISTINCT.

Supported list: TINYINT, SMALLINT, INTEGER, BIGINT, REAL, FLOAT, DOUBLE, NUMERIC, DECIMAL, BIT, BOOLEAN, CHAR, VARCHAR, LONGVARCHAR, NCHAR, NVARCHAR, LONGNVARCHAR, BINARY, VARBINARY, LONGVARBINARY, DATE, TIME, TIMESTAMP, TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE, CLOB, NCLOB, SQLXML, ROWID, BLOB, DATALINK.

Tradeoffs

  • N/A is cheaper and safer than a driver-specific JSON or serialization format, but an application that needs ARRAY has no fallback. The operator chose that policy for issue 238.
  • A declared column is stricter than before, so an application that read a BLOB through getInt or a VARCHAR through getBytes now gets SQLFeatureNotSupportedException. That is the Appendix B contract.
  • Expression columns stay permissive because there is no declared type to check. The profile test records that exception rather than claiming strictness the driver cannot provide.
  • The three non-N/A integration failures below are left for PR-6. This PR reduces the integration suite from 22 failures at the base head to 3, and all 19 cleared failures are N/A rows.

Blast Radius

  • Reads from a declared ARRAY, STRUCT, REF, JAVA_OBJECT, or DISTINCT column now throw SQLFeatureNotSupportedException instead of returning text or a wrapper.
  • Every getter/declared-type pair outside Table B-6 now throws. Listed conversions such as getInt on INTEGER are unchanged.
  • setObject with an N/A target, or with an Array, Struct, Ref, or an unbound Java class and no target, now throws.
  • setNull still accepts every target type, and the storage fallback for expression columns is unchanged.

Verification

Observed on the final head:

Command Result
./mvnw -q -pl libsql-jdbc -Dtest='Jdbc4AppendixBReadTest' -Dsurefire.failIfNoSpecifiedTests=false test 1050 tests, 0 failures, 0 errors
./mvnw -q -pl libsql-jdbc -Dtest='Jdbc4AppendixBProfileTest' test 4 tests, 0 failures, 0 errors
./mvnw -q -pl libsql-jdbc -Dtest='Jdbc4AppendixBSetObjectTest' test 34 tests, 0 failures, 0 errors
./mvnw -q -pl libsql-jdbc test 1510 tests, 0 failures, 0 errors, 0 skipped
./mvnw -q spotless:check exit 0
install + -Dit.test=Jdbc4AppendixBTypeMappingIT verify 148 tests, 3 failures, 0 errors

The same integration command at base 3d11c0c failed 22 tests. All 19 cleared rows are N/A rows. The three that remain are not N/A:

  • B-2: java.sql.RowId -> ROWID (the stub returns no bytes, so the bound value is null)
  • B-2: java.sql.SQLXML -> SQLXML (the stub returns no text, so the bound value is null)
  • B-5: java.sql.NClob -> NCLOB (the test reads getSubString as a String but expects an NClob)

They are pre-existing and outside the PR-5 scope; PR-6 reconciles the remaining integration and conformance failures.

@magnusp

magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Verdict: CLEAN at 068e91d. Base 3d11c0c, branch task/238-pr-5-unsupported-types, parent PR #243.

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

Lane Check Result
1 Regression against trunk trunk getInt on BLOB returns 0; head throws SQLFeatureNotSupportedException
2 getString on BLOB SQLFeatureNotSupportedException
3 getBytes on VARCHAR SQLFeatureNotSupportedException
4 getDate on INTEGER SQLFeatureNotSupportedException
5 Declared ARRAY getObject throws, SQLFeatureNotSupportedException
6 Declared STRUCT getObject throws
7 Declared JAVA_OBJECT getObject throws
8 Expression fallback SELECT 1 + 1 still returns 2 from getInt
9 Listed conversion getInt on INTEGER returns 42
10 Profile Jdbc4AppendixBProfileTest is 4 of 4 green and lists exactly the five N/A types

Units are 1,510 tests, 0 failures, 0 errors. The Appendix B read suite is 1,050 of 1,050 green, the profile test is 4 of 4, and the setObject test is 34 of 34. Spotless is clean. CI now passes the unit jobs and the Xerial control; the only failing job is the integration module.

Perf is PASS. The rejection path is 0.99 microseconds at p99 against a 10 microsecond budget. The getInt control is 0.15 microseconds at the head and 0.17 at trunk.

The integration suite is 148 tests, 3 failures. All three are defects in the test fixtures, not in the driver. A null-returning RowId and SQLXML proxy make those two rows bind null, and the NCLOB reader unwraps the wrapper to a String while the expectation asks for an NClob. They are handed to PR-6, which owns integration reconciliation, and I will not count them as clean evidence until that PR fixes them.

CI is red only in libsql-itests, for those three fixtures. No review-bot comments to triage.

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

@magnusp

magnusp commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Verdict correction, label. CLEAN overstates the intermediate state. The production change was clean on its ten lanes and its unit suites, but the integration suite carried three fixture failures that PR-6 fixed. The correct label is STACK-READY with one deferred fixture fix. The stack tip at 234186d is green, and the integration behavior is proven there. No code change is needed on this PR.

@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