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

Commit 2bf616b

Browse files
author
Nikita Glukhov
committed
Allow jsonpath parenthized expressions to be a base part of a path
1 parent e847f81 commit 2bf616b

File tree

7 files changed

+106
-16
lines changed

7 files changed

+106
-16
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,13 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
741741
v->type == jpiRoot ||
742742
v->type == jpiVariable ||
743743
v->type == jpiLast ||
744+
v->type == jpiAdd ||
745+
v->type == jpiSub ||
746+
v->type == jpiMul ||
747+
v->type == jpiDiv ||
748+
v->type == jpiMod ||
749+
v->type == jpiPlus ||
750+
v->type == jpiMinus ||
744751
v->type == jpiType ||
745752
v->type == jpiSize ||
746753
v->type == jpiAbs ||

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
691691
Datum ldatum;
692692
Datum rdatum;
693693
Datum res;
694+
bool hasNext;
694695

695696
jspGetLeftArg(jsp, &elem);
696697

@@ -722,7 +723,9 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
722723
if (rval->type != jbvNumeric)
723724
return jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
724725

725-
if (!found)
726+
hasNext = jspGetNext(jsp, &elem);
727+
728+
if (!found && !hasNext)
726729
return jperOk;
727730

728731
ldatum = NumericGetDatum(lval->val.numeric);
@@ -753,6 +756,9 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
753756
lval->type = jbvNumeric;
754757
lval->val.numeric = DatumGetNumeric(res);
755758

759+
if (hasNext)
760+
return recursiveExecute(cxt, &elem, lval, found);
761+
756762
*found = lappend(*found, lval);
757763

758764
return jperOk;
@@ -766,6 +772,7 @@ executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
766772
JsonPathItem elem;
767773
List *seq = NIL;
768774
ListCell *lc;
775+
bool hasNext;
769776

770777
jspGetArg(jsp, &elem);
771778
jper = recursiveExecuteAndUnwrap(cxt, &elem, jb, &seq);
@@ -775,6 +782,8 @@ executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
775782

776783
jper = jperNotFound;
777784

785+
hasNext = jspGetNext(jsp, &elem);
786+
778787
foreach(lc, seq)
779788
{
780789
JsonbValue *val = lfirst(lc);
@@ -785,12 +794,10 @@ executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
785794

786795
if (val->type == jbvNumeric)
787796
{
788-
jper = jperOk;
789-
790-
if (!found)
791-
return jper;
797+
if (!found && !hasNext)
798+
return jperOk;
792799
}
793-
else if (!found)
800+
else if (!found && !hasNext)
794801
continue; /* skip non-numerics processing */
795802

796803
if (val->type != jbvNumeric)
@@ -811,7 +818,26 @@ executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
811818
elog(ERROR, "unknown jsonpath arithmetic operation %d", jsp->type);
812819
}
813820

814-
*found = lappend(*found, val);
821+
if (hasNext)
822+
{
823+
JsonPathExecResult jper2 = recursiveExecute(cxt, &elem, val, found);
824+
825+
if (jperIsError(jper2))
826+
return jper2;
827+
828+
if (jper2 == jperOk)
829+
{
830+
if (!found)
831+
return jperOk;
832+
jper = jperOk;
833+
}
834+
}
835+
else
836+
{
837+
Assert(found);
838+
*found = lappend(*found, val);
839+
jper = jperOk;
840+
}
815841
}
816842

817843
return jper;

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,23 @@ makeItemUnary(int type, JsonPathParseItem* a)
142142
}
143143

144144
static JsonPathParseItem*
145-
makeItemList(List *list) {
146-
JsonPathParseItem *head, *end;
147-
ListCell *cell;
145+
makeItemList(List *list)
146+
{
147+
JsonPathParseItem *head, *end;
148+
ListCell *cell = list_head(list);
148149

149-
head = end = (JsonPathParseItem*)linitial(list);
150+
head = end = (JsonPathParseItem *) lfirst(cell);
150151

151-
foreach(cell, list)
152-
{
153-
JsonPathParseItem *c = (JsonPathParseItem*)lfirst(cell);
152+
if (!lnext(cell))
153+
return head;
154154

155-
if (c == head)
156-
continue;
155+
/* append items to the end of already existing list */
156+
while (end->next)
157+
end = end->next;
158+
159+
for_each_cell(cell, lnext(cell))
160+
{
161+
JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
157162

158163
end->next = c;
159164
end = c;
@@ -359,6 +364,7 @@ path_primary:
359364
accessor_expr:
360365
path_primary { $$ = list_make1($1); }
361366
| '.' key { $$ = list_make2(makeItemType(jpiCurrent), $2); }
367+
| '(' expr ')' accessor_op { $$ = list_make2($2, $4); }
362368
| accessor_expr accessor_op { $$ = lappend($1, $2); }
363369
;
364370

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,18 @@ select _jsonpath_query(jsonb 'null', 'aaa.type()');
832832
"string"
833833
(1 row)
834834

835+
select _jsonpath_query(jsonb '{"a": 2}', '($.a - 5).abs() + 10');
836+
_jsonpath_query
837+
-----------------
838+
13
839+
(1 row)
840+
841+
select _jsonpath_query(jsonb '{"a": 2.5}', '-($.a * $.a).floor() + 10');
842+
_jsonpath_query
843+
-----------------
844+
4
845+
(1 row)
846+
835847
select _jsonpath_query(jsonb '[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()');
836848
ERROR: SQL/JSON array not found
837849
select _jsonpath_query(jsonb '[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');

src/test/regress/expected/jsonpath.out

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,36 @@ ERROR: bad jsonpath representation
442442
LINE 1: select '$ ? (@ like_regex "pattern" flag "a")'::jsonpath;
443443
^
444444
DETAIL: unrecognized flag of LIKE_REGEX predicate at or near """
445+
select '($).a.b'::jsonpath;
446+
jsonpath
447+
-----------
448+
$."a"."b"
449+
(1 row)
450+
451+
select '($.a.b).c.d'::jsonpath;
452+
jsonpath
453+
-------------------
454+
$."a"."b"."c"."d"
455+
(1 row)
456+
457+
select '($.a.b + -$.x.y).c.d'::jsonpath;
458+
jsonpath
459+
----------------------------------
460+
($."a"."b" + -$."x"."y")."c"."d"
461+
(1 row)
462+
463+
select '(-+$.a.b).c.d'::jsonpath;
464+
jsonpath
465+
-------------------------
466+
(-(+$."a"."b"))."c"."d"
467+
(1 row)
468+
469+
select '1 + ($.a.b + 2).c.d'::jsonpath;
470+
jsonpath
471+
-------------------------------
472+
(1 + ($."a"."b" + 2)."c"."d")
473+
(1 row)
474+
445475
select '$ ? (a < 1)'::jsonpath;
446476
jsonpath
447477
-------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ select _jsonpath_query(jsonb 'null', '123.type()');
169169
select _jsonpath_query(jsonb 'null', '"123".type()');
170170
select _jsonpath_query(jsonb 'null', 'aaa.type()');
171171

172+
select _jsonpath_query(jsonb '{"a": 2}', '($.a - 5).abs() + 10');
173+
select _jsonpath_query(jsonb '{"a": 2.5}', '-($.a * $.a).floor() + 10');
174+
172175
select _jsonpath_query(jsonb '[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()');
173176
select _jsonpath_query(jsonb '[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');
174177

src/test/regress/sql/jsonpath.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ select '$ ? (@ like_regex "pattern" flag "isim")'::jsonpath;
8181
select '$ ? (@ like_regex "pattern" flag "xsms")'::jsonpath;
8282
select '$ ? (@ like_regex "pattern" flag "a")'::jsonpath;
8383

84+
select '($).a.b'::jsonpath;
85+
select '($.a.b).c.d'::jsonpath;
86+
select '($.a.b + -$.x.y).c.d'::jsonpath;
87+
select '(-+$.a.b).c.d'::jsonpath;
88+
select '1 + ($.a.b + 2).c.d'::jsonpath;
89+
8490
select '$ ? (a < 1)'::jsonpath;
8591
select '$ ? (a < -1)'::jsonpath;
8692
select '$ ? (a < +1)'::jsonpath;

0 commit comments

Comments
 (0)