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

Commit 7247297

Browse files
author
Nikita Glukhov
committed
Allow variable jsonpath specifications
1 parent 5070aad commit 7247297

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

31413141
APP_JUMB(jexpr->op);
31423142
JumbleExpr(jstate, jexpr->raw_expr);
3143-
JumbleExpr(jstate, (Node *) jexpr->path_spec);
3143+
JumbleExpr(jstate, jexpr->path_spec);
31443144
foreach(temp, jexpr->passing_names)
31453145
{
31463146
APP_JUMB_STRING(castNode(Value, temp)->val.str);

src/backend/executor/execExpr.c

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

2226+
scratch.d.jsonexpr.pathspec =
2227+
palloc(sizeof(*scratch.d.jsonexpr.pathspec));
2228+
2229+
ExecInitExprRec((Expr *) jexpr->path_spec, state,
2230+
&scratch.d.jsonexpr.pathspec->value,
2231+
&scratch.d.jsonexpr.pathspec->isnull);
2232+
22262233
scratch.d.jsonexpr.formatted_expr =
22272234
ExecInitExpr((Expr *) jexpr->formatted_expr, state->parent);
22282235

src/backend/executor/execExprInterp.c

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

4835-
if (op->d.jsonexpr.raw_expr->isnull)
4835+
if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
48364836
{
48374837
/* execute domain checks for NULLs */
48384838
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
@@ -4844,8 +4844,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
48444844
}
48454845

48464846
item = op->d.jsonexpr.raw_expr->value;
4847-
4848-
path = DatumGetJsonPathP(jexpr->path_spec->constvalue);
4847+
path = DatumGetJsonPathP(op->d.jsonexpr.pathspec->value);
48494848

48504849
/* reset JSON path variable contexts */
48514850
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
@@ -2565,7 +2565,7 @@ _copyJsonCommon(const JsonCommon *from)
25652565
JsonCommon *newnode = makeNode(JsonCommon);
25662566

25672567
COPY_NODE_FIELD(expr);
2568-
COPY_STRING_FIELD(pathspec);
2568+
COPY_NODE_FIELD(pathspec);
25692569
COPY_STRING_FIELD(pathname);
25702570
COPY_NODE_FIELD(passing);
25712571
COPY_LOCATION_FIELD(location);

src/backend/nodes/nodeFuncs.c

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

34073407
FLATCOPY(newnode, jexpr, JsonExpr);
3408+
MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
34083409
MUTATE(newnode->raw_expr, jexpr->raw_expr, Node *);
34093410
MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
34103411
MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
@@ -4264,6 +4265,8 @@ raw_expression_tree_walker(Node *node,
42644265

42654266
if (walker(jc->expr, context))
42664267
return true;
4268+
if (walker(jc->pathspec, context))
4269+
return true;
42674270
if (walker(jc->passing, context))
42684271
return true;
42694272
}

src/backend/parser/gram.y

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -629,15 +629,15 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
629629
json_aggregate_func
630630
json_object_aggregate_constructor
631631
json_array_aggregate_constructor
632+
json_path_specification
632633

633634
%type <list> json_name_and_value_list
634635
json_value_expr_list
635636
json_array_aggregate_order_by_clause_opt
636637
json_arguments
637638
json_passing_clause_opt
638639

639-
%type <str> json_path_specification
640-
json_table_path_name
640+
%type <str> json_table_path_name
641641
json_as_path_name_clause_opt
642642

643643
%type <ival> json_encoding
@@ -843,6 +843,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
843843
*/
844844
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
845845
%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
846+
%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
846847
%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
847848
%left Op OPERATOR /* multi-character ops and user-defined operators */
848849
%left '+' '-'
@@ -14831,7 +14832,7 @@ json_context_item:
1483114832
;
1483214833

1483314834
json_path_specification:
14834-
Sconst { $$ = $1; }
14835+
a_expr { $$ = $1; }
1483514836
;
1483614837

1483714838
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
@@ -4535,7 +4535,7 @@ static JsonExpr *
45354535
transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
45364536
{
45374537
JsonExpr *jsexpr = makeNode(JsonExpr);
4538-
Datum jsonpath;
4538+
Node *pathspec;
45394539
JsonFormatType format;
45404540

45414541
if (func->common->pathname)
@@ -4566,12 +4566,19 @@ transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
45664566

45674567
jsexpr->format = func->common->expr->format;
45684568

4569-
/* parse JSON path string */
4570-
jsonpath = DirectFunctionCall1(jsonpath_in,
4571-
CStringGetDatum(func->common->pathspec));
4569+
pathspec = transformExprRecurse(pstate, func->common->pathspec);
45724570

4573-
jsexpr->path_spec = makeConst(JSONPATHOID, -1, InvalidOid, -1,
4574-
jsonpath, false, false);
4571+
jsexpr->path_spec =
4572+
coerce_to_target_type(pstate, pathspec, exprType(pathspec),
4573+
JSONPATHOID, -1,
4574+
COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
4575+
exprLocation(pathspec));
4576+
if (!jsexpr->path_spec)
4577+
ereport(ERROR,
4578+
(errcode(ERRCODE_DATATYPE_MISMATCH),
4579+
errmsg("JSON path expression must be type %s, not type %s",
4580+
"jsonpath", format_type_be(exprType(pathspec))),
4581+
parser_errposition(pstate, exprLocation(pathspec))));
45754582

45764583
/* transform and coerce to json[b] passing arguments */
45774584
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
@@ -487,6 +487,8 @@ static char *generate_qualified_type_name(Oid typid);
487487
static text *string_to_text(char *str);
488488
static char *flatten_reloptions(Oid relid);
489489
static void get_reloptions(StringInfo buf, Datum reloptions);
490+
static void get_json_path_spec(Node *path_spec, deparse_context *context,
491+
bool showimplicit);
490492

491493
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
492494

@@ -7909,6 +7911,19 @@ get_rule_expr_paren(Node *node, deparse_context *context,
79097911
appendStringInfoChar(context->buf, ')');
79107912
}
79117913

7914+
7915+
/*
7916+
* get_json_path_spec - Parse back a JSON path specification
7917+
*/
7918+
static void
7919+
get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
7920+
{
7921+
if (IsA(path_spec, Const))
7922+
get_const_expr((Const *) path_spec, context, -1);
7923+
else
7924+
get_rule_expr(path_spec, context, showimplicit);
7925+
}
7926+
79127927
/*
79137928
* get_json_format - Parse back a JsonFormat node
79147929
*/
@@ -9271,7 +9286,7 @@ get_rule_expr(Node *node, deparse_context *context,
92719286

92729287
appendStringInfoString(buf, ", ");
92739288

9274-
get_const_expr(jexpr->path_spec, context, -1);
9289+
get_json_path_spec(jexpr->path_spec, context, showimplicit);
92759290

92769291
if (jexpr->passing_values)
92779292
{

src/include/executor/execExpr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,8 @@ typedef struct ExprEvalStep
676676
{
677677
Datum value;
678678
bool isnull;
679-
} *raw_expr; /* raw context item value */
679+
} *raw_expr, /* raw context item value */
680+
*pathspec; /* path specification value */
680681

681682
ExprState *formatted_expr; /* formatted context item */
682683
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
@@ -1535,7 +1535,7 @@ typedef struct JsonCommon
15351535
{
15361536
NodeTag type;
15371537
JsonValueExpr *expr; /* context item expression */
1538-
JsonPathSpec pathspec; /* JSON path specification */
1538+
Node *pathspec; /* JSON path specification expression */
15391539
char *pathname; /* path name, if any */
15401540
List *passing; /* list of PASSING clause arguments, if any */
15411541
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
@@ -1394,7 +1394,7 @@ typedef struct JsonExpr
13941394
Node *formatted_expr; /* formatted context item expression */
13951395
JsonCoercion *result_coercion; /* resulting coercion to RETURNING type */
13961396
JsonFormat *format; /* context item format (JSON/JSONB) */
1397-
Const *path_spec; /* JSON path specification */
1397+
Node *path_spec; /* JSON path specification expression */
13981398
List *passing_names; /* PASSING argument names */
13991399
List *passing_values; /* PASSING argument values */
14001400
JsonReturning *returning; /* RETURNING clause type/format info */

src/test/regress/expected/jsonb_sqljson.out

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

src/test/regress/sql/jsonb_sqljson.sql

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

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

0 commit comments

Comments
 (0)