Skip to content

fix(jdbc): derive column type name from decltype (#76) - #141

Merged
magnusp merged 1 commit into
mainfrom
fix/i76-typename-decltype
Oct 23, 2026
Merged

magnusp merged 1 commit into
mainfrom
fix/i76-typename-decltype

Conversation

@magnusp

@magnusp magnusp commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Fixes #76

Contract: Hrana 3 StmtResult.cols[].decltype is the source of truth for the declared column type; java.sql.ResultSetMetaData is the JDBC surface; xerial/sqlite-jdbc 3.53.4.0 is the behavioral reference.

Change

  • getColumnTypeName derives the name from decltype only. A null decltype marks an expression column and returns the documented unknown value NUMERIC before the first row, on a row, and after the last row.
  • Removed sampleType and its row scan, so the type name no longer depends on unvisited rows.
  • isSigned for an expression column now follows the current value's storage class (no scan). Without this, RSMetaDataTest.columTypeWithoutTable regressed because its CAST(... AS VARCHAR(50)) expression reports NUMERIC as its type name. This keeps the xerial per-row answer.

Evidence

Base dbb9749318356c537e95dae078bdee80c17a1714, head 7864433c187e3b95f2aba17bf303765e15c1f8fb.

  • Red at trunk with the new tests copied in and production code unchanged: 3 failures, every one expected: "NUMERIC" but was: "INTEGER" from the row-sampling path. /tmp/xerial-parity-i76/lane-1-trunk.log
  • Green at head: LibsqlResultSetMetaDataTest 34/34. /tmp/xerial-parity-i76/lane-1-head.log
  • Lane 2, mixed storage classes (integer then text): 1/1.
  • Lane 3, getColumnClassName is wrong for empty and mixed-type result sets #36 cursor-local class cases: 3/3.
  • Lane 4, module suite: 251/251, BUILD SUCCESS.
  • Lane 5, conformance against libSQL: BUILD SUCCESS, 123 tests, 1 skipped.
  • Lane 6, xerial control profile: BUILD SUCCESS, 100 tests, 1 skipped. The control RSMetaDataTest passes 13/13.
  • Lane 7, spotless:check: no violations.
  • Lane 8, full reactor ./mvnw test: BUILD SUCCESS.
  • Lane 9, vendored RSMetaDataTest: 13/13.
  • Lane 10, package: driver jar built.
  • Perf: medians of three interleaved runs each, trunk 2.14s and head 2.09s (head -2.3%, within the 10% rule). /tmp/xerial-parity-i76/perf.log

All logs are under /tmp/xerial-parity-i76/.

Divergences

  • On a row, an expression column returns NUMERIC; xerial reports the current storage class. Hrana metadata carries no runtime type, and getColumnTypeName samples the first non-null value across all rows #76 requires the type name to be independent of rows. isSigned still matches xerial's per-row result by reading only the current storage class.
  • The lowercase cast(... as ...) fallback in declType() is retained, matching xerial's getColumnDeclType. Uppercase CAST(1 AS BOOLEAN) is not matched, so that expression column now returns NUMERIC instead of its storage-class name.

Expression columns have no declared type, so getColumnTypeName returns the documented unknown value NUMERIC at every cursor position instead of sampling the first non-null row value. Remove sampleType and its row scan. Keep isSigned xerial-compatible for expression columns by reading the current row's storage class.
@magnusp
magnusp merged commit 57e1b53 into main Oct 23, 2026
5 checks passed
@magnusp
magnusp deleted the fix/i76-typename-decltype branch October 23, 2026 00:05
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.

getColumnTypeName samples the first non-null value across all rows

1 participant