Repository navigation
Conversation
getObject resolved the declared type on every call: it re-parsed the Col decltype with a regular expression and hit the registry map for each row. A million-row scan of INTEGER, REAL and VARCHAR columns fell from about 64M rows/s to 4.3M rows/s, and an INTEGER-only scan from about 105M to 21M. Resolve all columns once when the result set is built and read the cached JdbcType by index. getObject now converts from the Value it has already read instead of fetching the column again, and the metadata reads the same cache. An expression column keeps a null entry and falls back to the storage class of the value, which is a class test and a switch rather than another DeclaredType parse.
The getters resolved the current row again on every call: row index branch, rows.size(), rows.get and Row.values. A result set now keeps the value list of the row the getters read, so next() swaps it once per row and getCurrentValue is a single list read. This pays back the per-column lookup the declared-type resolution added to getObject: the three-column scan runs at trunk speed while the declared type is still read from the per-result-set cache. The documented quirks are kept: before the first next() the getters read the first row, and after the last row the list is null so they return SQL NULL.
Removing the xerial column mapping dropped the SQLite spellings that Appendix B does not name: DATETIME, INT, INT2, INT8, MEDIUMINT, UNSIGNED BIG INT, DOUBLE PRECISION, TEXT, CHARACTER, NATIVE CHARACTER and VARYING CHARACTER all became unknown and fell back to the storage class. Users saw DATETIME report VARCHAR instead of TIMESTAMP, INT8 report INTEGER instead of BIGINT, and DOUBLE PRECISION report FLOAT instead of DOUBLE. Keep the Appendix B registry at its 27 canonical names and add an alias table that points each SQLite spelling at the registry entry it stands for. Aliases resolve through the declared type, so INT8 stays BIGINT for a small value, and they share the canonical JdbcType instance, so getObject and the metadata agree. A test pins every alias. TEXT now resolves to VARCHAR, so the empty-result-set class name for a TEXT column is String rather than Object.
|
Verdict: CLEAN at d900f63. Base Ten lanes PASS at the verdict head. Transcripts are under
Perf is PASS on the specified metric. Three interleaved rounds on 1,000,000 rows with The first fix round closed two defects. The declared type is now resolved once per column per result set instead of once per Units at the verdict head are 1,487 tests, 507 failures, 0 errors. All failures are Appendix B rows owned by PR-2 through PR-5. Spotless is clean. CI is red only where Appendix B is red, by design. No review-bot comments to triage. STACK-READY. PR-2 branches from this head. |
|
Verdict correction, wording. "Matches trunk" in the alias lane applies to The INTEGER-only diagnostic lane varied between about 2 and 10 percent below trunk across runs. The plan's stated metric is the three-column lane, which passed at 2 to 4 percent faster. The note stands as a diagnostic, not a gate. |
## 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.
Why
JDBC 4.3 Appendix B keys
ResultSet.getObjectand the column metadata on the JDBC type declared for the column, not on the magnitude or storage class of the value read. The driver instead mapped each value at read time, so aBIGINTholding 42 came back as anIntegerand aNUMERICholding 42 came back as anInteger. PR-1 makes one resolvedJdbcTypeper column the source of truth forgetColumnType,getColumnClassName, andgetObject(int).What changed
JdbcTypeMappingwith theJdbcTyperecord (sqlType,javaClass,objectClass,storage,support), a registry for the 27 declared types in the plan, and a storage-class fallback for a missing or unknown declared type. A fallback is markedSupport.FALLBACK.getObjectand the metadata share oneJdbcTypeper column. The first cut resolved per call and lost 15 times the throughput; the regression is closed and pinned by the perf lane.INT,INT8,DATETIME,TEXT, andDOUBLE PRECISION. Aliases resolve through the declared type, so existing metadata does not change whenXerialColumnTypeis deleted.LibsqlResultSet.getObject(int)returns the declared object type:BIGINT->Longeven for 42,INTEGER->IntegerwithSQLExceptionwhen the value does not fit,NUMERIC/DECIMAL->BigDecimal,BIT/BOOLEAN->Boolean,REAL->Float,DATE/TIME/TIMESTAMP-> thejava.sqltemporal type, andCLOB/BLOB->Clob/Blob.getBooleanparses the text valuestrueandfalseand keeps integer coercion for every other storage class.XerialColumnTypeafter moving its last callers, and moved its integer helper intoJdbcTypeMapping.Scope
PR-1 touches only
libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/and the unit test files named in the plan plus the existinggetBooleantest. It does not changesetObject/setNull, add the PR-3 wrappers, or change the Appendix B oracle assertions. Expression columns with nodecltype, such asSELECT 1 + 1, keep the storage-class fallback and still return anInteger.Tradeoffs
NCLOBandDATALINKresolve toNClobandURLfor metadata, but their getters arrive in PR-3. Until thengetObjectkeeps the value's storage-class representation for those two declared types, which leaves their B-3 rows failing as assertions rather than throwing.INTEGERnow rejects a value outside theintrange instead of silently truncating it.Blast Radius
BIGINTcolumn reportingIntegerfor small values will seeLong.XerialColumnType, so this PR does not change those callers.Verification
All commands were run in
/tmp/opencode/libsql-238/pr-1at headd900f63.Jdbc4AppendixBReadTest#b3GetObjectReturnsDeclaredObjectTypeARRAY,STRUCT,REF(PR-5) andDATALINK,ROWID,NCLOB,SQLXML(PR-3). Down from 18.Jdbc4AppendixBReadTest#b1NaturalGetterReturnsDeclaredJavaTypeJdbc4AppendixBSetObjectTest./mvnw -q -pl libsql-jdbc test./mvnw -q spotless:checkPerf on a 1,000,000-row result set with
INTEGER,REAL, andVARCHAR, three interleaved rounds, mean of five measured runs after five warmups: trunk 63.4M, 64.2M, 64.4M rows/s, head 64.8M, 66.2M, 67.0M rows/s. Head is 2 to 4 percent faster and inside the 5 percent rule.