Skip to content

fix: H2 natural-key upserts, shared MERGE base, PostgreSQL JSONB binding, @StormTest script splitting#165

Merged
zantvoort merged 3 commits into
mainfrom
fix/merge-upsert-h2-and-json-binding
Jul 3, 2026
Merged

fix: H2 natural-key upserts, shared MERGE base, PostgreSQL JSONB binding, @StormTest script splitting#165
zantvoort merged 3 commits into
mainfrom
fix/merge-upsert-h2-and-json-binding

Conversation

@zantvoort

@zantvoort zantvoort commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Four fixes:

  1. @StormTest script splitting — scripts were split on every ;, so a semicolon inside a SQL comment, string literal, or quoted identifier broke all tests using the script. Now split with a comment/literal-aware scanner (9 new unit tests + an integration guard in test-schema.sql).

  2. H2 natural-key upserts — H2 cannot infer bare ? parameter types in the MERGE source query (Unknown data type), which is why six upsert tests sat @Disabled. Each parameter is now cast to the H2 type matching how the ORM binds the value (Instant/OffsetDateTime/ZonedDateTimeTIMESTAMP since they bind as Timestamp; BigDecimalDECFLOAT to avoid scale-0 truncation; enums → VARCHAR; FK columns resolve per-column leaf types through the referenced key, including compound record keys). All six disabled tests re-enabled, plus new dependent one-to-one (+Instant) and compound-FK coverage.

  3. Shared MergeEntityRepositoryImpl — H2, Oracle, and SQL Server each hand-rolled near-identical MERGE code. Now one base class in storm-core with hooks for the real differences (cast type, FROM DUAL, statement terminator, version expressions, insert clause). The source query projects only declared columns — expanded foreign-relation columns were bound but never referenced by ON/UPDATE/INSERT. Net −146 lines.

  4. @Json → PostgreSQL jsonb — converter output was bound via setString, which PostgreSQL rejects for json/jsonb columns, contradicting the documented JSONB recommendation. Converters now emit a JsonString marker bound through a SqlDialect.setParameter(JsonString) overload (default: plain string, unchanged elsewhere; PostgreSQL: untyped parameter so the server casts). Covered by new Testcontainers tests against real jsonb columns (insert/update/ON CONFLICT upsert/batch) plus JPA-binding and inline-literal tests.

Verification

  • Full reactor: 6,674 tests, 0 failures/errors/skips, including the PostgreSQL, Oracle, SQL Server, MySQL, and MariaDB container suites.
  • The new PostgreSQL JSON tests reproduce the exact original failure when run against the unpatched converters.

zantvoort added 3 commits July 3, 2026 21:25
…erals

The @stormtest script runner split statements with a bare split(";"),
so a semicolon inside a line comment, block comment, string literal or
quoted identifier broke every test class using the script. Statements
are now split with a small scanner that skips those regions and drops
comment-only fragments. A semicolon planted in test-schema.sql guards
the integration path.
…dialects

H2 cannot infer the type of a bare parameter in the projection of the
MERGE source query and failed natural-key upserts with "Unknown data
type". Each parameter is now cast to the H2 type matching how the ORM
binds the value; foreign key columns resolve their per-column leaf type
through the referenced key (including compound record keys). The six
previously @disabled H2 upsert tests are re-enabled, with new coverage
for dependent one-to-one entities (PK-is-FK) with temporal columns and
compound foreign keys.

The near-identical MERGE implementations of H2, Oracle and SQL Server
are extracted into a shared MergeEntityRepositoryImpl with hooks for
the dialect differences (cast type, source suffix, statement suffix,
version expressions, insert clause). The source query now projects only
declared columns — expanded foreign-relation columns were bound but
never referenced — and identifier quoting is applied consistently in
the update and bind-vars clauses.
@JSON converter output was bound with setString, which PostgreSQL
rejects for json/jsonb columns ("column is of type jsonb but expression
is of type character varying") — even though the documentation
recommends JSONB columns. The JSON converters now wrap their serialized
output in a JsonString marker, bound through a new
SqlDialect.setParameter(JsonString) overload: the default binds as a
plain string (unchanged behavior for other databases), while PostgreSQL
binds an untyped parameter so the server casts it to the column's JSON
type. The JPA template unwraps the marker and inline literal rendering
quotes it like a string.

New Testcontainers coverage exercises insert, update, ON CONFLICT
upsert and batch upsert against real jsonb columns, plus tests for the
JPA binding and inline literal paths.
@zantvoort zantvoort added this to the 1.11.7 milestone Jul 3, 2026
@zantvoort zantvoort force-pushed the fix/merge-upsert-h2-and-json-binding branch from 45df2eb to 1b91e38 Compare July 3, 2026 19:28
@zantvoort zantvoort merged commit 26c13f2 into main Jul 3, 2026
6 checks passed
@zantvoort zantvoort deleted the fix/merge-upsert-h2-and-json-binding branch July 3, 2026 19:28
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