Skip to content

Commit 5336bd8

Browse files
author
Nikita Glukhov
committed
Add jsonpath array constructors
1 parent bbb1f1e commit 5336bd8

File tree

9 files changed

+136
-6
lines changed

9 files changed

+136
-6
lines changed

doc/src/sgml/func.sgml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13694,6 +13694,13 @@ table2-mapping
1369413694
<entry><literal>pg $[*], 4, 5</literal></entry>
1369513695
<entry><literal>1, 2, 3, 4, 5</literal></entry>
1369613696
</row>
13697+
<row>
13698+
<entry>Array constructor</entry>
13699+
<entry>Construct a JSON array by enumeration of its elements enclosed in brackets</entry>
13700+
<entry><literal>[1, 2, 3]</literal></entry>
13701+
<entry><literal>pg [$[*], 4, 5]</literal></entry>
13702+
<entry><literal>[1, 2, 3, 4, 5]</literal></entry>
13703+
</row>
1369713704
</tbody>
1369813705
</tgroup>
1369913706
</table>

src/backend/utils/adt/jsonpath.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,13 +368,19 @@ flattenJsonPathParseItem(JsonPathEncodingContext *cxt, JsonPathParseItem *item,
368368
case jpiMinus:
369369
case jpiExists:
370370
case jpiDatetime:
371+
case jpiArray:
371372
{
372373
int32 arg = reserveSpaceForItemPointer(buf);
373374

374-
chld = !item->value.arg ? pos :
375-
flattenJsonPathParseItem(cxt, item->value.arg,
376-
nestingLevel + argNestingLevel,
377-
insideArraySubscript);
375+
if (item->type == jpiArray)
376+
checkJsonPathExtensionsEnabled(cxt, item->type);
377+
378+
if (!item->value.arg)
379+
break;
380+
381+
chld = flattenJsonPathParseItem(cxt, item->value.arg,
382+
nestingLevel + argNestingLevel,
383+
insideArraySubscript);
378384
*(int32 *) (buf->data + arg) = chld - pos;
379385
}
380386
break;
@@ -780,6 +786,15 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
780786
if (printBracketes || jspHasNext(v))
781787
appendStringInfoChar(buf, ')');
782788
break;
789+
case jpiArray:
790+
appendStringInfoChar(buf, '[');
791+
if (v->content.arg)
792+
{
793+
jspGetArg(v, &elem);
794+
printJsonPathItem(buf, &elem, false, false);
795+
}
796+
appendStringInfoChar(buf, ']');
797+
break;
783798
default:
784799
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
785800
}
@@ -979,6 +994,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
979994
case jpiMinus:
980995
case jpiFilter:
981996
case jpiDatetime:
997+
case jpiArray:
982998
read_int32(v->content.arg, base, pos);
983999
break;
9841000
case jpiIndexArray:
@@ -1009,7 +1025,8 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
10091025
v->type == jpiExists ||
10101026
v->type == jpiPlus ||
10111027
v->type == jpiMinus ||
1012-
v->type == jpiDatetime);
1028+
v->type == jpiDatetime ||
1029+
v->type == jpiArray);
10131030

10141031
jspInitByBuffer(a, v->base, v->content.arg);
10151032
}
@@ -1060,7 +1077,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
10601077
v->type == jpiDatetime ||
10611078
v->type == jpiKeyValue ||
10621079
v->type == jpiStartsWith ||
1063-
v->type == jpiSequence);
1080+
v->type == jpiSequence ||
1081+
v->type == jpiArray);
10641082

10651083
if (a)
10661084
jspInitByBuffer(a, v->base, v->nextPos);

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,25 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
11751175
break;
11761176
}
11771177

1178+
case jpiArray:
1179+
{
1180+
JsonValueList list = {0};
1181+
JsonbValue *arr;
1182+
1183+
if (jsp->content.arg)
1184+
{
1185+
jspGetArg(jsp, &elem);
1186+
res = executeItem(cxt, &elem, jb, &list);
1187+
1188+
if (jperIsError(res))
1189+
break;
1190+
}
1191+
1192+
arr = wrapItemsInArray(&list);
1193+
res = executeNextItem(cxt, jsp, NULL, arr, found, false);
1194+
break;
1195+
}
1196+
11781197
default:
11791198
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
11801199
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ path_primary:
217217
| '@' { $$ = makeItemType(jpiCurrent); }
218218
| LAST_P { $$ = makeItemType(jpiLast); }
219219
| '(' expr_seq ')' { $$ = $2; }
220+
| '[' ']' { $$ = makeItemUnary(jpiArray, NULL); }
221+
| '[' expr_or_seq ']' { $$ = makeItemUnary(jpiArray, $2); }
220222
;
221223

222224
accessor_expr:

src/include/utils/jsonpath.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ typedef enum JsonPathItemType
8888
jpiStartsWith, /* STARTS WITH predicate */
8989
jpiLikeRegex, /* LIKE_REGEX predicate */
9090
jpiSequence, /* sequence constructor: 'expr, ...' */
91+
jpiArray, /* array constructor: '[expr, ...]' */
9192
} JsonPathItemType;
9293

9394
/* XQuery regex mode flags for LIKE_REGEX predicate */

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,12 @@ SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*] ? (@.a > 10)');
21992199
------------------
22002200
(0 rows)
22012201

2202+
SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
2203+
jsonb_path_query
2204+
------------------
2205+
[1, 2]
2206+
(1 row)
2207+
22022208
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
22032209
ERROR: JSON object does not contain key "a"
22042210
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a');
@@ -2231,6 +2237,12 @@ SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].
22312237
[]
22322238
(1 row)
22332239

2240+
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
2241+
jsonb_path_query_array
2242+
------------------------
2243+
[[1, 2]]
2244+
(1 row)
2245+
22342246
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
22352247
ERROR: JSON object does not contain key "a"
22362248
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a', silent => true);
@@ -2578,3 +2590,48 @@ select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 5) ? (@ == 3)]');
25782590

25792591
select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 3) ? (@ == 3)]');
25802592
ERROR: jsonpath array subscript is not a single numeric value
2593+
-- extension: array constructors
2594+
select jsonb_path_query('[1, 2, 3]', 'pg []');
2595+
jsonb_path_query
2596+
------------------
2597+
[]
2598+
(1 row)
2599+
2600+
select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5]');
2601+
jsonb_path_query
2602+
-----------------------
2603+
[1, 2, 1, 2, 3, 4, 5]
2604+
(1 row)
2605+
2606+
select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5][*]');
2607+
jsonb_path_query
2608+
------------------
2609+
1
2610+
2
2611+
1
2612+
2
2613+
3
2614+
4
2615+
5
2616+
(7 rows)
2617+
2618+
select jsonb_path_query('[1, 2, 3]', 'pg [(1, (2, $[*])), (4, 5)]');
2619+
jsonb_path_query
2620+
-----------------------
2621+
[1, 2, 1, 2, 3, 4, 5]
2622+
(1 row)
2623+
2624+
select jsonb_path_query('[1, 2, 3]', 'pg [[1, 2], [$[*], 4], 5, [(1,2)?(@ > 5)]]');
2625+
jsonb_path_query
2626+
-------------------------------
2627+
[[1, 2], [1, 2, 3, 4], 5, []]
2628+
(1 row)
2629+
2630+
select jsonb_path_query('[1, 2, 3]', 'pg strict [1, 2, $[*].a, 4, 5]');
2631+
ERROR: jsonpath member accessor can only be applied to an object
2632+
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'pg [$[*][*] ? (@ > 3)]');
2633+
jsonb_path_query
2634+
------------------
2635+
[4, 5, 6, 7]
2636+
(1 row)
2637+

src/test/regress/expected/jsonpath.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,18 @@ select 'pg $[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
604604
pg $[(1, (2, $."a")),3,(4, 5)]
605605
(1 row)
606606

607+
select 'pg []'::jsonpath;
608+
jsonpath
609+
----------
610+
pg []
611+
(1 row)
612+
613+
select 'pg [[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
614+
jsonpath
615+
---------------------------------------------
616+
pg [[1, 2], ([(3, 4, 5), 6], []), $."a"[*]]
617+
(1 row)
618+
607619
select '$ ? (@.a < 1)'::jsonpath;
608620
jsonpath
609621
---------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,15 @@ set time zone default;
525525

526526
SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*]');
527527
SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', '$[*] ? (@.a > 10)');
528+
SELECT jsonb_path_query('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
528529

529530
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
530531
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a');
531532
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ == 1)');
532533
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 10)');
533534
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 1, "max": 4}');
534535
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*].a ? (@ > $min && @ < $max)', vars => '{"min": 3, "max": 4}');
536+
SELECT jsonb_path_query_array('[{"a": 1}, {"a": 2}]', 'pg [$[*].a]');
535537

536538
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a');
537539
SELECT jsonb_path_query_first('[{"a": 1}, {"a": 2}, {}]', 'strict $[*].a', silent => true);
@@ -587,3 +589,12 @@ select jsonb_path_query('[1,2,3,4,5]', 'pg -(10, 20, $[1 to 3], 30)');
587589
select jsonb_path_query('[1,2,3,4,5]', 'pg lax (10, 20.5, $[1 to 3], "30").double()');
588590
select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 5) ? (@ == 3)]');
589591
select jsonb_path_query('[1,2,3,4,5]', 'pg $[(0, $[*], 3) ? (@ == 3)]');
592+
593+
-- extension: array constructors
594+
select jsonb_path_query('[1, 2, 3]', 'pg []');
595+
select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5]');
596+
select jsonb_path_query('[1, 2, 3]', 'pg [1, 2, $[*], 4, 5][*]');
597+
select jsonb_path_query('[1, 2, 3]', 'pg [(1, (2, $[*])), (4, 5)]');
598+
select jsonb_path_query('[1, 2, 3]', 'pg [[1, 2], [$[*], 4], 5, [(1,2)?(@ > 5)]]');
599+
select jsonb_path_query('[1, 2, 3]', 'pg strict [1, 2, $[*].a, 4, 5]');
600+
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'pg [$[*][*] ? (@ > 3)]');

src/test/regress/sql/jsonpath.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ select 'pg (1, 2, $.a) == 5'::jsonpath;
113113
select 'pg $[(1, 2, $.a) to (3, 4)]'::jsonpath;
114114
select 'pg $[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
115115
116+
select 'pg []'::jsonpath;
117+
select 'pg [[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
118+
116119
select '$ ? (@.a < 1)'::jsonpath;
117120
select '$ ? (@.a < -1)'::jsonpath;
118121
select '$ ? (@.a < +1)'::jsonpath;

0 commit comments

Comments
 (0)