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

Commit c27849a

Browse files
author
Nikita Glukhov
committed
Add jsonpath sequences
1 parent 7c6a989 commit c27849a

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
@@ -215,6 +215,29 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
215215
case jpiDouble:
216216
case jpiKeyValue:
217217
break;
218+
case jpiSequence:
219+
{
220+
int32 nelems = list_length(item->value.sequence.elems);
221+
ListCell *lc;
222+
int offset;
223+
224+
appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
225+
226+
offset = buf->len;
227+
228+
appendStringInfoSpaces(buf, sizeof(int32) * nelems);
229+
230+
foreach(lc, item->value.sequence.elems)
231+
{
232+
int32 pos =
233+
flattenJsonPathParseItem(buf, lfirst(lc),
234+
allowCurrent, insideArraySubscript);
235+
236+
*(int32 *) &buf->data[offset] = pos;
237+
offset += sizeof(int32);
238+
}
239+
}
240+
break;
218241
default:
219242
elog(ERROR, "Unknown jsonpath item type: %d", item->type);
220243
}
@@ -300,6 +323,8 @@ operationPriority(JsonPathItemType op)
300323
{
301324
switch (op)
302325
{
326+
case jpiSequence:
327+
return -1;
303328
case jpiOr:
304329
return 0;
305330
case jpiAnd:
@@ -489,12 +514,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
489514
if (i)
490515
appendStringInfoChar(buf, ',');
491516

492-
printJsonPathItem(buf, &from, false, false);
517+
printJsonPathItem(buf, &from, false, from.type == jpiSequence);
493518

494519
if (range)
495520
{
496521
appendBinaryStringInfo(buf, " to ", 4);
497-
printJsonPathItem(buf, &to, false, false);
522+
printJsonPathItem(buf, &to, false, to.type == jpiSequence);
498523
}
499524
}
500525
appendStringInfoChar(buf, ']');
@@ -558,6 +583,25 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
558583
case jpiKeyValue:
559584
appendBinaryStringInfo(buf, ".keyvalue()", 11);
560585
break;
586+
case jpiSequence:
587+
if (printBracketes || jspHasNext(v))
588+
appendStringInfoChar(buf, '(');
589+
590+
for (i = 0; i < v->content.sequence.nelems; i++)
591+
{
592+
JsonPathItem elem;
593+
594+
if (i)
595+
appendBinaryStringInfo(buf, ", ", 2);
596+
597+
jspGetSequenceElement(v, i, &elem);
598+
599+
printJsonPathItem(buf, &elem, false, elem.type == jpiSequence);
600+
}
601+
602+
if (printBracketes || jspHasNext(v))
603+
appendStringInfoChar(buf, ')');
604+
break;
561605
default:
562606
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
563607
}
@@ -580,7 +624,7 @@ jsonpath_out(PG_FUNCTION_ARGS)
580624
appendBinaryStringInfo(&buf, "strict ", 7);
581625

582626
jspInit(&v, in);
583-
printJsonPathItem(&buf, &v, false, true);
627+
printJsonPathItem(&buf, &v, false, v.type != jpiSequence);
584628

585629
PG_RETURN_CSTRING(buf.data);
586630
}
@@ -702,6 +746,11 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
702746
read_int32(v->content.anybounds.first, base, pos);
703747
read_int32(v->content.anybounds.last, base, pos);
704748
break;
749+
case jpiSequence:
750+
read_int32(v->content.sequence.nelems, base, pos);
751+
read_int32_n(v->content.sequence.elems, base, pos,
752+
v->content.sequence.nelems);
753+
break;
705754
default:
706755
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
707756
}
@@ -768,7 +817,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
768817
v->type == jpiDouble ||
769818
v->type == jpiDatetime ||
770819
v->type == jpiKeyValue ||
771-
v->type == jpiStartsWith
820+
v->type == jpiStartsWith ||
821+
v->type == jpiSequence
772822
);
773823

774824
if (a)
@@ -872,3 +922,11 @@ jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
872922

873923
return true;
874924
}
925+
926+
void
927+
jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem)
928+
{
929+
Assert(v->type == jpiSequence);
930+
931+
jspInitByBuffer(elem, v->base, v->content.sequence.elems[i]);
932+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,6 +2261,51 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
22612261
}
22622262
}
22632263
break;
2264+
case jpiSequence:
2265+
{
2266+
JsonPathItem next;
2267+
bool hasNext = jspGetNext(jsp, &next);
2268+
JsonValueList list;
2269+
JsonValueList *plist = hasNext ? &list : found;
2270+
JsonValueListIterator it;
2271+
int i;
2272+
2273+
for (i = 0; i < jsp->content.sequence.nelems; i++)
2274+
{
2275+
JsonbValue *v;
2276+
2277+
if (hasNext)
2278+
memset(&list, 0, sizeof(list));
2279+
2280+
jspGetSequenceElement(jsp, i, &elem);
2281+
res = recursiveExecute(cxt, &elem, jb, plist);
2282+
2283+
if (jperIsError(res))
2284+
break;
2285+
2286+
if (!hasNext)
2287+
{
2288+
if (!found && res == jperOk)
2289+
break;
2290+
continue;
2291+
}
2292+
2293+
memset(&it, 0, sizeof(it));
2294+
2295+
while ((v = JsonValueListNext(&list, &it)))
2296+
{
2297+
res = recursiveExecute(cxt, &next, v, found);
2298+
2299+
if (jperIsError(res) || (!found && res == jperOk))
2300+
{
2301+
i = jsp->content.sequence.nelems;
2302+
break;
2303+
}
2304+
}
2305+
}
2306+
2307+
break;
2308+
}
22642309
default:
22652310
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
22662311
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,16 @@ makeItemLikeRegex(JsonPathParseItem *expr, string *pattern, string *flags)
252252
return v;
253253
}
254254

255+
static JsonPathParseItem *
256+
makeItemSequence(List *elems)
257+
{
258+
JsonPathParseItem *v = makeItemType(jpiSequence);
259+
260+
v->value.sequence.elems = elems;
261+
262+
return v;
263+
}
264+
255265
%}
256266

257267
/* BISON Declarations */
@@ -285,9 +295,9 @@ makeItemLikeRegex(JsonPathParseItem *expr, string *pattern, string *flags)
285295
%type <value> scalar_value path_primary expr array_accessor
286296
any_path accessor_op key predicate delimited_predicate
287297
index_elem starts_with_initial datetime_template opt_datetime_template
288-
expr_or_predicate
298+
expr_or_predicate expr_or_seq expr_seq
289299

290-
%type <elems> accessor_expr
300+
%type <elems> accessor_expr expr_list
291301

292302
%type <indexs> index_list
293303

@@ -311,7 +321,7 @@ makeItemLikeRegex(JsonPathParseItem *expr, string *pattern, string *flags)
311321
%%
312322

313323
result:
314-
mode expr_or_predicate {
324+
mode expr_or_seq {
315325
*result = palloc(sizeof(JsonPathParseResult));
316326
(*result)->expr = $2;
317327
(*result)->lax = $1;
@@ -324,6 +334,20 @@ expr_or_predicate:
324334
| predicate { $$ = $1; }
325335
;
326336

337+
expr_or_seq:
338+
expr_or_predicate { $$ = $1; }
339+
| expr_seq { $$ = $1; }
340+
;
341+
342+
expr_seq:
343+
expr_list { $$ = makeItemSequence($1); }
344+
;
345+
346+
expr_list:
347+
expr_or_predicate ',' expr_or_predicate { $$ = list_make2($1, $3); }
348+
| expr_list ',' expr_or_predicate { $$ = lappend($1, $3); }
349+
;
350+
327351
mode:
328352
STRICT_P { $$ = false; }
329353
| LAX_P { $$ = true; }
@@ -378,6 +402,7 @@ path_primary:
378402
| '$' { $$ = makeItemType(jpiRoot); }
379403
| '@' { $$ = makeItemType(jpiCurrent); }
380404
| LAST_P { $$ = makeItemType(jpiLast); }
405+
| '(' expr_seq ')' { $$ = $2; }
381406
;
382407

383408
accessor_expr:

src/include/utils/jsonpath.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ typedef enum JsonPathItemType {
8686
jpiLast,
8787
jpiStartsWith,
8888
jpiLikeRegex,
89+
jpiSequence,
8990
} JsonPathItemType;
9091

9192
/* XQuery regex mode flags for LIKE_REGEX predicate */
@@ -139,6 +140,11 @@ typedef struct JsonPathItem {
139140
uint32 last;
140141
} anybounds;
141142

143+
struct {
144+
int32 nelems;
145+
int32 *elems;
146+
} sequence;
147+
142148
struct {
143149
char *data; /* for bool, numeric and string/key */
144150
int32 datalen; /* filled only for string/key */
@@ -166,6 +172,7 @@ extern bool jspGetBool(JsonPathItem *v);
166172
extern char * jspGetString(JsonPathItem *v, int32 *len);
167173
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
168174
JsonPathItem *to, int i);
175+
extern void jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem);
169176

170177
/*
171178
* Parsing
@@ -211,6 +218,10 @@ struct JsonPathParseItem {
211218
uint32 flags;
212219
} like_regex;
213220

221+
struct {
222+
List *elems;
223+
} sequence;
224+
214225
/* scalars */
215226
Numeric numeric;
216227
bool boolean;

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,3 +1726,57 @@ SELECT jsonb '[{"a": 1}, {"a": 2}]' @~ '$[*].a > 2';
17261726
f
17271727
(1 row)
17281728

1729+
-- extension: path sequences
1730+
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
1731+
?column?
1732+
----------
1733+
10
1734+
20
1735+
1
1736+
2
1737+
3
1738+
4
1739+
5
1740+
30
1741+
(8 rows)
1742+
1743+
select jsonb '[1,2,3,4,5]' @* 'lax 10, 20, $[*].a, 30';
1744+
?column?
1745+
----------
1746+
10
1747+
20
1748+
30
1749+
(3 rows)
1750+
1751+
select jsonb '[1,2,3,4,5]' @* 'strict 10, 20, $[*].a, 30';
1752+
ERROR: SQL/JSON member not found
1753+
select jsonb '[1,2,3,4,5]' @* '-(10, 20, $[1 to 3], 30)';
1754+
?column?
1755+
----------
1756+
-10
1757+
-20
1758+
-2
1759+
-3
1760+
-4
1761+
-30
1762+
(6 rows)
1763+
1764+
select jsonb '[1,2,3,4,5]' @* 'lax (10, 20.5, $[1 to 3], "30").double()';
1765+
?column?
1766+
----------
1767+
10
1768+
20.5
1769+
2
1770+
3
1771+
4
1772+
30
1773+
(6 rows)
1774+
1775+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 5) ? (@ == 3)]';
1776+
?column?
1777+
----------
1778+
4
1779+
(1 row)
1780+
1781+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 3) ? (@ == 3)]';
1782+
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
@@ -510,6 +510,42 @@ select '((($ + 1)).a + ((2)).b ? ((((@ > 1)) || (exists(@.c)))))'::jsonpath;
510510
(($ + 1)."a" + 2."b"?(@ > 1 || exists (@."c")))
511511
(1 row)
512512

513+
select '1, 2 + 3, $.a[*] + 5'::jsonpath;
514+
jsonpath
515+
------------------------
516+
1, 2 + 3, $."a"[*] + 5
517+
(1 row)
518+
519+
select '(1, 2, $.a)'::jsonpath;
520+
jsonpath
521+
-------------
522+
1, 2, $."a"
523+
(1 row)
524+
525+
select '(1, 2, $.a).a[*]'::jsonpath;
526+
jsonpath
527+
----------------------
528+
(1, 2, $."a")."a"[*]
529+
(1 row)
530+
531+
select '(1, 2, $.a) == 5'::jsonpath;
532+
jsonpath
533+
----------------------
534+
((1, 2, $."a") == 5)
535+
(1 row)
536+
537+
select '$[(1, 2, $.a) to (3, 4)]'::jsonpath;
538+
jsonpath
539+
----------------------------
540+
$[(1, 2, $."a") to (3, 4)]
541+
(1 row)
542+
543+
select '$[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
544+
jsonpath
545+
-----------------------------
546+
$[(1, (2, $."a")),3,(4, 5)]
547+
(1 row)
548+
513549
select '$ ? (@.a < 1)'::jsonpath;
514550
jsonpath
515551
---------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,12 @@ SELECT jsonb '[{"a": 1}, {"a": 2}]' @? '$[*] ? (@.a > 2)';
387387

388388
SELECT jsonb '[{"a": 1}, {"a": 2}]' @~ '$[*].a > 1';
389389
SELECT jsonb '[{"a": 1}, {"a": 2}]' @~ '$[*].a > 2';
390+
391+
-- extension: path sequences
392+
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
393+
select jsonb '[1,2,3,4,5]' @* 'lax 10, 20, $[*].a, 30';
394+
select jsonb '[1,2,3,4,5]' @* 'strict 10, 20, $[*].a, 30';
395+
select jsonb '[1,2,3,4,5]' @* '-(10, 20, $[1 to 3], 30)';
396+
select jsonb '[1,2,3,4,5]' @* 'lax (10, 20.5, $[1 to 3], "30").double()';
397+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 5) ? (@ == 3)]';
398+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 3) ? (@ == 3)]';

0 commit comments

Comments
 (0)