Skip to content

fix(db): case-tolerant schema/table filters in DBMetadataInspector (H2 v2 uppercase fallback) - #3

Open
emmanuelbruno wants to merge 1 commit into
chore/build-correctnessfrom
feature/metadata-case-fallback
Open

emmanuelbruno wants to merge 1 commit into
chore/build-correctnessfrom
feature/metadata-case-fallback

Conversation

@emmanuelbruno

Copy link
Copy Markdown
Collaborator

Why — the H2 v2 uppercase trap

JDBC DatabaseMetaData lookups match the schema/table filters exactly — no case folding. H2 v2 stores unquoted identifiers UPPERCASE; postgres stores them natively lowercase. DBMetadataInspector.inspect passed the %%rdbmsSchema cell filter strings verbatim to md.getColumns / getPrimaryKeys / getIndexInfo / getImportedKeys / getTables (bytecode-verified in the shipped IJava-v1.4.6-pr12.jar; source call sites in this file, reached from JavaDBMSMagics.java:315) → a lowercase filter against an H2 EMPLOYEES table yields empty metadata and the entity renders without attributes/columns — silently. Postgres is unaffected (names natively lowercase, filters typically lowercase too).

ebpro lab workaround so far is content-side only (write the body-line filter uppercase, e.g. EMPLOYEES under PUBLIC) — it works but forces lesson authors to know each store's identifier case policy. This PR obsoletes that.

Change

Case-tolerant filter resolution inside DBMetadataInspector.inspect (the minimal-touch location — it fans out to all six metadata queries, so fixing the caller JavaDBMSMagics:315 alone would leave the other query paths and any future callers exposed):

  • Probe the filter as-given first; only when the column lookup returns zero rows, retry UPPER then lower (Locale.ROOT).
  • As-given matches — postgres and every correctly-cased call — take the identical code path with identical ordering semantics (single extra getColumns probe only in the currently-broken empty case).
  • The TableMetadata display name keeps the as-given value; only query filters are resolved.
  • Wildcard filters (e.g. % enumeration) match rows as-given → untouched.
String fSchema = schema;
String fTable = table;
if (table != null && !hasColumns(md, schema, table)) {
    String[][] candidates = {
        {schema == null ? null : schema.toUpperCase(Locale.ROOT), table.toUpperCase(Locale.ROOT)},
        {schema == null ? null : schema.toLowerCase(Locale.ROOT), table.toLowerCase(Locale.ROOT)},
    };
    for (String[] cand : candidates) {
        if (hasColumns(md, cand[0], cand[1])) { fSchema = cand[0]; fTable = cand[1]; break; }
    }
}

The resolved fSchema/fTable replace the verbatim arguments in the five JDBC metadata calls and the three information_schema prepared statements (constraints / check constraints / domains — H2's information_schema likewise stores the uppercase name).

Tests

New DBMetadataInspectorCaseTest (H2 is already on the test classpath — build.gradle: testImplementation 'com.h2database:h2:2.2.220', JUnit 4):

  • lowercase filter against UPPER-stored table → columns + PKs resolved, display name preserved;
  • as-given match → unchanged columns/order (ID first);
  • missing table → still empty (no false resolution).

Local: ./gradlew :test --tests 'io.github.spencerpark.ijava.magics.DBMetadataInspectorCaseTest' green; full :test --tests 'io.github.spencerpark.ijava.magics.*' green (Temurin JDK 25, Gradle 9.7.1).

Base note

Branches from chore/build-correctness (tip = tag v1.4.6-pr12, the lineage that produces the shipped jar). master predates DBMetadataInspector entirely, so it cannot host this change.

JDBC metadata lookups match filters exactly; H2 v2 stores unquoted
identifiers UPPERCASE (postgres lowercase) so a wrong-case filter
silently yielded empty metadata. Probe as-given first (behavior and
ordering unchanged when it matches), then UPPER, then lower.
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