Skip to content

Find the framework surface we do not implement, and write it down - #18

Merged
ShawnChen-Sirius merged 4 commits into
mainfrom
fix/issue-11-framework-coverage
Sep 9, 2026
Merged

ShawnChen-Sirius merged 4 commits into
mainfrom
fix/issue-11-framework-coverage

Conversation

@ShawnChen-Sirius

@ShawnChen-Sirius ShawnChen-Sirius commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Refs #11. Does not close it — Spring JdbcTemplate is still open, and ShardingSphere turns out
not to be closable here at all.

The issue said the deliverable is less the passing test than the list of unconditionally-called
surface we do not implement. Here is that list, and the headline is that it is empty.

DatabaseMetaData, the DBeaver stand-in

DatabaseMetaDataSurfaceIT invokes all 179 methods of JDBC 4.3's DatabaseMetaData
reflectively, with the arguments a GUI would pass:

Outcome Count
Returned a value or a result set with rows 160
Returned an empty result set 19
Threw SQLFeatureNotSupportedException 0
Threw anything else 0

So the risk the issue names — "a SQLFeatureNotSupportedException thrown from somewhere DBeaver
calls unconditionally would make the driver look broken on first contact" — does not exist.

The test does not stop at non-null. Every metadata result set is checked against the JDBC 4.3
column labels transcribed from the specification, not read back from the driver, and every
ResultSetMetaData accessor is called on every column of every one. All present, all answerable,
rows or not. That is the empty-ResultSet-with-correct-metadata path the issue flagged as a
different code path, and it is correct.

The 19 empty ones: getAttributes, getBestRowIdentifier, getCatalogs,
getClientInfoProperties, getColumnPrivileges, getCrossReference, getExportedKeys,
getFunctionColumns, getImportedKeys, getIndexInfo, getPrimaryKeys, getProcedureColumns,
getProcedures, getPseudoColumns, getSuperTables, getSuperTypes, getTablePrivileges,
getUDTs, getVersionColumns.

Per framework

  • MyBatis 3.5.19 — works, 8 tests. Finding: the default JdbcTransactionFactory with
    openSession() calls setAutoCommit(false), which the driver refuses; use openSession(true)
    or ManagedTransactionFactory. Auto-mapping picks the right TypeHandler per column and
    insert() returns the real affected-row count.
  • jOOQ 3.16.23 — works, 8 tests, two findings: .limit() fails by default because it is
    rendered as a bind value (fix: StatementType.STATIC_STATEMENT), and transaction() is refused
    with no setting to avoid it. Also recorded so it is not mistaken for a driver bug: jOOQ types
    every chDB column as Object because SQLDialect.DEFAULT has no Int32/DateTime64. The
    driver reports INTEGER/Int32/java.lang.Integer correctly on populated and zero-row sets,
    which is asserted.
  • ShardingSphere 5.5.3 — cannot be wired up, and it is upstream's doing. It already lists
    jdbc:chdb among its ClickHouse URL prefixes, but every DatabaseType then parses the URL with
    StandardJdbcUrlParser, which requires the //authority of a client/server URL. jdbc:chdb:,
    jdbc:chdb::memory: and a filesystem storage path all fail with
    UnrecognizedDatabaseURLException; the YAML driver and ShardingSphereDataSourceFactory fail
    identically, so no configuration reaches the driver. ShardingSphereIT pins this with 3 tests so
    it flips when upstream changes. Its connection management versus one-storage-path-per-JVM
    therefore stays untested; HikariPoolIT is the nearest cover, since ShardingSphere's own pools
    are HikariCP.
  • DBeaver — no blocker found, via the sweep above.

A cross-cutting finding

Bound parameters are typed String, because the engine's binding is textual. Measured on 26.7.0:
fine in = ? < ? > ? IN (?) and string contexts; fails in LIMIT ? / OFFSET ? (Code 440),
in arithmetic (Code 43), and in numeric table-function arguments like numbers(?) (Code 43). This
is the root cause of the jOOQ .limit() failure. Workaround is to cast in SQL — toUInt64(?).
Recorded in docs/unsupported.md; it deserves its own issue.

Nothing here is blocked on #12

The driver's DatabaseMetaData is implemented as plain SELECTs against system.* tables, so
none of the metadata surface touches SHOW or DESCRIBE.

Also in this PR

Three status lines that described the state before HikariPoolIT landed — the README row, the
phase-9 row in docs/v1-progress.md, and the docs/release-readiness.md gap table. The last was
the misleading one: it grouped ShardingSphere with Spring as "lower yield, half a day", when
ShardingSphere is not lower yield but unreachable.

Verified

mvn -pl chdb-jdbc,chdb-integration-tests -am verify on macos-aarch64: 148 unit + 127 integration
tests, 0 failures. mvn -pl chdb-jdbc dependency:tree shows exactly one dependency,
junit-jupiter:test — the driver's zero-third-party-runtime-dependency property is intact. New
dependencies are test scope only, with the version choices justified in the pom against the Java 11
floor (jOOQ 3.17.0 is class file 61, read out of the published jar).

🤖 Generated with Claude Code

Note

Add integration tests for DatabaseMetaData surface, jOOQ, MyBatis, and ShardingSphere

  • Adds DatabaseMetaDataSurfaceIT.java which reflectively invokes every DatabaseMetaData method from the running JDK, classifies outcomes, and verifies every ResultSet-returning method exposes the expected JDBC column labels
  • Adds JooqIT.java covering jOOQ 3.16 dialect selection, metadata walks, bind-value limits, type inference gaps, and transaction behavior
  • Adds MyBatisIT.java covering automatic mapping, #{...} parameter binding, type-handler selection, and transaction factories
  • Adds ShardingSphereIT.java showing that ShardingSphere recognizes jdbc:chdb as a ClickHouse prefix but rejects embedded URL forms lacking an authority
  • Updates docs/unsupported.md, docs/v1-progress.md, and README.md to record the tested framework surface, the ShardingSphere URL-parser blocker, and the string-bound-parameter limitation

Macroscope summarized a312ae9.

ShawnChen-Sirius and others added 2 commits September 9, 2026 17:46
Issue #11 asks for the list of driver surface a framework calls
unconditionally and we refuse, not for four passing tests. Four things
came out of the search, and docs/unsupported.md now carries them.

Nothing on DatabaseMetaData throws. DatabaseMetaDataSurfaceIT sweeps all
179 methods reflectively, which is what a JDBC GUI does at connect time
and the reason issue #11 ranks DBeaver first: one refusal inside that
sweep is reported as "could not connect", so the driver looks broken
before the user has typed anything. 160 answer, 19 return an empty result
set, none throws. The test asserts the zeros so the property cannot be
lost by accident, and it checks every metadata result set against the
column labels JDBC 4.3 specifies -- transcribed from the JDBC javadoc
rather than from this driver -- because jOOQ and MyBatis address those
columns by name and a zero-row set with the wrong labels is as broken as
a throw and much harder to see.

A bound parameter is typed String, and that is a usage constraint rather
than a footnote. It works in a comparison, where ClickHouse converts the
literal to the column's type, and fails where a numeric constant is
required: LIMIT, OFFSET, arithmetic, numeric table-function arguments.
jOOQ renders .limit() as a bind value by default, so this is the first
thing a jOOQ user hits, and MyBatis's #{} in numbers() is the second.
Both now have a test naming the engine's error code and the cast that
fixes it.

MyBatis and jOOQ both refuse a transaction at the same place: MyBatis's
default JdbcTransactionFactory and jOOQ's DefaultTransactionProvider each
call setAutoCommit(false) before running anything, which the driver will
not do. MyBatis has a way out -- openSession(true), or
ManagedTransactionFactory -- and jOOQ does not, so a jOOQ application
against chDB works outside transaction().

ShardingSphere cannot be wired up at all, and it is upstream's. 5.5.3
already lists jdbc:chdb among its ClickHouse URL prefixes, but every
DatabaseType then parses the URL with StandardJdbcUrlParser, which
requires the //authority of a client/server URL and refuses the URL
without it. No chDB URL of any form gets through, and the YAML driver and
the programmatic factory fail identically, so there is no configuration
route around it. ShardingSphereIT pins that rather than skipping, so it
starts failing when ShardingSphere can parse an embedded URL.

Versions are the last of each that runs on Java 11, the driver's floor:
MyBatis 3.5.19, jOOQ 3.16.23 -- 3.17.0 is the first Open Source Edition
compiled to class file 61, read out of the published jar -- and
ShardingSphere 5.5.3. All test scope; chdb-jdbc still resolves to no
non-test dependency at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three status lines still described the state before HikariPoolIT landed: the README
called the whole row 🚧 and named Spring, HikariCP and ShardingSphere as the set;
v1-progress listed HikariCP as not started, which it has not been since PR #6; and
release-readiness grouped ShardingSphere with Spring as "lower yield, half a day".

The last one is the misleading one. ShardingSphere is not lower yield, it is not
reachable: every DatabaseType parses the URL with StandardJdbcUrlParser, which
requires the //authority of a client/server URL, so jdbc:chdb:, jdbc:chdb::memory:
and a filesystem path are all rejected before any config we could write is read.
Half a day of effort would not have closed it. It is now its own row, marked
upstream and pinned by ShardingSphereIT so it flips when that changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread docs/unsupported.md Outdated
Review asked whether the documented 179-method sweep happens on Java 11,
the driver's floor, and said the interface has 170 methods there. It does
not: DatabaseMetaData.class.getMethods() returns 179 on JDK 11, 21 and 26
alike, and the scan classified all 179 on each. The interface declares
177 of its own -- getSchemas and supportsConvert being overload pairs --
and inherits unwrap and isWrapperFor from Wrapper. So the coverage claim
held, but it was resting on an assertion that could not have caught it if
it had not.

That assertion was `results.size() >= 165`, and the objection to it is
right whatever the count turns out to be: a floor loose enough to pass
with nine methods missing is not guarding the thing the test exists to
guard. Replaced with an exact equality against
DatabaseMetaData.class.getMethods().length -- every method the running
JDK reports, invoked and classified, once each. That says what the test
means, holds on any JDK, and does not have to be revisited when a JDBC
revision moves the total. Checked by deleting one method from the swept
list: the old assertion passed, the new one fails with 178 against 179.

Two smaller holes in the same spirit. scannableMethods() filtered out
Object's methods, which getMethods() on an interface never returns
anyway (measured 0 on all three JDKs) and which would have made the new
equality unreliable if it ever did; it is gone, so the count is exact
rather than nearly exact. And everyResultSetCarriesItsJdbcColumns()
silently skipped any ResultSet-returning method with no transcribed
column list, so a future JDBC catalog query would have quietly gone
unchecked; it now fails on the gap and asserts it checked every
ResultSet-returning method on the interface.

Docs say 179 where they say a number, but say it as a JDK property with
the JDK versions named, and lead with the coverage property instead. The
scan's printed report now names the JDK and the surface size, so a report
pasted into an issue can be compared with another one.

Verified with the full module suite on JDK 11 and JDK 26, and the sweep
alone on 21: 160 values, 19 empty result sets, 0 refusals, 0 errors on
each.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread docs/unsupported.md Outdated
…ut costs

Review said the "no setting for it" claim was wrong because jOOQ ships
NoTransactionProvider. Half right, and the half that is wrong is a trap
worth its own test.

Configuring org.jooq.impl.NoTransactionProvider changes nothing.
DefaultConfiguration.transactionProvider() treats that class as a
sentinel for "unset" and hands back a DefaultTransactionProvider
instead -- read out of jooq-3.16.23 and now asserted, along with
transaction() still being refused afterwards. Subclassing it does not
help either, because the check is an instanceof. Anyone following that
advice would have configured it, seen the same SQLFeatureNotSupportedException
and had no idea why.

What does work is the same SPI with any other implementation:
TransactionProvider has three methods, and a no-op class that is not
jOOQ's own is honoured, so begin never reaches setAutoCommit(false) and
the block runs. Verified.

The cost is the entire point, and it gets more space than the workaround.
A no-op provider means the statements in the block run one at a time as
they would outside it and a failure part-way through leaves the earlier
ones applied -- asserted by throwing out of a block after an INSERT and
finding the row still there. So it buys nothing but the ability to keep
transaction() in the source: not a weak transaction, no transaction, in
code that reads as though it had a unit of work. That is not a driver
gap. chDB has no transaction manager, so a framework's transaction
abstraction can only degrade to a no-op on it, and the real choice is
between a refusal at the call site and a block that silently is not
atomic. The driver refuses; installing the provider chooses the other
one.

Audited the section for the same sentence shape, since that is what got
picked up. Two more:

  * "not fixable from here" and "no configuration route around it" for
    ShardingSphere. The configuration claim stands -- no YAML key and no
    factory argument avoids the URL parse, which the test covers for both
    entry points -- but DatabaseType and ConnectionPropertiesParser are
    ServiceLoader SPIs, so a chDB-specific pair registered ahead of
    ShardingSphere's ClickHouse one is a code route. Documented, with why
    it is not taken: it means versioning a plugin against SPIs that move
    between minor releases, and it would test the plugin rather than this
    driver.

The jOOQ column-type paragraph already carried its workaround, and
checking 3.19.24 confirmed no ClickHouse dialect exists in the Open
Source Edition's SQLDialect to over-claim about.

Full module suite on JDK 11 and JDK 26.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ShawnChen-Sirius
ShawnChen-Sirius merged commit d5654f6 into main Sep 9, 2026
43 of 44 checks passed
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