From 8cc1eaeb06a2894eefbbb8fa6817f48342775192 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Wed, 26 Nov 2025 16:22:06 +0900 Subject: [PATCH] Fix error reporting for SQL/JSON path type mismatches transformJsonFuncExpr() used exprType()/exprLocation() on the possibly coerced path expression, which could be NULL when coercion to jsonpath failed. Preserve the original expression node so that type and location in the "must be of type jsonpath" error are reported correctly. Add regression tests to cover these cases. Reported-by: Jian He Author: Jian He Reviewed-by: Kirill Reshke Discussion: https://postgr.es/m/CACJufxHunVg81JMuNo8Yvv_hJD0DicgaVN2Wteu8aJbVJPBjZA@mail.gmail.com Backpatch-through: 17 --- src/backend/parser/parse_expr.c | 24 ++++++++++++------- .../regress/expected/sqljson_queryfuncs.out | 8 +++++++ src/test/regress/sql/sqljson_queryfuncs.sql | 2 ++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 32d6ae918caa..87b93166163d 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -4285,6 +4285,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func) { JsonExpr *jsexpr; Node *path_spec; + Node *coerced_path_spec; const char *func_name = NULL; JsonFormatType default_format; @@ -4500,17 +4501,24 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func) jsexpr->format = func->context_item->format; path_spec = transformExprRecurse(pstate, func->pathspec); - path_spec = coerce_to_target_type(pstate, path_spec, exprType(path_spec), - JSONPATHOID, -1, - COERCION_EXPLICIT, COERCE_IMPLICIT_CAST, - exprLocation(path_spec)); - if (path_spec == NULL) + coerced_path_spec = coerce_to_target_type(pstate, path_spec, + exprType(path_spec), + JSONPATHOID, -1, + COERCION_EXPLICIT, + COERCE_IMPLICIT_CAST, + exprLocation(path_spec)); + if (coerced_path_spec == NULL) + { + Oid pathspec_type = exprType(path_spec); + int pathspec_loc = exprLocation(path_spec); + ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("JSON path expression must be of type %s, not of type %s", - "jsonpath", format_type_be(exprType(path_spec))), - parser_errposition(pstate, exprLocation(path_spec)))); - jsexpr->path_spec = path_spec; + "jsonpath", format_type_be(pathspec_type)), + parser_errposition(pstate, pathspec_loc))); + } + jsexpr->path_spec = coerced_path_spec; /* Transform and coerce the PASSING arguments to jsonb. */ transformJsonPassingArgs(pstate, func_name, diff --git a/src/test/regress/expected/sqljson_queryfuncs.out b/src/test/regress/expected/sqljson_queryfuncs.out index 5a35aeb7bba3..53145f50f186 100644 --- a/src/test/regress/expected/sqljson_queryfuncs.out +++ b/src/test/regress/expected/sqljson_queryfuncs.out @@ -1331,6 +1331,10 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER); [123] (1 row) +SELECT JSON_QUERY(jsonb '{"a": 123}', ('$' || '.' || 'a' || NULL)::date WITH WRAPPER); +ERROR: JSON path expression must be of type jsonpath, not of type date +LINE 1: SELECT JSON_QUERY(jsonb '{"a": 123}', ('$' || '.' || 'a' || ... + ^ -- Should fail (invalid path) SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error'); ERROR: syntax error at or near " " of jsonpath input @@ -1355,6 +1359,10 @@ SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths; "aaa" (1 row) +SELECT json_value('"aaa"', jsonpaths RETURNING json) FROM jsonpaths; +ERROR: JSON path expression must be of type jsonpath, not of type jsonpaths +LINE 1: SELECT json_value('"aaa"', jsonpaths RETURNING json) FROM js... + ^ -- Test PASSING argument parsing SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xy); ERROR: could not find jsonpath variable "xyz" diff --git a/src/test/regress/sql/sqljson_queryfuncs.sql b/src/test/regress/sql/sqljson_queryfuncs.sql index 8d7b225b6121..a5d5e256d7ff 100644 --- a/src/test/regress/sql/sqljson_queryfuncs.sql +++ b/src/test/regress/sql/sqljson_queryfuncs.sql @@ -450,6 +450,7 @@ SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a'); SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY); SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a'); SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER); +SELECT JSON_QUERY(jsonb '{"a": 123}', ('$' || '.' || 'a' || NULL)::date WITH WRAPPER); -- Should fail (invalid path) SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error'); @@ -460,6 +461,7 @@ SELECT JSON_QUERY(NULL FORMAT JSON, '$'); -- Test non-const jsonpath CREATE TEMP TABLE jsonpaths (path) AS SELECT '$'; SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths; +SELECT json_value('"aaa"', jsonpaths RETURNING json) FROM jsonpaths; -- Test PASSING argument parsing SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xy);