Skip to content

Commit b4fad46

Browse files
committed
SQL/JSON: Improve some error messages
This improves some error messages emitted by SQL/JSON query functions by mentioning column name when available, such as when they are invoked as part of evaluating JSON_TABLE() columns. To do so, a new field column_name is added to both JsonFuncExpr and JsonExpr that is only populated when creating those nodes for transformed JSON_TABLE() columns. While at it, relevant error messages are reworded for clarity. Reported-by: Jian He <jian.universality@gmail.com> Suggested-by: Jian He <jian.universality@gmail.com> Discussion: https://postgr.es/m/CACJufxG_e0QLCgaELrr2ZNz7AxPeGCNKAORe3fHtFCQLsH4J4Q@mail.gmail.com
1 parent 40126ac commit b4fad46

File tree

10 files changed

+96
-57
lines changed

10 files changed

+96
-57
lines changed

src/backend/executor/execExprInterp.c

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4312,7 +4312,8 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
43124312
case JSON_QUERY_OP:
43134313
*op->resvalue = JsonPathQuery(item, path, jsexpr->wrapper, &empty,
43144314
!throw_error ? &error : NULL,
4315-
jsestate->args);
4315+
jsestate->args,
4316+
jsexpr->column_name);
43164317

43174318
*op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
43184319

@@ -4337,7 +4338,8 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
43374338
{
43384339
JsonbValue *jbv = JsonPathValue(item, path, &empty,
43394340
!throw_error ? &error : NULL,
4340-
jsestate->args);
4341+
jsestate->args,
4342+
jsexpr->column_name);
43414343

43424344
if (jbv == NULL)
43434345
{
@@ -4407,30 +4409,33 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
44074409
/* Handle ON EMPTY. */
44084410
if (empty)
44094411
{
4412+
*op->resvalue = (Datum) 0;
4413+
*op->resnull = true;
44104414
if (jsexpr->on_empty)
44114415
{
4412-
if (jsexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
4413-
ereport(ERROR,
4414-
errcode(ERRCODE_NO_SQL_JSON_ITEM),
4415-
errmsg("no SQL/JSON item"));
4416-
else
4416+
if (jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
4417+
{
44174418
jsestate->empty.value = BoolGetDatum(true);
4418-
4419-
Assert(jsestate->jump_empty >= 0);
4420-
return jsestate->jump_empty;
4419+
Assert(jsestate->jump_empty >= 0);
4420+
return jsestate->jump_empty;
4421+
}
4422+
}
4423+
else if (jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
4424+
{
4425+
jsestate->error.value = BoolGetDatum(true);
4426+
Assert(!throw_error && jsestate->jump_error >= 0);
4427+
return jsestate->jump_error;
44214428
}
4422-
else if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
4429+
4430+
if (jsexpr->column_name)
44234431
ereport(ERROR,
44244432
errcode(ERRCODE_NO_SQL_JSON_ITEM),
4425-
errmsg("no SQL/JSON item"));
4433+
errmsg("no SQL/JSON item found for specified path of column \"%s\"",
4434+
jsexpr->column_name));
44264435
else
4427-
jsestate->error.value = BoolGetDatum(true);
4428-
4429-
*op->resvalue = (Datum) 0;
4430-
*op->resnull = true;
4431-
4432-
Assert(!throw_error && jsestate->jump_error >= 0);
4433-
return jsestate->jump_error;
4436+
ereport(ERROR,
4437+
errcode(ERRCODE_NO_SQL_JSON_ITEM),
4438+
errmsg("no SQL/JSON item found for specified path"));
44344439
}
44354440

44364441
/*

src/backend/parser/parse_expr.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4311,6 +4311,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
43114311
jsexpr = makeNode(JsonExpr);
43124312
jsexpr->location = func->location;
43134313
jsexpr->op = func->op;
4314+
jsexpr->column_name = func->column_name;
43144315

43154316
/*
43164317
* jsonpath machinery can only handle jsonb documents, so coerce the input

src/backend/parser/parse_jsontable.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -402,19 +402,17 @@ transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
402402
Node *pathspec;
403403
JsonFuncExpr *jfexpr = makeNode(JsonFuncExpr);
404404

405-
/*
406-
* XXX consider inventing JSON_TABLE_VALUE_OP, etc. and pass the column
407-
* name via JsonExpr so that JsonPathValue(), etc. can provide error
408-
* message tailored to JSON_TABLE(), such as by mentioning the column
409-
* names in the message.
410-
*/
411405
if (jtc->coltype == JTC_REGULAR)
412406
jfexpr->op = JSON_VALUE_OP;
413407
else if (jtc->coltype == JTC_EXISTS)
414408
jfexpr->op = JSON_EXISTS_OP;
415409
else
416410
jfexpr->op = JSON_QUERY_OP;
417411

412+
/* Pass the column name so any runtime JsonExpr errors can print it. */
413+
Assert(jtc->name != NULL);
414+
jfexpr->column_name = pstrdup(jtc->name);
415+
418416
jfexpr->context_item = makeJsonValueExpr((Expr *) contextItemExpr, NULL,
419417
makeJsonFormat(JS_FORMAT_DEFAULT,
420418
JS_ENC_DEFAULT,

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3899,7 +3899,8 @@ JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
38993899
*/
39003900
Datum
39013901
JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
3902-
bool *error, List *vars)
3902+
bool *error, List *vars,
3903+
const char *column_name)
39033904
{
39043905
JsonbValue *singleton;
39053906
bool wrap;
@@ -3950,10 +3951,17 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
39503951
return (Datum) 0;
39513952
}
39523953

3953-
ereport(ERROR,
3954-
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3955-
errmsg("JSON path expression in JSON_QUERY should return singleton item without wrapper"),
3956-
errhint("Use WITH WRAPPER clause to wrap SQL/JSON item sequence into array.")));
3954+
if (column_name)
3955+
ereport(ERROR,
3956+
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3957+
errmsg("JSON path expression for column \"%s\" should return single item without wrapper",
3958+
column_name),
3959+
errhint("Use WITH WRAPPER clause to wrap SQL/JSON items into array.")));
3960+
else
3961+
ereport(ERROR,
3962+
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3963+
errmsg("JSON path expression in JSON_QUERY should return single item without wrapper"),
3964+
errhint("Use WITH WRAPPER clause to wrap SQL/JSON items into array.")));
39573965
}
39583966

39593967
if (singleton)
@@ -3970,7 +3978,8 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
39703978
* *error to true. *empty is set to true if no match is found.
39713979
*/
39723980
JsonbValue *
3973-
JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
3981+
JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
3982+
const char *column_name)
39743983
{
39753984
JsonbValue *res;
39763985
JsonValueList found = {0};
@@ -4006,9 +4015,15 @@ JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
40064015
return NULL;
40074016
}
40084017

4009-
ereport(ERROR,
4010-
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4011-
errmsg("JSON path expression in JSON_VALUE should return singleton scalar item")));
4018+
if (column_name)
4019+
ereport(ERROR,
4020+
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4021+
errmsg("JSON path expression for column \"%s\" should return single scalar item",
4022+
column_name)));
4023+
else
4024+
ereport(ERROR,
4025+
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4026+
errmsg("JSON path expression in JSON_VALUE should return single scalar item")));
40124027
}
40134028

40144029
res = JsonValueListHead(&found);
@@ -4024,9 +4039,15 @@ JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
40244039
return NULL;
40254040
}
40264041

4027-
ereport(ERROR,
4028-
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4029-
errmsg("JSON path expression in JSON_VALUE should return singleton scalar item")));
4042+
if (column_name)
4043+
ereport(ERROR,
4044+
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4045+
errmsg("JSON path expression for column \"%s\" should return single scalar item",
4046+
column_name)));
4047+
else
4048+
ereport(ERROR,
4049+
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4050+
errmsg("JSON path expression in JSON_VALUE should return single scalar item")));
40304051
}
40314052

40324053
if (res->type == jbvNull)

src/include/nodes/parsenodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,8 @@ typedef struct JsonFuncExpr
17911791
{
17921792
NodeTag type;
17931793
JsonExprOp op; /* expression type */
1794+
char *column_name; /* JSON_TABLE() column name or NULL if this is
1795+
* not for a JSON_TABLE() */
17941796
JsonValueExpr *context_item; /* context item expression */
17951797
Node *pathspec; /* JSON path specification expression */
17961798
List *passing; /* list of PASSING clause arguments, if any */

src/include/nodes/primnodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,9 @@ typedef struct JsonExpr
17821782

17831783
JsonExprOp op;
17841784

1785+
char *column_name; /* JSON_TABLE() column name or NULL if this is
1786+
* not for a JSON_TABLE() */
1787+
17851788
/* jsonb-valued expression to query */
17861789
Node *formatted_expr;
17871790

src/include/utils/jsonpath.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,11 @@ typedef struct JsonPathVariable
300300
/* SQL/JSON item */
301301
extern bool JsonPathExists(Datum jb, JsonPath *path, bool *error, List *vars);
302302
extern Datum JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
303-
bool *empty, bool *error, List *vars);
303+
bool *empty, bool *error, List *vars,
304+
const char *column_name);
304305
extern JsonbValue *JsonPathValue(Datum jb, JsonPath *jp, bool *empty,
305-
bool *error, List *vars);
306+
bool *error, List *vars,
307+
const char *column_name);
306308

307309
extern PGDLLIMPORT const TableFuncRoutine JsonbTableRoutine;
308310

src/test/regress/expected/sqljson_jsontable.out

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,11 +492,11 @@ FROM
492492
ON true;
493493
ERROR: invalid input syntax for type integer: "err"
494494
SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH '$.a' ERROR ON EMPTY)) jt;
495-
ERROR: no SQL/JSON item
495+
ERROR: no SQL/JSON item found for specified path of column "a"
496496
SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH 'strict $.a' ERROR ON ERROR) ERROR ON ERROR) jt;
497497
ERROR: jsonpath member accessor can only be applied to an object
498498
SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH 'lax $.a' ERROR ON EMPTY) ERROR ON ERROR) jt;
499-
ERROR: no SQL/JSON item
499+
ERROR: no SQL/JSON item found for specified path of column "a"
500500
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int PATH '$' DEFAULT 1 ON EMPTY DEFAULT 2 ON ERROR)) jt;
501501
a
502502
---
@@ -637,6 +637,10 @@ SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || 'a' COLUMNS (foo int)
637637
ERROR: only string constants are supported in JSON_TABLE path specification
638638
LINE 1: SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || '...
639639
^
640+
-- JsonPathQuery() error message mentioning column name
641+
SELECT * FROM JSON_TABLE('{"a": [{"b": "1"}, {"b": "2"}]}', '$' COLUMNS (b json path '$.a[*].b' ERROR ON ERROR));
642+
ERROR: JSON path expression for column "b" should return single item without wrapper
643+
HINT: Use WITH WRAPPER clause to wrap SQL/JSON items into array.
640644
-- JSON_TABLE: nested paths
641645
-- Duplicate path names
642646
SELECT * FROM JSON_TABLE(
@@ -849,7 +853,7 @@ SELECT sub.* FROM s,
849853
xx int path '$.c',
850854
NESTED PATH '$.a.za[1]' columns (NESTED PATH '$.z21[*]' COLUMNS (z21 int path '$?(@ >= $"x")' ERROR ON ERROR))
851855
)) sub;
852-
ERROR: no SQL/JSON item
856+
ERROR: no SQL/JSON item found for specified path of column "z21"
853857
-- Parent columns xx1, xx appear before NESTED ones
854858
SELECT sub.* FROM s,
855859
(VALUES (23)) x(x), generate_series(13, 13) y,

src/test/regress/expected/sqljson_queryfuncs.out

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -339,15 +339,15 @@ SELECT JSON_VALUE(jsonb '[]', '$');
339339
(1 row)
340340

341341
SELECT JSON_VALUE(jsonb '[]', '$' ERROR ON ERROR);
342-
ERROR: JSON path expression in JSON_VALUE should return singleton scalar item
342+
ERROR: JSON path expression in JSON_VALUE should return single scalar item
343343
SELECT JSON_VALUE(jsonb '{}', '$');
344344
json_value
345345
------------
346346

347347
(1 row)
348348

349349
SELECT JSON_VALUE(jsonb '{}', '$' ERROR ON ERROR);
350-
ERROR: JSON path expression in JSON_VALUE should return singleton scalar item
350+
ERROR: JSON path expression in JSON_VALUE should return single scalar item
351351
SELECT JSON_VALUE(jsonb '1', '$.a');
352352
json_value
353353
------------
@@ -363,9 +363,9 @@ SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 'error' ON ERROR);
363363
(1 row)
364364

365365
SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON ERROR);
366-
ERROR: no SQL/JSON item
366+
ERROR: no SQL/JSON item found for specified path
367367
SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY ERROR ON ERROR);
368-
ERROR: no SQL/JSON item
368+
ERROR: no SQL/JSON item found for specified path
369369
SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 2 ON ERROR);
370370
json_value
371371
------------
@@ -397,9 +397,9 @@ SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON EMPTY DEFAULT '3' ON ERROR
397397
(1 row)
398398

399399
SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY DEFAULT '3' ON ERROR);
400-
ERROR: no SQL/JSON item
400+
ERROR: no SQL/JSON item found for specified path
401401
SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
402-
ERROR: JSON path expression in JSON_VALUE should return singleton scalar item
402+
ERROR: JSON path expression in JSON_VALUE should return single scalar item
403403
SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' DEFAULT '0' ON ERROR);
404404
json_value
405405
------------
@@ -758,26 +758,26 @@ SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY OBJECT ON EMPTY);
758758
(1 row)
759759

760760
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY);
761-
ERROR: no SQL/JSON item
761+
ERROR: no SQL/JSON item found for specified path
762762
SELECT JSON_QUERY(jsonb '[]', '$[*]' DEFAULT '"empty"' ON EMPTY);
763763
json_query
764764
------------
765765
"empty"
766766
(1 row)
767767

768768
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY NULL ON ERROR);
769-
ERROR: no SQL/JSON item
769+
ERROR: no SQL/JSON item found for specified path
770770
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY ARRAY ON ERROR);
771-
ERROR: no SQL/JSON item
771+
ERROR: no SQL/JSON item found for specified path
772772
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY OBJECT ON ERROR);
773-
ERROR: no SQL/JSON item
773+
ERROR: no SQL/JSON item found for specified path
774774
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY ERROR ON ERROR);
775-
ERROR: no SQL/JSON item
775+
ERROR: no SQL/JSON item found for specified path
776776
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON ERROR);
777-
ERROR: no SQL/JSON item
777+
ERROR: no SQL/JSON item found for specified path
778778
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
779-
ERROR: JSON path expression in JSON_QUERY should return singleton item without wrapper
780-
HINT: Use WITH WRAPPER clause to wrap SQL/JSON item sequence into array.
779+
ERROR: JSON path expression in JSON_QUERY should return single item without wrapper
780+
HINT: Use WITH WRAPPER clause to wrap SQL/JSON items into array.
781781
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' DEFAULT '"empty"' ON ERROR);
782782
json_query
783783
------------
@@ -1033,7 +1033,7 @@ SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
10331033
(1 row)
10341034

10351035
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null ERROR ON ERROR);
1036-
ERROR: no SQL/JSON item
1036+
ERROR: no SQL/JSON item found for specified path
10371037
-- Test timestamptz passing and output
10381038
SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
10391039
json_query

src/test/regress/sql/sqljson_jsontable.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ FROM JSON_TABLE(
290290
-- Should fail (not supported)
291291
SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || 'a' COLUMNS (foo int));
292292

293+
-- JsonPathQuery() error message mentioning column name
294+
SELECT * FROM JSON_TABLE('{"a": [{"b": "1"}, {"b": "2"}]}', '$' COLUMNS (b json path '$.a[*].b' ERROR ON ERROR));
295+
293296
-- JSON_TABLE: nested paths
294297

295298
-- Duplicate path names

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy