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

Commit 6f4a00b

Browse files
author
Nikita Glukhov
committed
Allow variable jsonpath specifications
1 parent f51fb8f commit 6f4a00b

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
@@ -2847,7 +2847,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28472847

28482848
APP_JUMB(jexpr->op);
28492849
JumbleExpr(jstate, jexpr->raw_expr);
2850-
JumbleExpr(jstate, (Node *) jexpr->path_spec);
2850+
JumbleExpr(jstate, jexpr->path_spec);
28512851
JumbleExpr(jstate, (Node *) jexpr->passing.values);
28522852
JumbleExpr(jstate, jexpr->on_empty.default_expr);
28532853
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
@@ -2135,6 +2135,13 @@ ExecInitExprRec(Expr *node, ExprState *state,
21352135
&scratch.d.jsonexpr.raw_expr->value,
21362136
&scratch.d.jsonexpr.raw_expr->isnull);
21372137

2138+
scratch.d.jsonexpr.pathspec =
2139+
palloc(sizeof(*scratch.d.jsonexpr.pathspec));
2140+
2141+
ExecInitExprRec((Expr *) jexpr->path_spec, state,
2142+
&scratch.d.jsonexpr.pathspec->value,
2143+
&scratch.d.jsonexpr.pathspec->isnull);
2144+
21382145
scratch.d.jsonexpr.formatted_expr =
21392146
ExecInitExpr((Expr *) jexpr->formatted_expr,
21402147
state->parent);

src/backend/executor/execExprInterp.c

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

4467-
if (op->d.jsonexpr.raw_expr->isnull)
4467+
if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
44684468
{
44694469
/* execute domain checks for NULLs */
44704470
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
@@ -4476,8 +4476,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
44764476
}
44774477

44784478
item = op->d.jsonexpr.raw_expr->value;
4479-
4480-
path = DatumGetJsonPathP(jexpr->path_spec->constvalue);
4479+
path = DatumGetJsonPathP(op->d.jsonexpr.pathspec->value);
44814480

44824481
/* reset JSON path variable contexts */
44834482
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
@@ -2483,7 +2483,7 @@ _copyJsonCommon(const JsonCommon *from)
24832483
JsonCommon *newnode = makeNode(JsonCommon);
24842484

24852485
COPY_NODE_FIELD(expr);
2486-
COPY_STRING_FIELD(pathspec);
2486+
COPY_NODE_FIELD(pathspec);
24872487
COPY_STRING_FIELD(pathname);
24882488
COPY_NODE_FIELD(passing);
24892489
COPY_LOCATION_FIELD(location);

src/backend/nodes/nodeFuncs.c

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

31943194
FLATCOPY(newnode, jexpr, JsonExpr);
3195+
MUTATE(newnode->raw_expr, jexpr->path_spec, Node *);
31953196
MUTATE(newnode->raw_expr, jexpr->raw_expr, Node *);
31963197
MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
31973198
MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
@@ -3963,6 +3964,8 @@ raw_expression_tree_walker(Node *node,
39633964

39643965
if (walker(jc->expr, context))
39653966
return true;
3967+
if (walker(jc->pathspec, context))
3968+
return true;
39663969
if (walker(jc->passing, context))
39673970
return true;
39683971
}

src/backend/parser/gram.y

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
612612
json_aggregate_func
613613
json_object_aggregate_constructor
614614
json_array_aggregate_constructor
615+
json_path_specification
615616

616617
%type <list> json_arguments
617618
json_passing_clause_opt
@@ -621,8 +622,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
621622

622623
%type <typnam> json_returning_clause_opt
623624

624-
%type <str> json_path_specification
625-
json_table_path_name
625+
%type <str> json_table_path_name
626626
json_as_path_name_clause_opt
627627

628628
%type <ival> json_encoding
@@ -827,6 +827,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
827827
*/
828828
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
829829
%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
830+
%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
830831
%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
831832
%left Op OPERATOR /* multi-character ops and user-defined operators */
832833
%left '+' '-'
@@ -14804,7 +14805,7 @@ json_context_item:
1480414805
;
1480514806

1480614807
json_path_specification:
14807-
Sconst { $$ = $1; }
14808+
a_expr { $$ = $1; }
1480814809
;
1480914810

1481014811
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
@@ -4414,7 +4414,7 @@ static JsonExpr *
44144414
transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
44154415
{
44164416
JsonExpr *jsexpr = makeNode(JsonExpr);
4417-
Datum jsonpath;
4417+
Node *pathspec;
44184418
JsonFormatType format;
44194419

44204420
if (func->common->pathname)
@@ -4445,12 +4445,19 @@ transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
44454445

44464446
jsexpr->format = func->common->expr->format;
44474447

4448-
/* parse JSON path string */
4449-
jsonpath = DirectFunctionCall1(jsonpath_in,
4450-
CStringGetDatum(func->common->pathspec));
4448+
pathspec = transformExprRecurse(pstate, func->common->pathspec);
44514449

4452-
jsexpr->path_spec = makeConst(JSONPATHOID, -1, InvalidOid, -1,
4453-
jsonpath, false, false);
4450+
jsexpr->path_spec =
4451+
coerce_to_target_type(pstate, pathspec, exprType(pathspec),
4452+
JSONPATHOID, -1,
4453+
COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
4454+
exprLocation(pathspec));
4455+
if (!jsexpr->path_spec)
4456+
ereport(ERROR,
4457+
(errcode(ERRCODE_DATATYPE_MISMATCH),
4458+
errmsg("JSON path expression must be type %s, not type %s",
4459+
"jsonpath", format_type_be(exprType(pathspec))),
4460+
parser_errposition(pstate, exprLocation(pathspec))));
44544461

44554462
/* transform and coerce to json[b] passing arguments */
44564463
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
@@ -467,6 +467,8 @@ static void add_cast_to(StringInfo buf, Oid typid);
467467
static char *generate_qualified_type_name(Oid typid);
468468
static text *string_to_text(char *str);
469469
static char *flatten_reloptions(Oid relid);
470+
static void get_json_path_spec(Node *path_spec, deparse_context *context,
471+
bool showimplicit);
470472

471473
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
472474

@@ -7676,6 +7678,19 @@ get_rule_expr_paren(Node *node, deparse_context *context,
76767678
appendStringInfoChar(context->buf, ')');
76777679
}
76787680

7681+
7682+
/*
7683+
* get_json_path_spec - Parse back a JSON path specification
7684+
*/
7685+
static void
7686+
get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
7687+
{
7688+
if (IsA(path_spec, Const))
7689+
get_const_expr((Const *) path_spec, context, -1);
7690+
else
7691+
get_rule_expr(path_spec, context, showimplicit);
7692+
}
7693+
76797694
/*
76807695
* get_json_format - Parse back a JsonFormat structure
76817696
*/
@@ -8933,7 +8948,7 @@ get_rule_expr(Node *node, deparse_context *context,
89338948

89348949
appendStringInfoString(buf, ", ");
89358950

8936-
get_const_expr(jexpr->path_spec, context, -1);
8951+
get_json_path_spec(jexpr->path_spec, context, showimplicit);
89378952

89388953
if (jexpr->passing.values)
89398954
{

src/include/executor/execExpr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,8 @@ typedef struct ExprEvalStep
654654
{
655655
Datum value;
656656
bool isnull;
657-
} *raw_expr; /* raw context item value */
657+
} *raw_expr, /* raw context item value */
658+
*pathspec; /* path specification value */
658659

659660
ExprState *formatted_expr; /* formatted context item */
660661
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
@@ -1488,7 +1488,7 @@ typedef struct JsonCommon
14881488
{
14891489
NodeTag type;
14901490
JsonValueExpr *expr; /* context item expression */
1491-
JsonPathSpec pathspec; /* JSON path specification */
1491+
Node *pathspec; /* JSON path specification expression */
14921492
char *pathname; /* path name, if any */
14931493
List *passing; /* list of PASSING clause arguments, if any */
14941494
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
@@ -1324,7 +1324,7 @@ typedef struct JsonExpr
13241324
Node *formatted_expr; /* formatted context item expression */
13251325
JsonCoercion *result_coercion; /* resulting coercion to RETURNING type */
13261326
JsonFormat format; /* context item format (JSON/JSONB) */
1327-
Const *path_spec; /* JSON path specification */
1327+
Node *path_spec; /* JSON path specification expression */
13281328
JsonPassing passing; /* PASSING clause arguments */
13291329
JsonReturning returning; /* RETURNING clause type/format info */
13301330
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+
json_exists
874+
-------------
875+
t
876+
(1 row)
877+
878+
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a');
879+
json_value
880+
------------
881+
123
882+
(1 row)
883+
884+
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
885+
json_value
886+
------------
887+
foo
888+
(1 row)
889+
890+
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
891+
json_query
892+
------------
893+
123
894+
(1 row)
895+
896+
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
897+
json_query
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)