You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(model): the H2 adapter doesn't quote identifiers, so reserved-word column names fail #4440
The H2 model adapter doesn't quote identifiers. H2Model extends wheels.databaseAdapters.Base, whose $quoteIdentifier() returns the name unchanged. The MySQL, PostgreSQL, SQL Server, Oracle and SQLite adapters each override it. So on H2, generated SQL that names a column which is a reserved word (order, group, user, value, ...) is invalid. A model backed by such a table can't be queried or ordered by that column, whether the order is written order="column" or order="table.column".
Repro (H2 datasource): a table with a column named order, then model("X").findAll(order="order DESC"). The generated ORDER BY order DESC is a syntax error in H2.
The core spec OrderReservedWordSpec (#4392) is skipped on H2 for this reason.
Why this isn't a one-line override: H2 folds unquoted identifiers to upper case, while a quoted identifier is case-sensitive. Quoting every identifier would change how existing H2 schemas resolve. For example, a table created unquoted as users is stored as USERS, and a quoted "users" would no longer match it. Adding quoting needs its own design: match the stored case, quote only reserved words, or make it opt-in. It also needs H2 runs across the model and migrator suites.
Expected: reserved-word column names work on H2 the way they do on the other adapters, without changing how existing H2 schemas resolve.
Moved to v4.3.0. Quoting H2 identifiers has to follow how each H2 datasource is configured:
which identifiers are reserved depends on the H2 version (1.3.x vs 2.x);
whether, and in which case, unquoted names are folded depends on DATABASE_TO_UPPER / DATABASE_TO_LOWER.
Quoting with fixed rules breaks names that work unquoted today on some configurations. For example, on H2 2.x with DATABASE_TO_UPPER=FALSE, users, first$name and today work unquoted but fail as "USERS", "FIRST$NAME" and "TODAY". A fix needs to detect the version and those settings per datasource. It is also blocked on running H2 2.x in the test images, so it can be covered by the suite. #4599 holds a first attempt as a draft.
Fixed on develop in #4628 (merge commit e7437cd), superseding the closed draft #4599.
What now works on H2. The adapter probes the datasource's identifier fold case once (from INFORMATION_SCHEMA.SETTINGS, handling the H2 2.x SETTING_NAME/SETTING_VALUE and 1.3.x NAME/VALUE shapes) and quotes table and column identifiers in that case. Reserved-word columns (order, group, value, …) work in findAll(order=…), where=, selects and writes. Default folding quotes upper ("ORDER"); DATABASE_TO_LOWER=TRUE quotes lower. OrderReservedWordSpec is unskipped on H2 and passes.
What deliberately does not change. With folding disabled (DATABASE_TO_UPPER=FALSE) the adapter emits exactly the unquoted SQL it emitted before — reserved-word columns remain unsupported there, and schemas are untouched. Quoted lower/mixed-case schemas on a default-folding database were unsupported before and remain so. H2Migrator still emits unquoted DDL, and reserved-word table names on writes are still unquoted on every adapter.
Evidence. Full core suite on H2 2.1.214: 7287 passed / 0 failed / 0 errors (lucee7 + h2 CI leg green); SQLite: 1501 model + 189 database specs passed; Adobe 2023 and BoxLang legs green.
Follow-ups from independent review are tracked separately (migrator DDL quoting, tenant JOIN memo key, classifier unit coverage).
Activity
bpamiri commentedon Oct 11, 2026
H2 edge case, not a regression in 4.2.0; targeted for a later release.
bpamiri commentedon Oct 14, 2026
Moved to v4.3.0. Quoting H2 identifiers has to follow how each H2 datasource is configured:
DATABASE_TO_UPPER/DATABASE_TO_LOWER.Quoting with fixed rules breaks names that work unquoted today on some configurations. For example, on H2 2.x with
DATABASE_TO_UPPER=FALSE,users,first$nameandtodaywork unquoted but fail as"USERS","FIRST$NAME"and"TODAY". A fix needs to detect the version and those settings per datasource. It is also blocked on running H2 2.x in the test images, so it can be covered by the suite. #4599 holds a first attempt as a draft.fix(model): quote H2 identifiers in the datasource's fold case (#4628)
bpamiri commentedon Oct 21, 2026
Fixed on
developin #4628 (merge commit e7437cd), superseding the closed draft #4599.What now works on H2. The adapter probes the datasource's identifier fold case once (from
INFORMATION_SCHEMA.SETTINGS, handling the H2 2.xSETTING_NAME/SETTING_VALUEand 1.3.xNAME/VALUEshapes) and quotes table and column identifiers in that case. Reserved-word columns (order,group,value, …) work infindAll(order=…),where=, selects and writes. Default folding quotes upper ("ORDER");DATABASE_TO_LOWER=TRUEquotes lower.OrderReservedWordSpecis unskipped on H2 and passes.What deliberately does not change. With folding disabled (
DATABASE_TO_UPPER=FALSE) the adapter emits exactly the unquoted SQL it emitted before — reserved-word columns remain unsupported there, and schemas are untouched. Quoted lower/mixed-case schemas on a default-folding database were unsupported before and remain so.H2Migratorstill emits unquoted DDL, and reserved-word table names on writes are still unquoted on every adapter.Evidence. Full core suite on H2 2.1.214: 7287 passed / 0 failed / 0 errors (
lucee7 + h2CI leg green); SQLite: 1501 model + 189 database specs passed; Adobe 2023 and BoxLang legs green.Follow-ups from independent review are tracked separately (migrator DDL quoting, tenant JOIN memo key, classifier unit coverage).