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

Commit 9e62bfb

Browse files
author
Nikita Glukhov
committed
Add jsonpath .map() item method
1 parent 39f9952 commit 9e62bfb

File tree

7 files changed

+125
-3
lines changed

7 files changed

+125
-3
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
137137
case jpiPlus:
138138
case jpiMinus:
139139
case jpiExists:
140+
case jpiMap:
140141
{
141142
int32 arg;
142143

@@ -145,6 +146,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
145146

146147
chld = flattenJsonPathParseItem(buf, item->value.arg,
147148
item->type == jpiFilter ||
149+
item->type == jpiMap ||
148150
allowCurrent,
149151
insideArraySubscript);
150152
*(int32*)(buf->data + arg) = chld;
@@ -551,6 +553,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
551553
case jpiKeyValue:
552554
appendBinaryStringInfo(buf, ".keyvalue()", 11);
553555
break;
556+
case jpiMap:
557+
appendBinaryStringInfo(buf, ".map(", 5);
558+
jspGetArg(v, &elem);
559+
printJsonPathItem(buf, &elem, false, false);
560+
appendStringInfoChar(buf, ')');
561+
break;
554562
default:
555563
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
556564
}
@@ -684,6 +692,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
684692
case jpiMinus:
685693
case jpiFilter:
686694
case jpiDatetime:
695+
case jpiMap:
687696
read_int32(v->content.arg, base, pos);
688697
break;
689698
case jpiIndexArray:
@@ -710,7 +719,8 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
710719
v->type == jpiExists ||
711720
v->type == jpiPlus ||
712721
v->type == jpiMinus ||
713-
v->type == jpiDatetime
722+
v->type == jpiDatetime ||
723+
v->type == jpiMap
714724
);
715725

716726
jspInitByBuffer(a, v->base, v->content.arg);
@@ -762,7 +772,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
762772
v->type == jpiDouble ||
763773
v->type == jpiDatetime ||
764774
v->type == jpiKeyValue ||
765-
v->type == jpiStartsWith
775+
v->type == jpiStartsWith ||
776+
v->type == jpiMap
766777
);
767778

768779
if (a)

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,85 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20572057
res = executeLikeRegexPredicate(cxt, jsp, jb);
20582058
res = appendBoolResult(cxt, jsp, found, res, needBool);
20592059
break;
2060+
case jpiMap:
2061+
if (JsonbType(jb) != jbvArray)
2062+
{
2063+
if (cxt->lax)
2064+
{
2065+
JsonValueList reslist = { 0 };
2066+
2067+
jspGetArg(jsp, &elem);
2068+
res = recursiveExecute(cxt, &elem, jb, &reslist);
2069+
2070+
if (jperIsError(res))
2071+
return res;
2072+
2073+
if (JsonValueListLength(&reslist) != 1)
2074+
return jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2075+
2076+
res = recursiveExecuteNext(cxt, jsp, NULL,
2077+
JsonValueListHead(&reslist),
2078+
found, true);
2079+
}
2080+
else
2081+
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
2082+
}
2083+
else
2084+
{
2085+
JsonbValue element_buf;
2086+
JsonbValue *element;
2087+
JsonbIterator *it = NULL;
2088+
JsonbIteratorToken tok;
2089+
JsonValueList result = { 0 };
2090+
int size = JsonbArraySize(jb);
2091+
int i;
2092+
2093+
jspGetArg(jsp, &elem);
2094+
2095+
if (jb->type == jbvBinary && size > 0)
2096+
{
2097+
element = &element_buf;
2098+
it = JsonbIteratorInit(jb->val.binary.data);
2099+
tok = JsonbIteratorNext(&it, &element_buf, false);
2100+
if (tok != WJB_BEGIN_ARRAY)
2101+
elog(ERROR, "unexpected jsonb token at the array start");
2102+
}
2103+
2104+
for (i = 0; i < size; i++)
2105+
{
2106+
JsonValueList reslist = { 0 };
2107+
2108+
if (it)
2109+
{
2110+
tok = JsonbIteratorNext(&it, element, true);
2111+
if (tok != WJB_ELEM)
2112+
break;
2113+
}
2114+
else
2115+
element = &jb->val.array.elems[i];
2116+
2117+
res = recursiveExecute(cxt, &elem, element, &reslist);
2118+
2119+
if (jperIsError(res))
2120+
break;
2121+
2122+
if (JsonValueListLength(&reslist) != 1)
2123+
{
2124+
res = jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2125+
break;
2126+
}
2127+
2128+
JsonValueListConcat(&result, reslist);
2129+
}
2130+
2131+
if (jperIsError(res))
2132+
break;
2133+
2134+
res = recursiveExecuteNext(cxt, jsp, NULL,
2135+
wrapItemsInArray(&result),
2136+
found, false);
2137+
}
2138+
break;
20602139
default:
20612140
elog(ERROR,"2Wrong state: %d", jsp->type);
20622141
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ makeItemLikeRegex(JsonPathParseItem *expr, string *pattern, string *flags)
266266
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
267267
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
268268
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P DATETIME_P
269-
%token <str> KEYVALUE_P
269+
%token <str> KEYVALUE_P MAP_P
270270

271271
%type <result> result
272272

@@ -425,6 +425,8 @@ accessor_op:
425425
| '.' method '(' ')' { $$ = makeItemType($2); }
426426
| '.' DATETIME_P '(' opt_datetime_template ')'
427427
{ $$ = makeItemUnary(jpiDatetime, $4); }
428+
| '.' MAP_P '(' expr_or_predicate ')'
429+
{ $$ = makeItemUnary(jpiMap, $4); }
428430
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
429431
;
430432

@@ -462,6 +464,7 @@ key_name:
462464
| WITH_P
463465
| LIKE_REGEX_P
464466
| FLAG_P
467+
| MAP_P
465468
;
466469

467470
method:

src/backend/utils/adt/jsonpath_scan.l

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ static keyword keywords[] = {
273273
{ 2, false, TO_P, "to"},
274274
{ 3, false, ABS_P, "abs"},
275275
{ 3, false, LAX_P, "lax"},
276+
{ 3, false, MAP_P, "map"},
276277
{ 4, false, FLAG_P, "flag"},
277278
{ 4, false, LAST_P, "last"},
278279
{ 4, true, NULL_P, "null"},

src/include/utils/jsonpath.h

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

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

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,3 +1651,24 @@ SELECT jsonb '[{"a": 1}, {"a": 2}]' @~ '$[*].a > 2';
16511651
f
16521652
(1 row)
16531653

1654+
-- extension: map item method
1655+
select jsonb '1' @* 'strict $.map(@ + 10)';
1656+
ERROR: SQL/JSON array not found
1657+
select jsonb '1' @* 'lax $.map(@ + 10)';
1658+
?column?
1659+
----------
1660+
11
1661+
(1 row)
1662+
1663+
select jsonb '[1, 2, 3]' @* '$.map(@ + 10)';
1664+
?column?
1665+
--------------
1666+
[11, 12, 13]
1667+
(1 row)
1668+
1669+
select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.map(@.map(@ + 10))';
1670+
?column?
1671+
----------------------------------------
1672+
[[11, 12], [13, 14, 15], [], [16, 17]]
1673+
(1 row)
1674+

src/test/regress/sql/jsonb_jsonpath.sql

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

370370
SELECT jsonb '[{"a": 1}, {"a": 2}]' @~ '$[*].a > 1';
371371
SELECT jsonb '[{"a": 1}, {"a": 2}]' @~ '$[*].a > 2';
372+
373+
-- extension: map item method
374+
select jsonb '1' @* 'strict $.map(@ + 10)';
375+
select jsonb '1' @* 'lax $.map(@ + 10)';
376+
select jsonb '[1, 2, 3]' @* '$.map(@ + 10)';
377+
select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.map(@.map(@ + 10))';

0 commit comments

Comments
 (0)