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

Commit 612baac

Browse files
author
Nikita Glukhov
committed
Add jsonpath sequences
1 parent 843ce40 commit 612baac

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
@@ -222,6 +222,29 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
222222
case jpiDouble:
223223
case jpiKeyValue:
224224
break;
225+
case jpiSequence:
226+
{
227+
int32 nelems = list_length(item->value.sequence.elems);
228+
ListCell *lc;
229+
int offset;
230+
231+
appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
232+
233+
offset = buf->len;
234+
235+
appendStringInfoSpaces(buf, sizeof(int32) * nelems);
236+
237+
foreach(lc, item->value.sequence.elems)
238+
{
239+
int32 pos =
240+
flattenJsonPathParseItem(buf, lfirst(lc),
241+
allowCurrent, insideArraySubscript);
242+
243+
*(int32 *) &buf->data[offset] = pos;
244+
offset += sizeof(int32);
245+
}
246+
}
247+
break;
225248
default:
226249
elog(ERROR, "Unknown jsonpath item type: %d", item->type);
227250
}
@@ -307,6 +330,8 @@ operationPriority(JsonPathItemType op)
307330
{
308331
switch (op)
309332
{
333+
case jpiSequence:
334+
return -1;
310335
case jpiOr:
311336
return 0;
312337
case jpiAnd:
@@ -496,12 +521,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
496521
if (i)
497522
appendStringInfoChar(buf, ',');
498523

499-
printJsonPathItem(buf, &from, false, false);
524+
printJsonPathItem(buf, &from, false, from.type == jpiSequence);
500525

501526
if (range)
502527
{
503528
appendBinaryStringInfo(buf, " to ", 4);
504-
printJsonPathItem(buf, &to, false, false);
529+
printJsonPathItem(buf, &to, false, to.type == jpiSequence);
505530
}
506531
}
507532
appendStringInfoChar(buf, ']');
@@ -559,6 +584,25 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
559584
printJsonPathItem(buf, &elem, false, false);
560585
appendStringInfoChar(buf, ')');
561586
break;
587+
case jpiSequence:
588+
if (printBracketes || jspHasNext(v))
589+
appendStringInfoChar(buf, '(');
590+
591+
for (i = 0; i < v->content.sequence.nelems; i++)
592+
{
593+
JsonPathItem elem;
594+
595+
if (i)
596+
appendBinaryStringInfo(buf, ", ", 2);
597+
598+
jspGetSequenceElement(v, i, &elem);
599+
600+
printJsonPathItem(buf, &elem, false, elem.type == jpiSequence);
601+
}
602+
603+
if (printBracketes || jspHasNext(v))
604+
appendStringInfoChar(buf, ')');
605+
break;
562606
default:
563607
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
564608
}
@@ -581,7 +625,7 @@ jsonpath_out(PG_FUNCTION_ARGS)
581625
appendBinaryStringInfo(&buf, "strict ", 7);
582626

583627
jspInit(&v, in);
584-
printJsonPathItem(&buf, &v, false, true);
628+
printJsonPathItem(&buf, &v, false, v.type != jpiSequence);
585629

586630
PG_RETURN_CSTRING(buf.data);
587631
}
@@ -704,6 +748,11 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
704748
read_int32(v->content.anybounds.first, base, pos);
705749
read_int32(v->content.anybounds.last, base, pos);
706750
break;
751+
case jpiSequence:
752+
read_int32(v->content.sequence.nelems, base, pos);
753+
read_int32_n(v->content.sequence.elems, base, pos,
754+
v->content.sequence.nelems);
755+
break;
707756
default:
708757
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
709758
}
@@ -773,7 +822,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
773822
v->type == jpiDatetime ||
774823
v->type == jpiKeyValue ||
775824
v->type == jpiStartsWith ||
776-
v->type == jpiMap
825+
v->type == jpiMap ||
826+
v->type == jpiSequence
777827
);
778828

779829
if (a)
@@ -875,3 +925,11 @@ jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
875925

876926
return true;
877927
}
928+
929+
void
930+
jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem)
931+
{
932+
Assert(v->type == jpiSequence);
933+
934+
jspInitByBuffer(elem, v->base, v->content.sequence.elems[i]);
935+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,6 +2136,51 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
21362136
found, false);
21372137
}
21382138
break;
2139+
case jpiSequence:
2140+
{
2141+
JsonPathItem next;
2142+
bool hasNext = jspGetNext(jsp, &next);
2143+
JsonValueList list;
2144+
JsonValueList *plist = hasNext ? &list : found;
2145+
JsonValueListIterator it;
2146+
int i;
2147+
2148+
for (i = 0; i < jsp->content.sequence.nelems; i++)
2149+
{
2150+
JsonbValue *v;
2151+
2152+
if (hasNext)
2153+
memset(&list, 0, sizeof(list));
2154+
2155+
jspGetSequenceElement(jsp, i, &elem);
2156+
res = recursiveExecute(cxt, &elem, jb, plist);
2157+
2158+
if (jperIsError(res))
2159+
break;
2160+
2161+
if (!hasNext)
2162+
{
2163+
if (!found && res == jperOk)
2164+
break;
2165+
continue;
2166+
}
2167+
2168+
memset(&it, 0, sizeof(it));
2169+
2170+
while ((v = JsonValueListNext(&list, &it)))
2171+
{
2172+
res = recursiveExecute(cxt, &next, v, found);
2173+
2174+
if (jperIsError(res) || (!found && res == jperOk))
2175+
{
2176+
i = jsp->content.sequence.nelems;
2177+
break;
2178+
}
2179+
}
2180+
}
2181+
2182+
break;
2183+
}
21392184
default:
21402185
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
21412186
}

src/backend/utils/adt/jsonpath_gram.y

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

244+
static JsonPathParseItem *
245+
makeItemSequence(List *elems)
246+
{
247+
JsonPathParseItem *v = makeItemType(jpiSequence);
248+
249+
v->value.sequence.elems = elems;
250+
251+
return v;
252+
}
253+
244254
%}
245255

246256
/* BISON Declarations */
@@ -273,9 +283,9 @@ makeItemLikeRegex(JsonPathParseItem *expr, string *pattern, string *flags)
273283
%type <value> scalar_value path_primary expr pexpr array_accessor
274284
any_path accessor_op key predicate delimited_predicate
275285
index_elem starts_with_initial opt_datetime_template
276-
expr_or_predicate
286+
expr_or_predicate expr_or_seq expr_seq
277287

278-
%type <elems> accessor_expr
288+
%type <elems> accessor_expr expr_list
279289

280290
%type <indexs> index_list
281291

@@ -298,7 +308,7 @@ makeItemLikeRegex(JsonPathParseItem *expr, string *pattern, string *flags)
298308
%%
299309

300310
result:
301-
mode expr_or_predicate {
311+
mode expr_or_seq {
302312
*result = palloc(sizeof(JsonPathParseResult));
303313
(*result)->expr = $2;
304314
(*result)->lax = $1;
@@ -311,6 +321,20 @@ expr_or_predicate:
311321
| predicate { $$ = $1; }
312322
;
313323

324+
expr_or_seq:
325+
expr_or_predicate { $$ = $1; }
326+
| expr_seq { $$ = $1; }
327+
;
328+
329+
expr_seq:
330+
expr_list { $$ = makeItemSequence($1); }
331+
;
332+
333+
expr_list:
334+
expr_or_predicate ',' expr_or_predicate { $$ = list_make2($1, $3); }
335+
| expr_list ',' expr_or_predicate { $$ = lappend($1, $3); }
336+
;
337+
314338
mode:
315339
STRICT_P { $$ = false; }
316340
| LAX_P { $$ = true; }
@@ -365,6 +389,7 @@ path_primary:
365389
| '$' { $$ = makeItemType(jpiRoot); }
366390
| '@' { $$ = makeItemType(jpiCurrent); }
367391
| LAST_P { $$ = makeItemType(jpiLast); }
392+
| '(' expr_seq ')' { $$ = $2; }
368393
;
369394

370395
accessor_expr:

src/include/utils/jsonpath.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ typedef enum JsonPathItemType {
8585
jpiStartsWith,
8686
jpiLikeRegex,
8787
jpiMap,
88+
jpiSequence,
8889
} JsonPathItemType;
8990

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

142+
struct {
143+
int32 nelems;
144+
int32 *elems;
145+
} sequence;
146+
141147
struct {
142148
char *data; /* for bool, numeric and string/key */
143149
int32 datalen; /* filled only for string/key */
@@ -165,6 +171,7 @@ extern bool jspGetBool(JsonPathItem *v);
165171
extern char * jspGetString(JsonPathItem *v, int32 *len);
166172
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
167173
JsonPathItem *to, int i);
174+
extern void jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem);
168175

169176
/*
170177
* Parsing
@@ -210,6 +217,10 @@ struct JsonPathParseItem {
210217
uint32 flags;
211218
} like_regex;
212219

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

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,3 +1666,57 @@ select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.map(@.map(@ + 10))';
16661666
[[11, 12], [13, 14, 15], [], [16, 17]]
16671667
(1 row)
16681668

1669+
-- extension: path sequences
1670+
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
1671+
?column?
1672+
----------
1673+
10
1674+
20
1675+
1
1676+
2
1677+
3
1678+
4
1679+
5
1680+
30
1681+
(8 rows)
1682+
1683+
select jsonb '[1,2,3,4,5]' @* 'lax 10, 20, $[*].a, 30';
1684+
?column?
1685+
----------
1686+
10
1687+
20
1688+
30
1689+
(3 rows)
1690+
1691+
select jsonb '[1,2,3,4,5]' @* 'strict 10, 20, $[*].a, 30';
1692+
ERROR: SQL/JSON member not found
1693+
select jsonb '[1,2,3,4,5]' @* '-(10, 20, $[1 to 3], 30)';
1694+
?column?
1695+
----------
1696+
-10
1697+
-20
1698+
-2
1699+
-3
1700+
-4
1701+
-30
1702+
(6 rows)
1703+
1704+
select jsonb '[1,2,3,4,5]' @* 'lax (10, 20, $[1 to 3], 30).map(@ + 100)';
1705+
?column?
1706+
----------
1707+
110
1708+
120
1709+
102
1710+
103
1711+
104
1712+
130
1713+
(6 rows)
1714+
1715+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 5) ? (@ == 3)]';
1716+
?column?
1717+
----------
1718+
4
1719+
(1 row)
1720+
1721+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 3) ? (@ == 3)]';
1722+
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
@@ -494,6 +494,42 @@ select '1 + ($.a.b > 2).c.d'::jsonpath;
494494
(1 + ($."a"."b" > 2)."c"."d")
495495
(1 row)
496496

497+
select '1, 2 + 3, $.a[*] + 5'::jsonpath;
498+
jsonpath
499+
------------------------
500+
1, 2 + 3, $."a"[*] + 5
501+
(1 row)
502+
503+
select '(1, 2, $.a)'::jsonpath;
504+
jsonpath
505+
-------------
506+
1, 2, $."a"
507+
(1 row)
508+
509+
select '(1, 2, $.a).a[*]'::jsonpath;
510+
jsonpath
511+
----------------------
512+
(1, 2, $."a")."a"[*]
513+
(1 row)
514+
515+
select '(1, 2, $.a) == 5'::jsonpath;
516+
jsonpath
517+
----------------------
518+
((1, 2, $."a") == 5)
519+
(1 row)
520+
521+
select '$[(1, 2, $.a) to (3, 4)]'::jsonpath;
522+
jsonpath
523+
----------------------------
524+
$[(1, 2, $."a") to (3, 4)]
525+
(1 row)
526+
527+
select '$[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
528+
jsonpath
529+
-----------------------------
530+
$[(1, (2, $."a")),3,(4, 5)]
531+
(1 row)
532+
497533
select '$ ? (@.a < 1)'::jsonpath;
498534
jsonpath
499535
---------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,12 @@ select jsonb '1' @* 'strict $.map(@ + 10)';
374374
select jsonb '1' @* 'lax $.map(@ + 10)';
375375
select jsonb '[1, 2, 3]' @* '$.map(@ + 10)';
376376
select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.map(@.map(@ + 10))';
377+
378+
-- extension: path sequences
379+
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
380+
select jsonb '[1,2,3,4,5]' @* 'lax 10, 20, $[*].a, 30';
381+
select jsonb '[1,2,3,4,5]' @* 'strict 10, 20, $[*].a, 30';
382+
select jsonb '[1,2,3,4,5]' @* '-(10, 20, $[1 to 3], 30)';
383+
select jsonb '[1,2,3,4,5]' @* 'lax (10, 20, $[1 to 3], 30).map(@ + 100)';
384+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 5) ? (@ == 3)]';
385+
select jsonb '[1,2,3,4,5]' @* '$[(0, $[*], 3) ? (@ == 3)]';

0 commit comments

Comments
 (0)