{"record":{"id":"1416f94cdcc6806f","repo":"hibernate/hibernate-orm","slug":"h2-json-table-only-supports-literal-default-expr","errorCode":null,"errorMessage":"H2 json_table() only supports literal default expressions, but got {defaultExpression}","messagePattern":"H2 json_table\\(\\) only supports literal default expressions, but got (.+?)","errorType":"exception","errorClass":"QueryException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java","lineNumber":709,"sourceCode":"\t\t\t}\n\t\t\treturn addSelectableMappings( selectableMappings, tableIdentifierVariable, columnDefinition.columns(), nextClauseLevel, readExpression, converter );\n\t\t}\n\n\t\tprotected void addSelectableMappings(List<SelectableMapping> selectableMappings, String tableIdentifierVariable, JsonTableOrdinalityColumnDefinition definition, int clauseLevel, SqmToSqlAstConverter converter) {\n\t\t\taddSelectableMapping(\n\t\t\t\t\tselectableMappings,\n\t\t\t\t\tdefinition.name(),\n\t\t\t\t\tordinalityExpression( tableIdentifierVariable, clauseLevel ),\n\t\t\t\t\tconverter.getCreationContext().getTypeConfiguration().getBasicTypeForJavaType( Long.class )\n\t\t\t);\n\t\t}\n\n\t\tprotected void addSelectableMappings(List<SelectableMapping> selectableMappings, JsonTableValueColumnDefinition definition, String parentReadExpression, SqmToSqlAstConverter converter) {\n\t\t\tfinal JsonValueEmptyBehavior emptyBehavior = definition.emptyBehavior();\n\t\t\tfinal Literal defaultExpression;\n\t\t\tif ( emptyBehavior != null && emptyBehavior.getDefaultExpression() != null ) {\n\t\t\t\tif ( !( emptyBehavior.getDefaultExpression() instanceof Literal literal ) ) {\n\t\t\t\t\tthrow new QueryException( \"H2 json_table() only supports literal default expressions, but got \" + emptyBehavior.getDefaultExpression() );\n\t\t\t\t}\n\t\t\t\tdefaultExpression = literal;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdefaultExpression = null;\n\t\t\t}\n\t\t\tfinal String baseReadExpression = determineElementReadExpression( definition.name(), definition.jsonPath(), parentReadExpression );\n\t\t\tfinal String elementReadExpression = castValueExpression( baseReadExpression, definition.type(), defaultExpression, converter );\n\n\t\t\taddSelectableMapping(\n\t\t\t\t\tselectableMappings,\n\t\t\t\t\tdefinition.name(),\n\t\t\t\t\telementReadExpression,\n\t\t\t\t\tdefinition.type().getJdbcMapping()\n\t\t\t);\n\t\t}\n\n\t\tprivate String castValueExpression(String baseReadExpression, CastTarget castTarget, @Nullable Literal defaultExpression, SqmToSqlAstConverter converter) {","sourceCodeStart":691,"sourceCodeEnd":727,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java#L691-L727","documentation":"In the H2 json_table() emulation, a column-level default <expr> on empty clause is implemented by baking the default value into the generated coalesce/cast SQL text at translation time. Only compile-time Literal values can be baked in; a bind parameter or computed expression as the default is rejected with this QueryException while the column mappings are built.","triggerScenarios":"HQL on H2 with a non-literal column default: json_table(d.doc, '$.items[*]' columns(name varchar default :fallback on empty)) or columns(name varchar default concat('n','a') on empty). emptyBehavior.getDefaultExpression() is not a Literal, so addSelectableMappings throws.","commonSituations":"Wanting per-request default values for missing JSON fields; the same query rendering fine on dialects that inline defaults as ordinary SQL expressions, then failing on the H2 test profile.","solutions":["Use a literal default: columns(name varchar default 'N/A' on empty)","Apply the variable default outside the function: select coalesce(t.name, :fallback) from ... json_table(...) t","Default to null inside json_table and substitute the default in application code","Fall back to a native query if the default must be a computed expression"],"exampleFix":"// before - throws on H2 (non-literal default expression)\nselect t.name from Document d, json_table(d.doc, '$.items[*]' columns(name varchar default :fallback on empty)) t\n\n// after - literal default inside, variable default applied with coalesce outside\nselect coalesce(t.name, :fallback) from Document d, json_table(d.doc, '$.items[*]' columns(name varchar default 'N/A' on empty)) t","handlingStrategy":"validation","validationCode":"// Heuristic lint: json_table column defaults must be literals on H2\nprivate static final Pattern NONLITERAL_DEFAULT =\n    Pattern.compile(\"(?i)default\\\\s+(:\\\\w+|[a-z_]+\\\\s*\\\\()\");\n\nstatic void assertLiteralDefaults(String hql) {\n    if (NONLITERAL_DEFAULT.matcher(hql).find()) {\n        throw new IllegalArgumentException(\n            \"json_table column defaults must be literals on H2; apply variable defaults with coalesce() outside\");\n    }\n}","typeGuard":"// Java predicate (type-guard analogue) for the default expression node\nstatic boolean isLiteralDefault(org.hibernate.sql.ast.tree.expression.Expression defaultExpr) {\n    return defaultExpr instanceof org.hibernate.sql.ast.tree.expression.Literal;\n}","tryCatchPattern":"try {\n    return session.createQuery(hql, Object[].class).getResultList();\n} catch (org.hibernate.QueryException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"only supports literal default expressions\")) {\n        // Rewrite: move the dynamic default outside the json_table call\n        throw new IllegalArgumentException(\n            \"Use a literal default or coalesce(t.col, :fallback) instead\", e);\n    }\n    throw e;\n}","preventionTips":["Write column defaults as literals inside json_table and apply variable defaults via coalesce() outside","Remember HQL string literals use single quotes: default 'N/A' on empty","Keep per-dialect integration tests for json_table column clauses"],"tags":["hibernate","h2","json","hql","json-table","default-expression","bind-parameter"],"backgroundTag":"non-literal-default-expression","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}