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

Commit aa76ea4

Browse files
author
Nikita Glukhov
committed
Add jsonpath sequences
1 parent 20b24cd commit aa76ea4

File tree

8 files changed

+252
-7
lines changed

8 files changed

+252
-7
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,29 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
199199
case jpiDouble:
200200
case jpiKeyValue:
201201
break;
202+
case jpiSequence:
203+
{
204+
int32 nelems = list_length(item->value.sequence.elems);
205+
ListCell *lc;
206+
int offset;
207+
208+
appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
209+
210+
offset = buf->len;
211+
212+
appendStringInfoSpaces(buf, sizeof(int32) * nelems);
213+
214+
foreach(lc, item->value.sequence.elems)
215+
{
216+
int32 pos =
217+
flattenJsonPathParseItem(buf, lfirst(lc),
218+
allowCurrent, insideArraySubscript);
219+
220+
*(int32 *) &buf->data[offset] = pos;
221+
offset += sizeof(int32);
222+
}
223+
}
224+
break;
202225
default:
203226
elog(ERROR, "Unknown jsonpath item type: %d", item->type);
204227
}
@@ -284,6 +307,8 @@ operationPriority(JsonPathItemType op)
284307
{
285308
switch (op)
286309
{
310+
case jpiSequence:
311+
return -1;
287312
case jpiOr:
288313
return 0;
289314
case jpiAnd:
@@ -441,12 +466,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
441466
if (i)
442467
appendStringInfoChar(buf, ',');
443468

444-
printJsonPathItem(buf, &from, false, false);
469+
printJsonPathItem(buf, &from, false, from.type == jpiSequence);
445470

446471
if (range)
447472
{
448473
appendBinaryStringInfo(buf, " to ", 4);
449-
printJsonPathItem(buf, &to, false, false);
474+
printJsonPathItem(buf, &to, false, to.type == jpiSequence);
450475
}
451476
}
452477
appendStringInfoChar(buf, ']');
@@ -504,6 +529,25 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
504529
printJsonPathItem(buf, &elem, false, false);
505530
appendStringInfoChar(buf, ')');
506531
break;
532+
case jpiSequence:
533+
if (printBracketes || jspHasNext(v))
534+
appendStringInfoChar(buf, '(');
535+
536+
for (i = 0; i < v->content.sequence.nelems; i++)
537+
{
538+
JsonPathItem elem;
539+
540+
if (i)
541+
appendBinaryStringInfo(buf, ", ", 2);
542+
543+
jspGetSequenceElement(v, i, &elem);
544+
545+
printJsonPathItem(buf, &elem, false, elem.type == jpiSequence);
546+
}
547+
548+
if (printBracketes || jspHasNext(v))
549+
appendStringInfoChar(buf, ')');
550+
break;
507551
default:
508552
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
509553
}
@@ -526,7 +570,7 @@ jsonpath_out(PG_FUNCTION_ARGS)
526570
appendBinaryStringInfo(&buf, "strict ", 7);
527571

528572
jspInit(&v, in);
529-
printJsonPathItem(&buf, &v, false, true);
573+
printJsonPathItem(&buf, &v, false, v.type != jpiSequence);
530574

531575
PG_RETURN_CSTRING(buf.data);
532576
}
@@ -643,6 +687,11 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
643687
read_int32(v->content.anybounds.first, base, pos);
644688
read_int32(v->content.anybounds.last, base, pos);
645689
break;
690+
case jpiSequence:
691+
read_int32(v->content.sequence.nelems, base, pos);
692+
read_int32_n(v->content.sequence.elems, base, pos,
693+
v->content.sequence.nelems);
694+
break;
646695
default:
647696
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
648697
}
@@ -712,7 +761,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
712761
v->type == jpiDatetime ||
713762
v->type == jpiKeyValue ||
714763
v->type == jpiStartsWith ||
715-
v->type == jpiMap
764+
v->type == jpiMap ||
765+
v->type == jpiSequence
716766
);
717767

718768
if (a)
@@ -814,3 +864,11 @@ jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
814864

815865
return true;
816866
}
867+
868+
void
869+
jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem)
870+
{
871+
Assert(v->type == jpiSequence);
872+
873+
jspInitByBuffer(elem, v->base, v->content.sequence.elems[i]);
874+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1963,6 +1963,51 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
19631963
found, false);
19641964
}
19651965
break;
1966+
case jpiSequence:
1967+
{
1968+
JsonPathItem next;
1969+
bool hasNext = jspGetNext(jsp, &next);
1970+
JsonValueList list;
1971+
JsonValueList *plist = hasNext ? &list : found;
1972+
JsonValueListIterator it;
1973+
int i;
1974+
1975+
for (i = 0; i < jsp->content.sequence.nelems; i++)
1976+
{
1977+
JsonbValue *v;
1978+
1979+
if (hasNext)
1980+
memset(&list, 0, sizeof(list));
1981+
1982+
jspGetSequenceElement(jsp, i, &elem);
1983+
res = recursiveExecute(cxt, &elem, jb, plist);
1984+
1985+
if (jperIsError(res))
1986+
break;
1987+
1988+
if (!hasNext)
1989+
{
1990+
if (!found && res == jperOk)
1991+
break;
1992+
continue;
1993+
}
1994+
1995+
memset(&it, 0, sizeof(it));
1996+
1997+
while ((v = JsonValueListNext(&list, &it)))
1998+
{
1999+
res = recursiveExecute(cxt, &next, v, found);
2000+
2001+
if (jperIsError(res) || (!found && res == jperOk))
2002+
{
2003+
i = jsp->content.sequence.nelems;
2004+
break;
2005+
}
2006+
}
2007+
}
2008+
2009+
break;
2010+
}
19662011
default:
19672012
elog(ERROR,"2Wrong state: %d", jsp->type);
19682013
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@ makeAny(int first, int last)
203203
return v;
204204
}
205205

206+
static JsonPathParseItem *
207+
makeItemSequence(List *elems)
208+
{
209+
JsonPathParseItem *v = makeItemType(jpiSequence);
210+
211+
v->value.sequence.elems = elems;
212+
213+
return v;
214+
}
215+
206216
%}
207217

208218
/* BISON Declarations */
@@ -236,9 +246,9 @@ makeAny(int first, int last)
236246
%type <value> scalar_value path_primary expr pexpr array_accessor
237247
any_path accessor_op key predicate delimited_predicate
238248
index_elem starts_with_initial opt_datetime_template
239-
expr_or_predicate
249+
expr_or_predicate expr_or_seq expr_seq
240250

241-
%type <elems> accessor_expr
251+
%type <elems> accessor_expr expr_list
242252

243253
%type <indexs> index_list
244254

@@ -261,7 +271,7 @@ makeAny(int first, int last)
261271
%%
262272

263273
result:
264-
mode expr_or_predicate {
274+
mode expr_or_seq {
265275
*result = palloc(sizeof(JsonPathParseResult));
266276
(*result)->expr = $2;
267277
(*result)->lax = $1;
@@ -274,6 +284,20 @@ expr_or_predicate:
274284
| predicate { $$ = $1; }
275285
;
276286

287+
expr_or_seq:
288+
expr_or_predicate { $$ = $1; }
289+
| expr_seq { $$ = $1; }
290+
;
291+
292+
expr_seq:
293+
expr_list { $$ = makeItemSequence($1); }
294+
;
295+
296+
expr_list:
297+
expr_or_predicate ',' expr_or_predicate { $$ = list_make2($1, $3); }
298+
| expr_list ',' expr_or_predicate { $$ = lappend($1, $3); }
299+
;
300+
277301
mode:
278302
STRICT_P { $$ = false; }
279303
| LAX_P { $$ = true; }
@@ -328,6 +352,7 @@ path_primary:
328352
| '$' { $$ = makeItemType(jpiRoot); }
329353
| '@' { $$ = makeItemType(jpiCurrent); }
330354
| LAST_P { $$ = makeItemType(jpiLast); }
355+
| '(' expr_seq ')' { $$ = $2; }
331356
;
332357

333358
accessor_expr:

src/include/utils/jsonpath.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ typedef enum JsonPathItemType {
8484
jpiLast,
8585
jpiStartsWith,
8686
jpiMap,
87+
jpiSequence,
8788
} JsonPathItemType;
8889

8990

@@ -132,6 +133,11 @@ typedef struct JsonPathItem {
132133
uint32 last;
133134
} anybounds;
134135

136+
struct {
137+
int32 nelems;
138+
int32 *elems;
139+
} sequence;
140+
135141
struct {
136142
char *data; /* for bool, numeric and string/key */
137143
int32 datalen; /* filled only for string/key */
@@ -152,6 +158,7 @@ extern bool jspGetBool(JsonPathItem *v);
152158
extern char * jspGetString(JsonPathItem *v, int32 *len);
153159
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
154160
JsonPathItem *to, int i);
161+
extern void jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem);
155162

156163
/*
157164
* Parsing
@@ -190,6 +197,10 @@ struct JsonPathParseItem {
190197
uint32 last;
191198
} anybounds;
192199

200+
struct {
201+
List *elems;
202+
} sequence;
203+
193204
/* scalars */
194205
Numeric numeric;
195206
bool boolean;

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,3 +1617,57 @@ select _jsonpath_query(jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]', '$.map(@.map(@ +
16171617
[[11, 12], [13, 14, 15], [], [16, 17]]
16181618
(1 row)
16191619

1620+
-- extension: path sequences
1621+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '10, 20, $[*], 30');
1622+
_jsonpath_query
1623+
-----------------
1624+
10
1625+
20
1626+
1
1627+
2
1628+
3
1629+
4
1630+
5
1631+
30
1632+
(8 rows)
1633+
1634+
select _jsonpath_query(jsonb '[1,2,3,4,5]', 'lax 10, 20, $[*].a, 30');
1635+
_jsonpath_query
1636+
-----------------
1637+
10
1638+
20
1639+
30
1640+
(3 rows)
1641+
1642+
select _jsonpath_query(jsonb '[1,2,3,4,5]', 'strict 10, 20, $[*].a, 30');
1643+
ERROR: SQL/JSON member not found
1644+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '-(10, 20, $[1 to 3], 30)');
1645+
_jsonpath_query
1646+
-----------------
1647+
-10
1648+
-20
1649+
-2
1650+
-3
1651+
-4
1652+
-30
1653+
(6 rows)
1654+
1655+
select _jsonpath_query(jsonb '[1,2,3,4,5]', 'lax (10, 20, $[1 to 3], 30).map(@ + 100)');
1656+
_jsonpath_query
1657+
-----------------
1658+
110
1659+
120
1660+
102
1661+
103
1662+
104
1663+
130
1664+
(6 rows)
1665+
1666+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '$[(0, $[*], 5) ? (@ == 3)]');
1667+
_jsonpath_query
1668+
-----------------
1669+
4
1670+
(1 row)
1671+
1672+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '$[(0, $[*], 3) ? (@ == 3)]');
1673+
ERROR: Invalid SQL/JSON subscript

src/test/regress/expected/jsonpath.out

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,42 @@ select '1 + ($.a.b > 2).c.d'::jsonpath;
461461
(1 + ($."a"."b" > 2)."c"."d")
462462
(1 row)
463463

464+
select '1, 2 + 3, $.a[*] + 5'::jsonpath;
465+
jsonpath
466+
------------------------
467+
1, 2 + 3, $."a"[*] + 5
468+
(1 row)
469+
470+
select '(1, 2, $.a)'::jsonpath;
471+
jsonpath
472+
-------------
473+
1, 2, $."a"
474+
(1 row)
475+
476+
select '(1, 2, $.a).a[*]'::jsonpath;
477+
jsonpath
478+
----------------------
479+
(1, 2, $."a")."a"[*]
480+
(1 row)
481+
482+
select '(1, 2, $.a) == 5'::jsonpath;
483+
jsonpath
484+
----------------------
485+
((1, 2, $."a") == 5)
486+
(1 row)
487+
488+
select '$[(1, 2, $.a) to (3, 4)]'::jsonpath;
489+
jsonpath
490+
----------------------------
491+
$[(1, 2, $."a") to (3, 4)]
492+
(1 row)
493+
494+
select '$[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
495+
jsonpath
496+
-----------------------------
497+
$[(1, (2, $."a")),3,(4, 5)]
498+
(1 row)
499+
464500
select '$ ? (a < 1)'::jsonpath;
465501
jsonpath
466502
-------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,12 @@ select _jsonpath_query(jsonb '1', 'strict $.map(@ + 10)');
379379
select _jsonpath_query(jsonb '1', 'lax $.map(@ + 10)');
380380
select _jsonpath_query(jsonb '[1, 2, 3]', '$.map(@ + 10)');
381381
select _jsonpath_query(jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]', '$.map(@.map(@ + 10))');
382+
383+
-- extension: path sequences
384+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '10, 20, $[*], 30');
385+
select _jsonpath_query(jsonb '[1,2,3,4,5]', 'lax 10, 20, $[*].a, 30');
386+
select _jsonpath_query(jsonb '[1,2,3,4,5]', 'strict 10, 20, $[*].a, 30');
387+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '-(10, 20, $[1 to 3], 30)');
388+
select _jsonpath_query(jsonb '[1,2,3,4,5]', 'lax (10, 20, $[1 to 3], 30).map(@ + 100)');
389+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '$[(0, $[*], 5) ? (@ == 3)]');
390+
select _jsonpath_query(jsonb '[1,2,3,4,5]', '$[(0, $[*], 3) ? (@ == 3)]');

0 commit comments

Comments
 (0)