From 079e1d30c443a9566506a28420461938b68ef6f8 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Fri, 6 Nov 2026 11:11:45 +0900 Subject: [PATCH v2-master] JSON_TABLE: fix missing collation assignments transformJsonTable() transforms the context item and the PASSING arguments into the JsonExpr stored in TableFunc.docexpr, and copies the transformed PASSING arguments into TableFunc.passingvalexprs, but never ran assign_expr_collations() over them. That is needed here because assign_query_collations() doesn't look into the range table, which is why transformRangeTableFunc() does it for XMLTABLE, as does transformJsonTableColumn() for JSON_TABLE's own column expressions. As a result, expressions in there were left without collations. A text comparison failed with "could not determine which collation to use for string comparison", even with an explicit COLLATE clause, and a row comparison was left with an empty inputcollids list, which made the executor compare zero columns and return the result for equal rows (so ROW(1, 2) < ROW(3, 4) yielded false), or trip an assertion in ExecInitExprRec() in assert-enabled builds. Fix by calling assign_expr_collations() on the transformed JsonExpr, before the PASSING arguments are copied out of it. Oversight in commit de3600452b6, so back-patch to 17. Bug: #19768 Reported-by: Venu Kummari Author: Venu Kummari Discussion: https://postgr.es/m/19768-3633daa830be95db@postgresql.org Backpatch-through: 17 --- src/backend/parser/parse_jsontable.c | 8 +++++ .../regress/expected/sqljson_jsontable.out | 30 +++++++++++++++++++ src/test/regress/sql/sqljson_jsontable.sql | 11 +++++++ 3 files changed, 49 insertions(+) diff --git a/src/backend/parser/parse_jsontable.c b/src/backend/parser/parse_jsontable.c index 0b469ba636f..801bcb461bf 100644 --- a/src/backend/parser/parse_jsontable.c +++ b/src/backend/parser/parse_jsontable.c @@ -162,6 +162,14 @@ transformJsonTable(ParseState *pstate, JsonTable *jt) jfe->location = jt->location; tf->docexpr = transformExpr(pstate, (Node *) jfe, EXPR_KIND_FROM_FUNCTION); + /* + * Assign collations now, because assign_query_collations() doesn't look + * into the range table. This must happen before the PASSING arguments + * are copied into tf->passingvalexprs below, so that the copies carry + * the collations too. + */ + assign_expr_collations(pstate, tf->docexpr); + /* * Create a JsonTablePlan that will generate row pattern that becomes * source data for JSON path expressions in jt->columns. This also adds diff --git a/src/test/regress/expected/sqljson_jsontable.out b/src/test/regress/expected/sqljson_jsontable.out index fd5e32160d4..1dcb9479de3 100644 --- a/src/test/regress/expected/sqljson_jsontable.out +++ b/src/test/regress/expected/sqljson_jsontable.out @@ -2019,3 +2019,33 @@ CREATE OR REPLACE VIEW public.json_table_view_on_empty AS ) ERROR ON ERROR ) DROP VIEW json_table_view_on_empty; +-- Collations must be assigned to expressions in the context item and in +-- the PASSING arguments (bug #19768) +SELECT * FROM JSON_TABLE(jsonb_build_object('k', ROW(1, 2) < ROW(3, 4)), '$' + COLUMNS (a bool PATH '$.k')); + a +--- + t +(1 row) + +SELECT * FROM JSON_TABLE(jsonb_build_object('k', 'a' < 'b' COLLATE "C"), '$' + COLUMNS (a bool PATH '$.k')); + a +--- + t +(1 row) + +SELECT * FROM JSON_TABLE(jsonb '{}', '$' PASSING 'a' < 'b' COLLATE "C" AS x + COLUMNS (a bool PATH '$x')); + a +--- + t +(1 row) + +SELECT * FROM JSON_TABLE(jsonb '{}', '$' PASSING ROW(1, 2) < ROW(3, 4) AS x + COLUMNS (a bool PATH '$x')); + a +--- + t +(1 row) + diff --git a/src/test/regress/sql/sqljson_jsontable.sql b/src/test/regress/sql/sqljson_jsontable.sql index c24d9ffdd29..73e60411df6 100644 --- a/src/test/regress/sql/sqljson_jsontable.sql +++ b/src/test/regress/sql/sqljson_jsontable.sql @@ -1106,3 +1106,14 @@ SELECT * FROM JSON_TABLE(jsonb '{}', '$' AS p0 \sv json_table_view_on_empty; DROP VIEW json_table_view_on_empty; + +-- Collations must be assigned to expressions in the context item and in +-- the PASSING arguments (bug #19768) +SELECT * FROM JSON_TABLE(jsonb_build_object('k', ROW(1, 2) < ROW(3, 4)), '$' + COLUMNS (a bool PATH '$.k')); +SELECT * FROM JSON_TABLE(jsonb_build_object('k', 'a' < 'b' COLLATE "C"), '$' + COLUMNS (a bool PATH '$.k')); +SELECT * FROM JSON_TABLE(jsonb '{}', '$' PASSING 'a' < 'b' COLLATE "C" AS x + COLUMNS (a bool PATH '$x')); +SELECT * FROM JSON_TABLE(jsonb '{}', '$' PASSING ROW(1, 2) < ROW(3, 4) AS x + COLUMNS (a bool PATH '$x')); -- 2.47.3