Fix error reporting for SQL/JSON path type mismatches
authorAmit Langote <[email protected]>
Thu, 27 Nov 2025 01:40:19 +0000 (10:40 +0900)
committerAmit Langote <[email protected]>
Thu, 27 Nov 2025 02:59:36 +0000 (11:59 +0900)
transformJsonFuncExpr() used exprType()/exprLocation() on the
possibly coerced path expression, which could be NULL when
coercion to jsonpath failed, leading to "cache lookup failed
for type 0" errors.

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 <[email protected]>
Author: Jian He <[email protected]>
Reviewed-by: Kirill Reshke <[email protected]>
Discussion: https://postgr.es/m/CACJufxHunVg81JMuNo8Yvv_hJD0DicgaVN2Wteu8aJbVJPBjZA@mail.gmail.com
Backpatch-through: 17

src/backend/parser/parse_expr.c
src/test/regress/expected/sqljson_queryfuncs.out
src/test/regress/sql/sqljson_queryfuncs.sql

index b27e597799be5d356160901f319b39ecce0e47aa..a3356485bea9d01518604432c7ee577cfb5d50be 100644 (file)
@@ -4273,6 +4273,9 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 {
    JsonExpr   *jsexpr;
    Node       *path_spec;
+   Oid         pathspec_type;
+   int         pathspec_loc;
+   Node       *coerced_path_spec;
    const char *func_name = NULL;
    JsonFormatType default_format;
 
@@ -4488,17 +4491,21 @@ 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)
+   pathspec_type = exprType(path_spec);
+   pathspec_loc = exprLocation(path_spec);
+   coerced_path_spec = coerce_to_target_type(pstate, path_spec,
+                                             pathspec_type,
+                                             JSONPATHOID, -1,
+                                             COERCION_EXPLICIT,
+                                             COERCE_IMPLICIT_CAST,
+                                             pathspec_loc);
+   if (coerced_path_spec == NULL)
        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,
index 5a35aeb7bba3afd01a70c728481712299856e483..53145f50f186810a6b02c09ebc58d60c4bdbd640 100644 (file)
@@ -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"
index 8d7b225b612174bdf4290f2f7a005d206c5adbd9..a5d5e256d7ff0c16f1af3f70c984889c2eb1d8a8 100644 (file)
@@ -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);