Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 39b5797

Browse files
author
Nikita Glukhov
committed
Allow variable jsonpath specifications
1 parent b321bdf commit 39b5797

File tree

13 files changed

+94
-18
lines changed

13 files changed

+94
-18
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2925,7 +2925,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
29252925

29262926
APP_JUMB(jexpr->op);
29272927
JumbleExpr(jstate, jexpr->raw_expr);
2928-
JumbleExpr(jstate, (Node *) jexpr->path_spec);
2928+
JumbleExpr(jstate, jexpr->path_spec);
29292929
JumbleExpr(jstate, (Node *) jexpr->passing.values);
29302930
JumbleExpr(jstate, jexpr->on_empty.default_expr);
29312931
JumbleExpr(jstate, jexpr->on_error.default_expr);

src/backend/executor/execExpr.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,6 +2128,13 @@ ExecInitExprRec(Expr *node, ExprState *state,
21282128
&scratch.d.jsonexpr.raw_expr->value,
21292129
&scratch.d.jsonexpr.raw_expr->isnull);
21302130

2131+
scratch.d.jsonexpr.pathspec =
2132+
palloc(sizeof(*scratch.d.jsonexpr.pathspec));
2133+
2134+
ExecInitExprRec((Expr *) jexpr->path_spec, state,
2135+
&scratch.d.jsonexpr.pathspec->value,
2136+
&scratch.d.jsonexpr.pathspec->isnull);
2137+
21312138
scratch.d.jsonexpr.formatted_expr =
21322139
ExecInitExpr((Expr *) jexpr->formatted_expr, state->parent);
21332140

src/backend/executor/execExprInterp.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4517,7 +4517,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45174517
*op->resnull = true; /* until we get a result */
45184518
*op->resvalue = (Datum) 0;
45194519

4520-
if (op->d.jsonexpr.raw_expr->isnull)
4520+
if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
45214521
{
45224522
/* execute domain checks for NULLs */
45234523
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
@@ -4529,8 +4529,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45294529
}
45304530

45314531
item = op->d.jsonexpr.raw_expr->value;
4532-
4533-
path = DatumGetJsonPathP(jexpr->path_spec->constvalue);
4532+
path = DatumGetJsonPathP(op->d.jsonexpr.pathspec->value);
45344533

45354534
/* reset JSON path variable contexts */
45364535
foreach(lc, op->d.jsonexpr.args)

src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2498,7 +2498,7 @@ _copyJsonCommon(const JsonCommon *from)
24982498
JsonCommon *newnode = makeNode(JsonCommon);
24992499

25002500
COPY_NODE_FIELD(expr);
2501-
COPY_STRING_FIELD(pathspec);
2501+
COPY_NODE_FIELD(pathspec);
25022502
COPY_STRING_FIELD(pathname);
25032503
COPY_NODE_FIELD(passing);
25042504
COPY_LOCATION_FIELD(location);

src/backend/nodes/nodeFuncs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,7 @@ expression_tree_mutator(Node *node,
32313231
JsonExpr *newnode;
32323232

32333233
FLATCOPY(newnode, jexpr, JsonExpr);
3234+
MUTATE(newnode->raw_expr, jexpr->path_spec, Node *);
32343235
MUTATE(newnode->raw_expr, jexpr->raw_expr, Node *);
32353236
MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
32363237
MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
@@ -4003,6 +4004,8 @@ raw_expression_tree_walker(Node *node,
40034004

40044005
if (walker(jc->expr, context))
40054006
return true;
4007+
if (walker(jc->pathspec, context))
4008+
return true;
40064009
if (walker(jc->passing, context))
40074010
return true;
40084011
}

src/backend/parser/gram.y

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
617617
json_aggregate_func
618618
json_object_aggregate_constructor
619619
json_array_aggregate_constructor
620+
json_path_specification
620621

621622
%type <list> json_arguments
622623
json_passing_clause_opt
@@ -626,8 +627,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
626627

627628
%type <typnam> json_returning_clause_opt
628629

629-
%type <str> json_path_specification
630-
json_table_path_name
630+
%type <str> json_table_path_name
631631
json_as_path_name_clause_opt
632632

633633
%type <ival> json_encoding
@@ -832,6 +832,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
832832
*/
833833
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
834834
%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
835+
%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
835836
%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
836837
%left Op OPERATOR /* multi-character ops and user-defined operators */
837838
%left '+' '-'
@@ -14828,7 +14829,7 @@ json_context_item:
1482814829
;
1482914830

1483014831
json_path_specification:
14831-
Sconst { $$ = $1; }
14832+
a_expr { $$ = $1; }
1483214833
;
1483314834

1483414835
json_as_path_name_clause_opt:

src/backend/parser/parse_expr.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4502,7 +4502,7 @@ static JsonExpr *
45024502
transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
45034503
{
45044504
JsonExpr *jsexpr = makeNode(JsonExpr);
4505-
Datum jsonpath;
4505+
Node *pathspec;
45064506
JsonFormatType format;
45074507

45084508
if (func->common->pathname)
@@ -4533,12 +4533,19 @@ transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
45334533

45344534
jsexpr->format = func->common->expr->format;
45354535

4536-
/* parse JSON path string */
4537-
jsonpath = DirectFunctionCall1(jsonpath_in,
4538-
CStringGetDatum(func->common->pathspec));
4536+
pathspec = transformExprRecurse(pstate, func->common->pathspec);
45394537

4540-
jsexpr->path_spec = makeConst(JSONPATHOID, -1, InvalidOid, -1,
4541-
jsonpath, false, false);
4538+
jsexpr->path_spec =
4539+
coerce_to_target_type(pstate, pathspec, exprType(pathspec),
4540+
JSONPATHOID, -1,
4541+
COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
4542+
exprLocation(pathspec));
4543+
if (!jsexpr->path_spec)
4544+
ereport(ERROR,
4545+
(errcode(ERRCODE_DATATYPE_MISMATCH),
4546+
errmsg("JSON path expression must be type %s, not type %s",
4547+
"jsonpath", format_type_be(exprType(pathspec))),
4548+
parser_errposition(pstate, exprLocation(pathspec))));
45424549

45434550
/* transform and coerce to json[b] passing arguments */
45444551
transformJsonPassingArgs(pstate, format, func->common->passing,

src/backend/utils/adt/ruleutils.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@ static void add_cast_to(StringInfo buf, Oid typid);
469469
static char *generate_qualified_type_name(Oid typid);
470470
static text *string_to_text(char *str);
471471
static char *flatten_reloptions(Oid relid);
472+
static void get_json_path_spec(Node *path_spec, deparse_context *context,
473+
bool showimplicit);
472474

473475
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
474476

@@ -7819,6 +7821,19 @@ get_rule_expr_paren(Node *node, deparse_context *context,
78197821
appendStringInfoChar(context->buf, ')');
78207822
}
78217823

7824+
7825+
/*
7826+
* get_json_path_spec - Parse back a JSON path specification
7827+
*/
7828+
static void
7829+
get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
7830+
{
7831+
if (IsA(path_spec, Const))
7832+
get_const_expr((Const *) path_spec, context, -1);
7833+
else
7834+
get_rule_expr(path_spec, context, showimplicit);
7835+
}
7836+
78227837
/*
78237838
* get_json_format - Parse back a JsonFormat structure
78247839
*/
@@ -9078,7 +9093,7 @@ get_rule_expr(Node *node, deparse_context *context,
90789093

90799094
appendStringInfoString(buf, ", ");
90809095

9081-
get_const_expr(jexpr->path_spec, context, -1);
9096+
get_json_path_spec(jexpr->path_spec, context, showimplicit);
90829097

90839098
if (jexpr->passing.values)
90849099
{

src/include/executor/execExpr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,8 @@ typedef struct ExprEvalStep
672672
{
673673
Datum value;
674674
bool isnull;
675-
} *raw_expr; /* raw context item value */
675+
} *raw_expr, /* raw context item value */
676+
*pathspec; /* path specification value */
676677

677678
ExprState *formatted_expr; /* formatted context item */
678679
ExprState *result_expr; /* coerced to output type */

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,7 @@ typedef struct JsonCommon
15231523
{
15241524
NodeTag type;
15251525
JsonValueExpr *expr; /* context item expression */
1526-
JsonPathSpec pathspec; /* JSON path specification */
1526+
Node *pathspec; /* JSON path specification expression */
15271527
char *pathname; /* path name, if any */
15281528
List *passing; /* list of PASSING clause arguments, if any */
15291529
int location; /* token location, or -1 if unknown */

src/include/nodes/primnodes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,7 @@ typedef struct JsonExpr
13421342
Node *formatted_expr; /* formatted context item expression */
13431343
JsonCoercion *result_coercion; /* resulting coercion to RETURNING type */
13441344
JsonFormat format; /* context item format (JSON/JSONB) */
1345-
Const *path_spec; /* JSON path specification */
1345+
Node *path_spec; /* JSON path specification expression */
13461346
JsonPassing passing; /* PASSING clause arguments */
13471347
JsonReturning returning; /* RETURNING clause type/format info */
13481348
JsonBehavior on_empty; /* ON EMPTY behavior */

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,3 +869,37 @@ INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
869869
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
870870
DETAIL: Failing row contains ({"a": 10}, 1, [1, 2]).
871871
DROP TABLE test_jsonb_constraints;
872+
-- Extension: non-constant JSON path
873+
SELECT JSON_EXISTS(jsonb '{"a": 123}', '$' || '.' || 'a');
874+
json_exists
875+
-------------
876+
t
877+
(1 row)
878+
879+
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a');
880+
json_value
881+
------------
882+
123
883+
(1 row)
884+
885+
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
886+
json_value
887+
------------
888+
foo
889+
(1 row)
890+
891+
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
892+
json_query
893+
------------
894+
123
895+
(1 row)
896+
897+
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
898+
json_query
899+
------------
900+
[123]
901+
(1 row)
902+
903+
-- Should fail (invalid path)
904+
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
905+
ERROR: syntax error, unexpected IDENT_P at or near "error" of jsonpath input

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,12 @@ INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
264264
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
265265

266266
DROP TABLE test_jsonb_constraints;
267+
268+
-- Extension: non-constant JSON path
269+
SELECT JSON_EXISTS(jsonb '{"a": 123}', '$' || '.' || 'a');
270+
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a');
271+
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
272+
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
273+
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
274+
-- Should fail (invalid path)
275+
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');

0 commit comments

Comments
 (0)