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

Commit 32671f2

Browse files
author
Nikita Glukhov
committed
Add outer jsonpath item references
1 parent f2cb085 commit 32671f2

File tree

9 files changed

+159
-6
lines changed

9 files changed

+159
-6
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,16 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
358358
case jpiAnyArray:
359359
case jpiAnyKey:
360360
break;
361+
case jpiCurrentN:
362+
if (item->value.current.level < 0 ||
363+
item->value.current.level >= nestingLevel)
364+
ereport(ERROR,
365+
(errcode(ERRCODE_SYNTAX_ERROR),
366+
errmsg("invalid outer item reference in jsonpath @")));
367+
368+
appendBinaryStringInfo(buf, (char *) &item->value.current.level,
369+
sizeof(item->value.current.level));
370+
break;
361371
case jpiCurrent:
362372
if (nestingLevel <= 0)
363373
ereport(ERROR,
@@ -672,6 +682,10 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
672682
Assert(!inKey);
673683
appendStringInfoChar(buf, '@');
674684
break;
685+
case jpiCurrentN:
686+
Assert(!inKey);
687+
appendStringInfo(buf, "@%d", v->content.current.level);
688+
break;
675689
case jpiRoot:
676690
Assert(!inKey);
677691
appendStringInfoChar(buf, '$');
@@ -979,6 +993,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
979993
case jpiKeyValue:
980994
case jpiLast:
981995
break;
996+
case jpiCurrentN:
997+
read_int32(v->content.current.level, base, pos);
998+
break;
982999
case jpiKey:
9831000
case jpiString:
9841001
case jpiVariable:
@@ -1075,6 +1092,7 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
10751092
v->type == jpiIndexArray ||
10761093
v->type == jpiFilter ||
10771094
v->type == jpiCurrent ||
1095+
v->type == jpiCurrentN ||
10781096
v->type == jpiExists ||
10791097
v->type == jpiRoot ||
10801098
v->type == jpiVariable ||

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ typedef struct JsonBaseObjectInfo
120120
*/
121121
typedef struct JsonItemStackEntry
122122
{
123+
JsonBaseObjectInfo base;
123124
JsonItem *item;
124125
struct JsonItemStackEntry *parent;
125126
} JsonItemStackEntry;
@@ -443,8 +444,8 @@ static int compareDatetime(Datum val1, Oid typid1, int tz1,
443444
Datum val2, Oid typid2, int tz2,
444445
bool *error);
445446

446-
static void pushJsonItem(JsonItemStack *stack,
447-
JsonItemStackEntry *entry, JsonItem *item);
447+
static void pushJsonItem(JsonItemStack *stack, JsonItemStackEntry *entry,
448+
JsonItem *item, JsonBaseObjectInfo *base);
448449
static void popJsonItem(JsonItemStack *stack);
449450

450451
static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt,
@@ -884,7 +885,7 @@ executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
884885
cxt.throwErrors = throwErrors;
885886
cxt.isJsonb = isJsonb;
886887

887-
pushJsonItem(&cxt.stack, &root, cxt.root);
888+
pushJsonItem(&cxt.stack, &root, cxt.root, &cxt.baseObject);
888889

889890
if (jspStrictAbsenseOfErrors(&cxt) && !result)
890891
{
@@ -1015,6 +1016,27 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
10151016
found, true);
10161017
break;
10171018

1019+
case jpiCurrentN:
1020+
{
1021+
JsonItemStackEntry *current = cxt->stack;
1022+
int i;
1023+
1024+
for (i = 0; i < jsp->content.current.level; i++)
1025+
{
1026+
current = current->parent;
1027+
1028+
if (!current)
1029+
elog(ERROR, "invalid jsonpath current item reference");
1030+
}
1031+
1032+
baseObject = cxt->baseObject;
1033+
cxt->baseObject = current->base;
1034+
jb = current->item;
1035+
res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1036+
cxt->baseObject = baseObject;
1037+
break;
1038+
}
1039+
10181040
case jpiAnyArray:
10191041
if (JsonbType(jb) == jbvArray)
10201042
{
@@ -2175,7 +2197,7 @@ executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
21752197
JsonItemStackEntry current;
21762198
JsonPathBool res;
21772199

2178-
pushJsonItem(&cxt->stack, &current, jb);
2200+
pushJsonItem(&cxt->stack, &current, jb, &cxt->baseObject);
21792201
res = executeBoolItem(cxt, jsp, jb, false);
21802202
popJsonItem(&cxt->stack);
21812203

@@ -3712,9 +3734,11 @@ wrapItemsInArray(const JsonValueList *items, bool isJsonb)
37123734
}
37133735

37143736
static void
3715-
pushJsonItem(JsonItemStack *stack, JsonItemStackEntry *entry, JsonItem *item)
3737+
pushJsonItem(JsonItemStack *stack, JsonItemStackEntry *entry, JsonItem *item,
3738+
JsonBaseObjectInfo *base)
37163739
{
37173740
entry->item = item;
3741+
entry->base = *base;
37183742
entry->parent = *stack;
37193743
*stack = entry;
37203744
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
5858
JsonPathString *flags);
5959
static JsonPathParseItem *makeItemSequence(List *elems);
6060
static JsonPathParseItem *makeItemObject(List *fields);
61+
static JsonPathParseItem *makeItemCurrentN(int level);
6162

6263
/*
6364
* Bison doesn't allocate anything that needs to live across parser calls,
@@ -96,7 +97,7 @@ static JsonPathParseItem *makeItemObject(List *fields);
9697
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
9798
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
9899
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
99-
%token <str> DATETIME_P
100+
%token <str> DATETIME_P CURRENT_P
100101

101102
%type <result> result
102103

@@ -211,6 +212,7 @@ path_primary:
211212
scalar_value { $$ = $1; }
212213
| '$' { $$ = makeItemType(jpiRoot); }
213214
| '@' { $$ = makeItemType(jpiCurrent); }
215+
| CURRENT_P { $$ = makeItemCurrentN(pg_atoi(&$1.val[1], 4, 0)); }
214216
| LAST_P { $$ = makeItemType(jpiLast); }
215217
| '(' expr_seq ')' { $$ = $2; }
216218
| '[' ']' { $$ = makeItemUnary(jpiArray, NULL); }
@@ -357,6 +359,20 @@ makeItemType(JsonPathItemType type)
357359
return v;
358360
}
359361

362+
static JsonPathParseItem *
363+
makeItemCurrentN(int level)
364+
{
365+
JsonPathParseItem *v;
366+
367+
if (!level)
368+
return makeItemType(jpiCurrent);
369+
370+
v = makeItemType(jpiCurrentN);
371+
v->value.current.level = level;
372+
373+
return v;
374+
}
375+
360376
static JsonPathParseItem *
361377
makeItemString(JsonPathString *s)
362378
{

src/backend/utils/adt/jsonpath_scan.l

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ hex_fail \\x{hex_dig}{0,1}
194194
BEGIN xvq;
195195
}
196196

197+
\@[0-9]+ {
198+
addstring(true, yytext, yyleng);
199+
addchar(false, '\0');
200+
yylval->str = scanstring;
201+
return CURRENT_P;
202+
}
203+
197204
{special} { return *yytext; }
198205

199206
{blank}+ { /* ignore */ }

src/include/utils/jsonpath.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ typedef enum JsonPathItemType
7373
jpiAny, /* .** */
7474
jpiKey, /* .key */
7575
jpiCurrent, /* @ */
76+
jpiCurrentN, /* @N */
7677
jpiRoot, /* $ */
7778
jpiVariable, /* $variable */
7879
jpiFilter, /* ? (predicate) */
@@ -169,6 +170,11 @@ typedef struct JsonPathItem
169170
} *fields;
170171
} object;
171172

173+
struct
174+
{
175+
int32 level;
176+
} current;
177+
172178
struct
173179
{
174180
char *data; /* for bool, numeric and string/key */
@@ -262,6 +268,10 @@ struct JsonPathParseItem
262268
List *fields;
263269
} object;
264270

271+
struct {
272+
int level;
273+
} current;
274+
265275
/* scalars */
266276
Numeric numeric;
267277
bool boolean;

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,3 +2614,38 @@ select jsonb_path_query('null', '{"a": 1}["b"]');
26142614
------------------
26152615
(0 rows)
26162616

2617+
-- extension: outer item reference (@N)
2618+
select jsonb_path_query('[2,4,1,5,3]', '$[*] ? (!exists($[*] ? (@ < @1)))');
2619+
jsonb_path_query
2620+
------------------
2621+
1
2622+
(1 row)
2623+
2624+
select jsonb_path_query('[2,4,1,5,3]', '$[*] ? (!exists($[*] ? (@ > @1)))');
2625+
jsonb_path_query
2626+
------------------
2627+
5
2628+
(1 row)
2629+
2630+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ < @1[1]) > 2)');
2631+
jsonb_path_query
2632+
------------------
2633+
[2, 4, 1, 5, 3]
2634+
(1 row)
2635+
2636+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ < @1[1]) > 3)');
2637+
jsonb_path_query
2638+
------------------
2639+
(0 rows)
2640+
2641+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ ? (@ ? (@ < @3[1]) > @2[0]) > @1[0]) > 2)');
2642+
jsonb_path_query
2643+
------------------
2644+
[2, 4, 1, 5, 3]
2645+
(1 row)
2646+
2647+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ ? (@ ? (@ < @3[2]) > @2[0]) > @1[0]) > 2)');
2648+
jsonb_path_query
2649+
------------------
2650+
(0 rows)
2651+

src/test/regress/expected/jsonpath.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,3 +1054,31 @@ select '(1.).e3'::jsonpath;
10541054
ERROR: syntax error, unexpected ')' at or near ")" of jsonpath input
10551055
LINE 1: select '(1.).e3'::jsonpath;
10561056
^
1057+
select '@1'::jsonpath;
1058+
ERROR: invalid outer item reference in jsonpath @
1059+
LINE 1: select '@1'::jsonpath;
1060+
^
1061+
select '@-1'::jsonpath;
1062+
ERROR: @ is not allowed in root expressions
1063+
LINE 1: select '@-1'::jsonpath;
1064+
^
1065+
select '$ ? (@0 > 1)'::jsonpath;
1066+
jsonpath
1067+
-----------
1068+
$?(@ > 1)
1069+
(1 row)
1070+
1071+
select '$ ? (@1 > 1)'::jsonpath;
1072+
ERROR: invalid outer item reference in jsonpath @
1073+
LINE 1: select '$ ? (@1 > 1)'::jsonpath;
1074+
^
1075+
select '$.a ? (@.b ? (@1 > @) > 5)'::jsonpath;
1076+
jsonpath
1077+
----------------------------
1078+
$."a"?(@."b"?(@1 > @) > 5)
1079+
(1 row)
1080+
1081+
select '$.a ? (@.b ? (@2 > @) > 5)'::jsonpath;
1082+
ERROR: invalid outer item reference in jsonpath @
1083+
LINE 1: select '$.a ? (@.b ? (@2 > @) > 5)'::jsonpath;
1084+
^

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,3 +590,11 @@ select jsonb_path_query('{"a": 1, "b": 2}', 'lax $["b", "c", "b", "a", 0 to 3]')
590590

591591
select jsonb_path_query('null', '{"a": 1}["a"]');
592592
select jsonb_path_query('null', '{"a": 1}["b"]');
593+
594+
-- extension: outer item reference (@N)
595+
select jsonb_path_query('[2,4,1,5,3]', '$[*] ? (!exists($[*] ? (@ < @1)))');
596+
select jsonb_path_query('[2,4,1,5,3]', '$[*] ? (!exists($[*] ? (@ > @1)))');
597+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ < @1[1]) > 2)');
598+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ < @1[1]) > 3)');
599+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ ? (@ ? (@ < @3[1]) > @2[0]) > @1[0]) > 2)');
600+
select jsonb_path_query('[2,4,1,5,3]', 'strict $ ? (@[*] ? (@ ? (@ ? (@ < @3[2]) > @2[0]) > @1[0]) > 2)');

src/test/regress/sql/jsonpath.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,10 @@ select '1..e'::jsonpath;
198198
select '1..e3'::jsonpath;
199199
select '(1.).e'::jsonpath;
200200
select '(1.).e3'::jsonpath;
201+
202+
select '@1'::jsonpath;
203+
select '@-1'::jsonpath;
204+
select '$ ? (@0 > 1)'::jsonpath;
205+
select '$ ? (@1 > 1)'::jsonpath;
206+
select '$.a ? (@.b ? (@1 > @) > 5)'::jsonpath;
207+
select '$.a ? (@.b ? (@2 > @) > 5)'::jsonpath;

0 commit comments

Comments
 (0)