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

Commit 17fe932

Browse files
author
Nikita Glukhov
committed
Add jsonpath .reduce() and .fold() item methods
1 parent 0c6b642 commit 17fe932

File tree

9 files changed

+334
-12
lines changed

9 files changed

+334
-12
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
3030
/* position from begining of jsonpath data */
3131
int32 pos = buf->len - JSONPATH_HDRSZ;
3232
int32 chld, next;
33+
bool allowCurrentInArg = false;
3334

3435
check_stack_depth();
3536

@@ -62,6 +63,11 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
6263
appendBinaryStringInfo(buf, (char*)&item->value.boolean,
6364
sizeof(item->value.boolean));
6465
break;
66+
case jpiFold:
67+
case jpiFoldl:
68+
case jpiFoldr:
69+
allowCurrentInArg = true;
70+
/* fall through */
6571
case jpiAnd:
6672
case jpiOr:
6773
case jpiEqual:
@@ -90,23 +96,28 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
9096
appendBinaryStringInfo(buf, (char*)&right /* fake value */, sizeof(right));
9197

9298
chld = flattenJsonPathParseItem(buf, item->value.args.left,
93-
allowCurrent,
99+
allowCurrent ||
100+
allowCurrentInArg,
94101
insideArraySubscript);
95102
*(int32*)(buf->data + left) = chld;
96103
chld = flattenJsonPathParseItem(buf, item->value.args.right,
97-
allowCurrent,
104+
allowCurrent ||
105+
allowCurrentInArg,
98106
insideArraySubscript);
99107
*(int32*)(buf->data + right) = chld;
100108
}
101109
break;
102110
case jpiFilter:
111+
case jpiMap:
112+
case jpiReduce:
113+
allowCurrentInArg = true;
114+
/* fall through */
103115
case jpiIsUnknown:
104116
case jpiNot:
105117
case jpiPlus:
106118
case jpiMinus:
107119
case jpiExists:
108120
case jpiDatetime:
109-
case jpiMap:
110121
case jpiArray:
111122
{
112123
int32 arg = item->value.arg ? buf->len : 0;
@@ -117,9 +128,8 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
117128
break;
118129

119130
chld = flattenJsonPathParseItem(buf, item->value.arg,
120-
item->type == jpiFilter ||
121-
item->type == jpiMap ||
122-
allowCurrent,
131+
allowCurrent ||
132+
allowCurrentInArg,
123133
insideArraySubscript);
124134
*(int32*)(buf->data + arg) = chld;
125135
}
@@ -604,6 +614,28 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
604614

605615
appendStringInfoChar(buf, '}');
606616
break;
617+
case jpiReduce:
618+
appendBinaryStringInfo(buf, ".reduce(", 8);
619+
jspGetArg(v, &elem);
620+
printJsonPathItem(buf, &elem, false, false);
621+
appendStringInfoChar(buf, ')');
622+
break;
623+
case jpiFold:
624+
case jpiFoldl:
625+
case jpiFoldr:
626+
if (v->type == jpiFold)
627+
appendBinaryStringInfo(buf, ".fold(", 6);
628+
else if (v->type == jpiFoldl)
629+
appendBinaryStringInfo(buf, ".foldl(", 7);
630+
else
631+
appendBinaryStringInfo(buf, ".foldr(", 7);
632+
jspGetLeftArg(v, &elem);
633+
printJsonPathItem(buf, &elem, false, false);
634+
appendBinaryStringInfo(buf, ", ", 2);
635+
jspGetRightArg(v, &elem);
636+
printJsonPathItem(buf, &elem, false, false);
637+
appendStringInfoChar(buf, ')');
638+
break;
607639
default:
608640
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
609641
}
@@ -721,6 +753,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
721753
case jpiLessOrEqual:
722754
case jpiGreaterOrEqual:
723755
case jpiStartsWith:
756+
case jpiFold:
757+
case jpiFoldl:
758+
case jpiFoldr:
724759
read_int32(v->content.args.left, base, pos);
725760
read_int32(v->content.args.right, base, pos);
726761
break;
@@ -733,6 +768,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
733768
case jpiDatetime:
734769
case jpiMap:
735770
case jpiArray:
771+
case jpiReduce:
736772
read_int32(v->content.arg, base, pos);
737773
break;
738774
case jpiIndexArray:
@@ -771,7 +807,8 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
771807
v->type == jpiMinus ||
772808
v->type == jpiDatetime ||
773809
v->type == jpiMap ||
774-
v->type == jpiArray
810+
v->type == jpiArray ||
811+
v->type == jpiReduce
775812
);
776813

777814
jspInitByBuffer(a, v->base, v->content.arg);
@@ -827,7 +864,11 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
827864
v->type == jpiMap ||
828865
v->type == jpiSequence ||
829866
v->type == jpiArray ||
830-
v->type == jpiObject
867+
v->type == jpiObject ||
868+
v->type == jpiReduce ||
869+
v->type == jpiFold ||
870+
v->type == jpiFoldl ||
871+
v->type == jpiFoldr
831872
);
832873

833874
if (a)
@@ -855,7 +896,10 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
855896
v->type == jpiMul ||
856897
v->type == jpiDiv ||
857898
v->type == jpiMod ||
858-
v->type == jpiStartsWith
899+
v->type == jpiStartsWith ||
900+
v->type == jpiFold ||
901+
v->type == jpiFoldl ||
902+
v->type == jpiFoldr
859903
);
860904

861905
jspInitByBuffer(a, v->base, v->content.args.left);
@@ -878,7 +922,10 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
878922
v->type == jpiMul ||
879923
v->type == jpiDiv ||
880924
v->type == jpiMod ||
881-
v->type == jpiStartsWith
925+
v->type == jpiStartsWith ||
926+
v->type == jpiFold ||
927+
v->type == jpiFoldl ||
928+
v->type == jpiFoldr
882929
);
883930

884931
jspInitByBuffer(a, v->base, v->content.args.right);

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ static inline JsonPathExecResult recursiveExecuteUnwrap(JsonPathExecContext *cxt
5555

5656
static inline JsonbValue *wrapItemsInArray(const JsonValueList *items);
5757

58+
static inline JsonbValue *wrapItem(JsonbValue *jbv);
59+
60+
static Datum returnDATUM(void *arg, bool *isNull);
5861

5962
static inline void
6063
JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
@@ -285,6 +288,9 @@ computeJsonPathVariable(JsonPathItem *variable, List *vars, JsonbValue *value)
285288
JsonbInitBinary(value, jb);
286289
}
287290
break;
291+
case (Oid) -1: /* JsonbValue */
292+
*value = *(JsonbValue *) DatumGetPointer(computedValue);
293+
break;
288294
default:
289295
ereport(ERROR,
290296
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2090,6 +2096,160 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20902096
res = recursiveExecuteNext(cxt, jsp, NULL, obj, found, false);
20912097
}
20922098
break;
2099+
case jpiReduce:
2100+
case jpiFold:
2101+
case jpiFoldl:
2102+
case jpiFoldr:
2103+
if (JsonbType(jb) != jbvArray)
2104+
{
2105+
if (cxt->lax)
2106+
{
2107+
if (jsp->type == jpiReduce)
2108+
res = recursiveExecuteNext(cxt, jsp, NULL, jb, found, true);
2109+
else
2110+
res = recursiveExecute(cxt, jsp, wrapItem(jb), found);
2111+
}
2112+
else
2113+
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
2114+
}
2115+
else
2116+
{
2117+
JsonbValue jbv;
2118+
JsonbValue *result = NULL;
2119+
int size = JsonbArraySize(jb);
2120+
int i;
2121+
bool foldr = jsp->type == jpiFoldr;
2122+
2123+
if (jsp->type == jpiReduce)
2124+
jspGetArg(jsp, &elem);
2125+
else
2126+
{
2127+
JsonValueList reslist = { 0 };
2128+
2129+
jspGetRightArg(jsp, &elem);
2130+
res = recursiveExecute(cxt, &elem, jb, &reslist);
2131+
2132+
if (jperIsError(res))
2133+
return res;
2134+
2135+
if (JsonValueListLength(&reslist) != 1)
2136+
return jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2137+
2138+
result = JsonValueListHead(&reslist);
2139+
2140+
jspGetLeftArg(jsp, &elem);
2141+
}
2142+
2143+
if (jsp->type == jpiReduce && size == 1)
2144+
{
2145+
if (jb->type == jbvBinary)
2146+
{
2147+
result = getIthJsonbValueFromContainer(jb->val.binary.data, 0);
2148+
if (!result)
2149+
{
2150+
res = jperNotFound;
2151+
break;
2152+
}
2153+
}
2154+
else
2155+
{
2156+
Assert(jb->type == jbvArray);
2157+
result = &jb->val.array.elems[0];
2158+
}
2159+
}
2160+
else if (size)
2161+
{
2162+
JsonPathVariable *v1 = palloc(sizeof(*v1));
2163+
JsonPathVariable *v2 = palloc(sizeof(*v2));
2164+
JsonbIterator *it = NULL;
2165+
JsonbIteratorToken tok;
2166+
JsonbValue *element;
2167+
2168+
if (jb->type == jbvBinary)
2169+
{
2170+
if (foldr)
2171+
{
2172+
/* unpack array for reverse iteration */
2173+
JsonbParseState *ps = NULL;
2174+
2175+
jb = pushJsonbValue(&ps, WJB_ELEM, jb);
2176+
}
2177+
else
2178+
{
2179+
element = &jbv;
2180+
it = JsonbIteratorInit(jb->val.binary.data);
2181+
tok = JsonbIteratorNext(&it, &jbv, false);
2182+
if (tok != WJB_BEGIN_ARRAY)
2183+
elog(ERROR, "unexpected jsonb token at the array start");
2184+
}
2185+
}
2186+
2187+
v1->cb = returnDATUM;
2188+
v2->cb = returnDATUM;
2189+
v1->varName = cstring_to_text_with_len("1", 1);
2190+
v2->varName = cstring_to_text_with_len("2", 1);
2191+
v1->typid = (Oid) -1; /* raw JsonbValue */
2192+
v2->typid = (Oid) -1;
2193+
v1->typmod = -1;
2194+
v2->typmod = -1;
2195+
2196+
cxt->vars = lcons(v1, lcons(v2, cxt->vars));
2197+
2198+
if (foldr)
2199+
{
2200+
/* swap $1 and $2 for foldr() */
2201+
JsonPathVariable *tmp = v1;
2202+
2203+
v1 = v2;
2204+
v2 = tmp;
2205+
}
2206+
2207+
for (i = 0; i < size; i++)
2208+
{
2209+
JsonValueList reslist = { 0 };
2210+
2211+
if (it)
2212+
{
2213+
tok = JsonbIteratorNext(&it, element, true);
2214+
if (tok != WJB_ELEM)
2215+
break;
2216+
}
2217+
else if (foldr)
2218+
element = &jb->val.array.elems[size - i - 1];
2219+
else
2220+
element = &jb->val.array.elems[i];
2221+
2222+
if (!i && jsp->type == jpiReduce)
2223+
{
2224+
result = copyJsonbValue(element);
2225+
continue;
2226+
}
2227+
2228+
v1->cb_arg = result;
2229+
v2->cb_arg = element;
2230+
2231+
res = recursiveExecute(cxt, &elem, jb, &reslist);
2232+
2233+
if (jperIsError(res))
2234+
return res;
2235+
2236+
if (JsonValueListLength(&reslist) != 1)
2237+
return jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2238+
2239+
result = JsonValueListHead(&reslist);
2240+
}
2241+
2242+
cxt->vars = list_delete_first(list_delete_first(cxt->vars));
2243+
}
2244+
else if (jsp->type == jpiReduce)
2245+
{
2246+
res = jperNotFound;
2247+
break;
2248+
}
2249+
2250+
res = recursiveExecuteNext(cxt, jsp, NULL, result, found, false);
2251+
}
2252+
break;
20932253
default:
20942254
elog(ERROR,"2Wrong state: %d", jsp->type);
20952255
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ makeItemObject(List *fields)
245245
%token <str> TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P STARTS_P WITH_P
246246
%token <str> STRING_P NUMERIC_P INT_P EXISTS_P STRICT_P LAX_P LAST_P
247247
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P DATETIME_P
248-
%token <str> KEYVALUE_P MAP_P
248+
%token <str> KEYVALUE_P MAP_P REDUCE_P FOLD_P FOLDL_P FOLDR_P
249249

250250
%token <str> OR_P AND_P NOT_P
251251
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
@@ -262,7 +262,7 @@ makeItemObject(List *fields)
262262

263263
%type <indexs> index_list
264264

265-
%type <optype> comp_op method
265+
%type <optype> comp_op method fold
266266

267267
%type <boolean> mode
268268

@@ -322,6 +322,7 @@ scalar_value:
322322
| NUMERIC_P { $$ = makeItemNumeric(&$1); }
323323
| INT_P { $$ = makeItemNumeric(&$1); }
324324
| '$' STRING_P { $$ = makeItemVariable(&$2); }
325+
| '$' INT_P { $$ = makeItemVariable(&$2); }
325326
;
326327

327328
comp_op:
@@ -439,9 +440,19 @@ accessor_op:
439440
{ $$ = makeItemUnary(jpiDatetime, $4); }
440441
| '.' MAP_P '(' expr_or_predicate ')'
441442
{ $$ = makeItemUnary(jpiMap, $4); }
443+
| '.' REDUCE_P '(' expr_or_predicate ')'
444+
{ $$ = makeItemUnary(jpiReduce, $4); }
445+
| '.' fold '(' expr_or_predicate ',' expr_or_predicate ')'
446+
{ $$ = makeItemBinary($2, $4, $6); }
442447
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
443448
;
444449

450+
fold:
451+
FOLD_P { $$ = jpiFold; }
452+
| FOLDL_P { $$ = jpiFoldl; }
453+
| FOLDR_P { $$ = jpiFoldr; }
454+
;
455+
445456
opt_datetime_template:
446457
STRING_P { $$ = makeItemString(&$1); }
447458
| /* EMPTY */ { $$ = NULL; }
@@ -475,6 +486,10 @@ key_name:
475486
| STARTS_P
476487
| WITH_P
477488
| MAP_P
489+
| REDUCE_P
490+
| FOLD_P
491+
| FOLDL_P
492+
| FOLDR_P
478493
;
479494

480495
method:

0 commit comments

Comments
 (0)