{"record":{"id":"0ff1e98d1611ede0","repo":"hibernate/hibernate-orm","slug":"h2-json-table-only-supports-literal-json-paths-0ff1e9","errorCode":null,"errorMessage":"H2 json_table() only supports literal json paths, but got {jsonPath}","messagePattern":"H2 json_table\\(\\) only supports literal json paths, but got (.+?)","errorType":"exception","errorClass":"QueryException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java","lineNumber":620,"sourceCode":"\t\t\t\tboolean lateral,\n\t\t\t\tboolean withOrdinality,\n\t\t\t\tSqmToSqlAstConverter converter) {\n\t\t\tfinal JsonTableArguments arguments = JsonTableArguments.extract( sqlAstNodes );\n\t\t\tfinal Expression jsonDocument = arguments.jsonDocument();\n\t\t\tfinal String documentPath;\n\t\t\tfinal ColumnReference columnReference = jsonDocument.getColumnReference();\n\t\t\tif ( columnReference != null ) {\n\t\t\t\tdocumentPath = columnReference.getExpressionText();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdocumentPath = tableIdentifierVariable + \"_.\" + \"d\";\n\t\t\t}\n\n\t\t\tfinal String parentPath;\n\t\t\tfinal boolean isArray;\n\t\t\tif ( arguments.jsonPath() != null ) {\n\t\t\t\tif ( !( arguments.jsonPath() instanceof Literal literal) ) {\n\t\t\t\t\tthrow new QueryException( \"H2 json_table() only supports literal json paths, but got \" + arguments.jsonPath() );\n\t\t\t\t}\n\t\t\t\tfinal String rawJsonPath = (String) literal.getLiteralValue();\n\t\t\t\tisArray = isArrayAccess( rawJsonPath );\n\t\t\t\tfinal String jsonPath = isArray ? rawJsonPath.substring( 0, rawJsonPath.length() - 3 ) : rawJsonPath;\n\t\t\t\tparentPath = H2JsonValueFunction.applyJsonPath( documentPath, true, arguments.isJsonType(), jsonPath, arguments.passingClause() );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// We have to assume this is an array\n\t\t\t\tisArray = true;\n\t\t\t\tparentPath = documentPath;\n\t\t\t}\n\n\t\t\tfinal String parentReadExpression;\n\t\t\tif ( isArray ) {\n\t\t\t\tparentReadExpression = parentPath + \"[\" + tableIdentifierVariable + \".x]\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\tparentReadExpression = '(' + parentPath + ')';","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java#L602-L638","documentation":"Beyond the initial transformation, the H2 json_table() emulation also computes the parent read path for columns while building the lateral subquery select. That step again needs the literal path text (to detect array access and to strip a trailing [*]), so a non-literal path expression makes this code path throw during SQL generation, even if earlier checks passed.","triggerScenarios":"An H2 json_table() call with a non-literal path expression that reaches the nested transformer - typically the same query that binds the path: json_table(d.doc, :path columns(...)), or a path slot filled with a dynamic expression.","commonSituations":"Parameterized json_table queries in generic flattening code; refactors that moved a formerly literal path into a bind parameter without updating the H2 tests.","solutions":["Inline the literal path: json_table(d.doc, '$.items[*]' columns(...))","Parameterize inner parts with PASSING while keeping the path string literal","Use a native H2 query for dynamic paths","Guard with a repository-layer check that json_table paths are string literals"],"exampleFix":"// before - throws on H2\nselect t.name from Document d, json_table(d.doc, :path columns(name varchar)) t\n\n// after - literal path\nselect t.name from Document d, json_table(d.doc, '$.items[*]' columns(name varchar)) t","handlingStrategy":"validation","validationCode":"// Heuristic lint: json_table paths must be string literals on H2\nprivate static final Pattern PARAM_TABLE_PATH =\n    Pattern.compile(\"(?i)json_table\\\\s*\\\\([^,]+,\\\\s*:\\\\w+\");\n\nstatic void assertLiteralJsonPaths(String hql) {\n    if (PARAM_TABLE_PATH.matcher(hql).find()) {\n        throw new IllegalArgumentException(\n            \"json_table path must be a string literal on H2; column read paths are derived from the path text\");\n    }\n}","typeGuard":"// Java predicate (type-guard analogue) for Criteria/SQM path expressions\nstatic boolean isLiteralPath(org.hibernate.query.sqm.tree.expression.SqmExpression<?> pathExpr) {\n    return pathExpr instanceof org.hibernate.query.sqm.tree.expression.SqmLiteral<?>;\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 json paths\")) {\n        throw new IllegalArgumentException(\"Inline the json_table path as a literal: \" + hql, e);\n    }\n    throw e;\n}","preventionTips":["Treat the json_table path as part of the query text, not a runtime value, on H2","Cover every json_table query in a per-dialect integration test","Refactor dynamic paths into a small set of literal-path query templates"],"tags":["hibernate","h2","json","hql","json-table","json-path","bind-parameter"],"backgroundTag":"non-literal-json-path","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}