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

Commit 0e9f94c

Browse files
author
Nikita Glukhov
committed
Allow variable jsonpath specifications
1 parent d551cee commit 0e9f94c

File tree

13 files changed

+95
-18
lines changed

13 files changed

+95
-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
@@ -2871,7 +2871,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28712871

28722872
APP_JUMB(jexpr->op);
28732873
JumbleExpr(jstate, jexpr->raw_expr);
2874-
JumbleExpr(jstate, (Node *) jexpr->path_spec);
2874+
JumbleExpr(jstate, jexpr->path_spec);
28752875
JumbleExpr(jstate, (Node *) jexpr->passing.values);
28762876
JumbleExpr(jstate, jexpr->on_empty.default_expr);
28772877
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
@@ -2048,6 +2048,13 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
20482048
&scratch.d.jsonexpr.raw_expr->value,
20492049
&scratch.d.jsonexpr.raw_expr->isnull);
20502050

2051+
scratch.d.jsonexpr.pathspec =
2052+
palloc(sizeof(*scratch.d.jsonexpr.pathspec));
2053+
2054+
ExecInitExprRec((Expr *) jexpr->path_spec, parent, state,
2055+
&scratch.d.jsonexpr.pathspec->value,
2056+
&scratch.d.jsonexpr.pathspec->isnull);
2057+
20512058
scratch.d.jsonexpr.formatted_expr =
20522059
ExecInitExpr((Expr *) jexpr->formatted_expr, parent);
20532060

src/backend/executor/execExprInterp.c

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

3931-
if (op->d.jsonexpr.raw_expr->isnull)
3931+
if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
39323932
{
39333933
/* execute domain checks for NULLs */
39343934
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
@@ -3940,8 +3940,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
39403940
}
39413941

39423942
item = op->d.jsonexpr.raw_expr->value;
3943-
3944-
path = DatumGetJsonPathP(jexpr->path_spec->constvalue);
3943+
path = DatumGetJsonPathP(op->d.jsonexpr.pathspec->value);
39453944

39463945
/* reset JSON path variable contexts */
39473946
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
@@ -2420,7 +2420,7 @@ _copyJsonCommon(const JsonCommon *from)
24202420
JsonCommon *newnode = makeNode(JsonCommon);
24212421

24222422
COPY_NODE_FIELD(expr);
2423-
COPY_STRING_FIELD(pathspec);
2423+
COPY_NODE_FIELD(pathspec);
24242424
COPY_STRING_FIELD(pathname);
24252425
COPY_NODE_FIELD(passing);
24262426
COPY_LOCATION_FIELD(location);

src/backend/nodes/nodeFuncs.c

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

31683168
FLATCOPY(newnode, jexpr, JsonExpr);
3169+
MUTATE(newnode->raw_expr, jexpr->path_spec, Node *);
31693170
MUTATE(newnode->raw_expr, jexpr->raw_expr, Node *);
31703171
MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
31713172
MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
@@ -3934,6 +3935,8 @@ raw_expression_tree_walker(Node *node,
39343935

39353936
if (walker(jc->expr, context))
39363937
return true;
3938+
if (walker(jc->pathspec, context))
3939+
return true;
39373940
if (walker(jc->passing, context))
39383941
return true;
39393942
}

src/backend/parser/gram.y

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
606606
json_aggregate_func
607607
json_object_aggregate_constructor
608608
json_array_aggregate_constructor
609+
json_path_specification
609610

610611
%type <list> json_arguments
611612
json_passing_clause_opt
@@ -615,8 +616,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
615616

616617
%type <typnam> json_returning_clause_opt
617618

618-
%type <str> json_path_specification
619-
json_table_path_name
619+
%type <str> json_table_path_name
620620
json_as_path_name_clause_opt
621621

622622
%type <ival> json_encoding
@@ -818,6 +818,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
818818
*/
819819
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
820820
%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
821+
%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
821822
%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP
822823
%left Op OPERATOR /* multi-character ops and user-defined operators */
823824
%left '+' '-'
@@ -14397,7 +14398,7 @@ json_context_item:
1439714398
;
1439814399

1439914400
json_path_specification:
14400-
Sconst { $$ = $1; }
14401+
a_expr { $$ = $1; }
1440114402
;
1440214403

1440314404
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
@@ -4395,7 +4395,7 @@ static JsonExpr *
43954395
transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
43964396
{
43974397
JsonExpr *jsexpr = makeNode(JsonExpr);
4398-
Datum jsonpath;
4398+
Node *pathspec;
43994399
JsonFormatType format;
44004400

44014401
if (func->common->pathname)
@@ -4426,12 +4426,19 @@ transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
44264426

44274427
jsexpr->format = func->common->expr->format;
44284428

4429-
/* parse JSON path string */
4430-
jsonpath = DirectFunctionCall1(jsonpath_in,
4431-
CStringGetDatum(func->common->pathspec));
4429+
pathspec = transformExprRecurse(pstate, func->common->pathspec);
44324430

4433-
jsexpr->path_spec = makeConst(JSONPATHOID, -1, InvalidOid, -1,
4434-
jsonpath, false, false);
4431+
jsexpr->path_spec =
4432+
coerce_to_target_type(pstate, pathspec, exprType(pathspec),
4433+
JSONPATHOID, -1,
4434+
COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
4435+
exprLocation(pathspec));
4436+
if (!jsexpr->path_spec)
4437+
ereport(ERROR,
4438+
(errcode(ERRCODE_DATATYPE_MISMATCH),
4439+
errmsg("JSON path expression must be type %s, not type %s",
4440+
"jsonpath", format_type_be(exprType(pathspec))),
4441+
parser_errposition(pstate, exprLocation(pathspec))));
44354442

44364443
/* transform and coerce to json[b] passing arguments */
44374444
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
@@ -462,6 +462,8 @@ static char *generate_function_name(Oid funcid, int nargs,
462462
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
463463
static text *string_to_text(char *str);
464464
static char *flatten_reloptions(Oid relid);
465+
static void get_json_path_spec(Node *path_spec, deparse_context *context,
466+
bool showimplicit);
465467

466468
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
467469

@@ -7585,6 +7587,19 @@ get_rule_expr_paren(Node *node, deparse_context *context,
75857587
appendStringInfoChar(context->buf, ')');
75867588
}
75877589

7590+
7591+
/*
7592+
* get_json_path_spec - Parse back a JSON path specification
7593+
*/
7594+
static void
7595+
get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
7596+
{
7597+
if (IsA(path_spec, Const))
7598+
get_const_expr((Const *) path_spec, context, -1);
7599+
else
7600+
get_rule_expr(path_spec, context, showimplicit);
7601+
}
7602+
75887603
/*
75897604
* get_json_format - Parse back a JsonFormat structure
75907605
*/
@@ -8825,7 +8840,7 @@ get_rule_expr(Node *node, deparse_context *context,
88258840

88268841
appendStringInfoString(buf, ", ");
88278842

8828-
get_const_expr(jexpr->path_spec, context, -1);
8843+
get_json_path_spec(jexpr->path_spec, context, showimplicit);
88298844

88308845
if (jexpr->passing.values)
88318846
{

src/include/executor/execExpr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,8 @@ typedef struct ExprEvalStep
578578
{
579579
Datum value;
580580
bool isnull;
581-
} *raw_expr; /* raw context item value */
581+
} *raw_expr, /* raw context item value */
582+
*pathspec; /* path specification value */
582583

583584
ExprState *formatted_expr; /* formatted context item */
584585
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
@@ -1462,7 +1462,7 @@ typedef struct JsonCommon
14621462
{
14631463
NodeTag type;
14641464
JsonValueExpr *expr; /* context item expression */
1465-
JsonPathSpec pathspec; /* JSON path specification */
1465+
Node *pathspec; /* JSON path specification expression */
14661466
char *pathname; /* path name, if any */
14671467
List *passing; /* list of PASSING clause arguments, if any */
14681468
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
@@ -1317,7 +1317,7 @@ typedef struct JsonExpr
13171317
Node *formatted_expr; /* formatted context item expression */
13181318
JsonCoercion *result_coercion; /* resulting coercion to RETURNING type */
13191319
JsonFormat format; /* context item format (JSON/JSONB) */
1320-
Const *path_spec; /* JSON path specification */
1320+
Node *path_spec; /* JSON path specification expression */
13211321
JsonPassing passing; /* PASSING clause arguments */
13221322
JsonReturning returning; /* RETURNING clause type/format info */
13231323
JsonBehavior on_empty; /* ON EMPTY behavior */

src/test/regress/expected/jsonb_sqljson.out

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

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)