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

Commit a1dc87d

Browse files
author
Nikita Glukhov
committed
Add outer jsonpath item references
1 parent 5509fb1 commit a1dc87d

File tree

9 files changed

+224
-27
lines changed

9 files changed

+224
-27
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
*/
2626
static int
2727
flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
28-
bool allowCurrent, bool insideArraySubscript)
28+
int nestingLevel, bool insideArraySubscript)
2929
{
3030
/* position from begining of jsonpath data */
3131
int32 pos = buf->len - JSONPATH_HDRSZ;
3232
int32 chld, next;
33-
bool allowCurrentInArg = false;
33+
int argNestingLevel = 0;
3434

3535
check_stack_depth();
3636

@@ -66,7 +66,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
6666
case jpiFold:
6767
case jpiFoldl:
6868
case jpiFoldr:
69-
allowCurrentInArg = true;
69+
argNestingLevel = 1;
7070
/* fall through */
7171
case jpiAnd:
7272
case jpiOr:
@@ -96,13 +96,11 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
9696
appendBinaryStringInfo(buf, (char*)&right /* fake value */, sizeof(right));
9797

9898
chld = flattenJsonPathParseItem(buf, item->value.args.left,
99-
allowCurrent ||
100-
allowCurrentInArg,
99+
nestingLevel + argNestingLevel,
101100
insideArraySubscript);
102101
*(int32*)(buf->data + left) = chld - pos;
103102
chld = flattenJsonPathParseItem(buf, item->value.args.right,
104-
allowCurrent ||
105-
allowCurrentInArg,
103+
nestingLevel + argNestingLevel,
106104
insideArraySubscript);
107105
*(int32*)(buf->data + right) = chld - pos;
108106
}
@@ -125,15 +123,17 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
125123
appendStringInfoChar(buf, '\0');
126124

127125
chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
128-
allowCurrent,
126+
nestingLevel,
129127
insideArraySubscript);
130128
*(int32 *)(buf->data + offs) = chld - pos;
131129
}
132130
break;
133-
case jpiFilter:
134131
case jpiMap:
132+
argNestingLevel++;
133+
/* fall through */
134+
case jpiFilter:
135135
case jpiReduce:
136-
allowCurrentInArg = true;
136+
argNestingLevel++;
137137
/* fall through */
138138
case jpiIsUnknown:
139139
case jpiNot:
@@ -151,8 +151,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
151151
break;
152152

153153
chld = flattenJsonPathParseItem(buf, item->value.arg,
154-
allowCurrent ||
155-
allowCurrentInArg,
154+
nestingLevel + argNestingLevel,
156155
insideArraySubscript);
157156
*(int32*)(buf->data + arg) = chld - pos;
158157
}
@@ -164,8 +163,18 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
164163
case jpiAnyArray:
165164
case jpiAnyKey:
166165
break;
166+
case jpiCurrentN:
167+
if (item->value.current.level < 0 ||
168+
item->value.current.level >= nestingLevel)
169+
ereport(ERROR,
170+
(errcode(ERRCODE_SYNTAX_ERROR),
171+
errmsg("invalid outer item reference in jsonpath @")));
172+
173+
appendBinaryStringInfo(buf, (char *) &item->value.current.level,
174+
sizeof(item->value.current.level));
175+
break;
167176
case jpiCurrent:
168-
if (!allowCurrent)
177+
if (nestingLevel <= 0)
169178
ereport(ERROR,
170179
(errcode(ERRCODE_SYNTAX_ERROR),
171180
errmsg("@ is not allowed in root expressions")));
@@ -195,12 +204,12 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
195204
int32 frompos =
196205
flattenJsonPathParseItem(buf,
197206
item->value.array.elems[i].from,
198-
true, true) - pos;
207+
nestingLevel + 1, true) - pos;
199208

200209
if (item->value.array.elems[i].to)
201210
topos = flattenJsonPathParseItem(buf,
202211
item->value.array.elems[i].to,
203-
true, true) - pos;
212+
nestingLevel + 1, true) - pos;
204213
else
205214
topos = 0;
206215

@@ -245,7 +254,8 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
245254
{
246255
int32 elempos =
247256
flattenJsonPathParseItem(buf, lfirst(lc),
248-
allowCurrent, insideArraySubscript);
257+
nestingLevel,
258+
insideArraySubscript);
249259

250260
*(int32 *) &buf->data[offset] = elempos - pos;
251261
offset += sizeof(int32);
@@ -269,11 +279,11 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
269279
JsonPathParseItem *field = lfirst(lc);
270280
int32 keypos =
271281
flattenJsonPathParseItem(buf, field->value.args.left,
272-
allowCurrent,
282+
nestingLevel,
273283
insideArraySubscript);
274284
int32 valpos =
275285
flattenJsonPathParseItem(buf, field->value.args.right,
276-
allowCurrent,
286+
nestingLevel,
277287
insideArraySubscript);
278288
int32 *ppos = (int32 *) &buf->data[offset];
279289

@@ -290,7 +300,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
290300

291301
if (item->next)
292302
*(int32*)(buf->data + next) =
293-
flattenJsonPathParseItem(buf, item->next, allowCurrent,
303+
flattenJsonPathParseItem(buf, item->next, nestingLevel,
294304
insideArraySubscript) - pos;
295305

296306
return pos;
@@ -315,7 +325,7 @@ jsonpath_in(PG_FUNCTION_ARGS)
315325
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
316326
errmsg("invalid input syntax for jsonpath: \"%s\"", in)));
317327

318-
flattenJsonPathParseItem(&buf, jsonpath->expr, false, false);
328+
flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
319329

320330
res = (JsonPath*)buf.data;
321331
SET_VARSIZE(res, buf.len);
@@ -534,6 +544,10 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
534544
Assert(!inKey);
535545
appendStringInfoChar(buf, '@');
536546
break;
547+
case jpiCurrentN:
548+
Assert(!inKey);
549+
appendStringInfo(buf, "@%d", v->content.current.level);
550+
break;
537551
case jpiRoot:
538552
Assert(!inKey);
539553
appendStringInfoChar(buf, '$');
@@ -787,6 +801,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
787801
case jpiMin:
788802
case jpiMax:
789803
break;
804+
case jpiCurrentN:
805+
read_int32(v->content.current.level, base, pos);
806+
break;
790807
case jpiKey:
791808
case jpiString:
792809
case jpiVariable:
@@ -894,6 +911,7 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
894911
v->type == jpiIndexArray ||
895912
v->type == jpiFilter ||
896913
v->type == jpiCurrent ||
914+
v->type == jpiCurrentN ||
897915
v->type == jpiExists ||
898916
v->type == jpiRoot ||
899917
v->type == jpiVariable ||

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,20 @@
3333
#define JSONXOID JSONBOID
3434
#endif
3535

36+
typedef struct JsonItemStackEntry
37+
{
38+
JsonbValue *item;
39+
struct JsonItemStackEntry *parent;
40+
} JsonItemStackEntry;
41+
42+
typedef JsonItemStackEntry *JsonItemStack;
43+
3644
typedef struct JsonPathExecContext
3745
{
3846
List *vars;
3947
bool lax;
4048
JsonbValue *root; /* for $ evaluation */
49+
JsonItemStack stack; /* for @N evaluation */
4150
int innermostArraySize; /* for LAST array index evaluation */
4251
} JsonPathExecContext;
4352

@@ -106,6 +115,10 @@ static inline JsonPathExecResult recursiveExecute(JsonPathExecContext *cxt,
106115
static inline JsonPathExecResult recursiveExecuteBool(JsonPathExecContext *cxt,
107116
JsonPathItem *jsp, JsonbValue *jb);
108117

118+
static inline JsonPathExecResult recursiveExecuteNested(JsonPathExecContext *cxt,
119+
JsonPathItem *jsp, JsonbValue *jb,
120+
JsonValueList *found);
121+
109122
static inline JsonPathExecResult recursiveExecuteUnwrap(JsonPathExecContext *cxt,
110123
JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
111124

@@ -245,6 +258,20 @@ JsonbWrapInBinary(JsonbValue *jbv, JsonbValue *out)
245258
return JsonbInitBinary(out, jb);
246259
}
247260

261+
static inline void
262+
pushJsonItem(JsonItemStack *stack, JsonItemStackEntry *entry, JsonbValue *item)
263+
{
264+
entry->item = item;
265+
entry->parent = *stack;
266+
*stack = entry;
267+
}
268+
269+
static inline void
270+
popJsonItem(JsonItemStack *stack)
271+
{
272+
*stack = (*stack)->parent;
273+
}
274+
248275
/********************Execute functions for JsonPath***************************/
249276

250277
/*
@@ -1131,7 +1158,7 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
11311158
JsonbValue *jbv;
11321159
JsonValueList found = { 0 };
11331160
JsonbValue tmp;
1134-
JsonPathExecResult res = recursiveExecute(cxt, jsp, jb, &found);
1161+
JsonPathExecResult res = recursiveExecuteNested(cxt, jsp, jb, &found);
11351162

11361163
if (jperIsError(res))
11371164
return res;
@@ -1345,6 +1372,25 @@ appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
13451372
return recursiveExecuteNext(cxt, jsp, &next, &jbv, found, true);
13461373
}
13471374

1375+
static inline JsonPathExecResult
1376+
recursiveExecuteNested(JsonPathExecContext *cxt, JsonPathItem *jsp,
1377+
JsonbValue *jb, JsonValueList *found)
1378+
{
1379+
JsonItemStackEntry current;
1380+
JsonPathExecResult res;
1381+
1382+
pushJsonItem(&cxt->stack, &current, jb);
1383+
1384+
/* found == NULL is used here when executing boolean filter expressions */
1385+
res = found
1386+
? recursiveExecute(cxt, jsp, jb, found)
1387+
: recursiveExecuteBool(cxt, jsp, jb);
1388+
1389+
popJsonItem(&cxt->stack);
1390+
1391+
return res;
1392+
}
1393+
13481394
/*
13491395
* Main executor function: walks on jsonpath structure and tries to find
13501396
* correspoding parts of jsonb. Note, jsonb and jsonpath values should be
@@ -1457,11 +1503,29 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
14571503
jb = cxt->root;
14581504
/* fall through */
14591505
case jpiCurrent:
1506+
case jpiCurrentN:
14601507
{
14611508
JsonbValue *v;
14621509
JsonbValue vbuf;
14631510
bool copy = true;
14641511

1512+
if (jsp->type == jpiCurrentN)
1513+
{
1514+
int i;
1515+
JsonItemStackEntry *current = cxt->stack;
1516+
1517+
for (i = 0; i < jsp->content.current.level; i++)
1518+
{
1519+
current = current->parent;
1520+
1521+
if (!current)
1522+
elog(ERROR,
1523+
"invalid jsonpath current item reference");
1524+
}
1525+
1526+
jb = current->item;
1527+
}
1528+
14651529
if (JsonbType(jb) == jbvScalar)
14661530
{
14671531
if (jspHasNext(jsp))
@@ -1667,7 +1731,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
16671731
continue;
16681732
}
16691733

1670-
res = recursiveExecute(cxt, &from, jb, &keys);
1734+
res = recursiveExecuteNested(cxt, &from, jb, &keys);
16711735

16721736
if (jperIsError(res))
16731737
return res;
@@ -1818,7 +1882,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
18181882
break;
18191883
case jpiFilter:
18201884
jspGetArg(jsp, &elem);
1821-
res = recursiveExecuteBool(cxt, &elem, jb);
1885+
res = recursiveExecuteNested(cxt, &elem, jb, NULL);
18221886
if (res != jperOk)
18231887
res = jperNotFound;
18241888
else
@@ -2248,9 +2312,14 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
22482312
if (cxt->lax)
22492313
{
22502314
JsonValueList reslist = { 0 };
2315+
JsonItemStackEntry entry;
22512316

22522317
jspGetArg(jsp, &elem);
2253-
res = recursiveExecute(cxt, &elem, jb, &reslist);
2318+
2319+
/* push additional stack entry for the whole item */
2320+
pushJsonItem(&cxt->stack, &entry, jb);
2321+
res = recursiveExecuteNested(cxt, &elem, jb, &reslist);
2322+
popJsonItem(&cxt->stack);
22542323

22552324
if (jperIsError(res))
22562325
return res;
@@ -2272,6 +2341,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
22722341
JsonbIterator *it = NULL;
22732342
JsonbIteratorToken tok;
22742343
JsonValueList result = { 0 };
2344+
JsonItemStackEntry entry;
22752345
int size = JsonbArraySize(jb);
22762346
int i;
22772347

@@ -2286,6 +2356,9 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
22862356
elog(ERROR, "unexpected jsonb token at the array start");
22872357
}
22882358

2359+
/* push additional stack entry for the whole array */
2360+
pushJsonItem(&cxt->stack, &entry, jb);
2361+
22892362
for (i = 0; i < size; i++)
22902363
{
22912364
JsonValueList reslist = { 0 };
@@ -2299,7 +2372,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
22992372
else
23002373
element = &jb->val.array.elems[i];
23012374

2302-
res = recursiveExecute(cxt, &elem, element, &reslist);
2375+
res = recursiveExecuteNested(cxt, &elem, element, &reslist);
23032376

23042377
if (jperIsError(res))
23052378
break;
@@ -2313,6 +2386,8 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
23132386
JsonValueListConcat(&result, reslist);
23142387
}
23152388

2389+
popJsonItem(&cxt->stack);
2390+
23162391
if (jperIsError(res))
23172392
break;
23182393

@@ -2580,7 +2655,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
25802655
v1->cb_arg = result;
25812656
v2->cb_arg = element;
25822657

2583-
res = recursiveExecute(cxt, &elem, jb, &reslist);
2658+
res = recursiveExecuteNested(cxt, &elem, jb, &reslist);
25842659

25852660
if (jperIsError(res))
25862661
return res;
@@ -2847,14 +2922,18 @@ executeJsonPath(JsonPath *path, List *vars, Jsonb *json, JsonValueList *foundJso
28472922
JsonPathExecContext cxt;
28482923
JsonPathItem jsp;
28492924
JsonbValue jbv;
2925+
JsonItemStackEntry root;
28502926

28512927
jspInit(&jsp, path);
28522928

28532929
cxt.vars = vars;
28542930
cxt.lax = (path->header & JSONPATH_LAX) != 0;
28552931
cxt.root = JsonbInitBinary(&jbv, json);
2932+
cxt.stack = NULL;
28562933
cxt.innermostArraySize = -1;
28572934

2935+
pushJsonItem(&cxt.stack, &root, cxt.root);
2936+
28582937
if (!cxt.lax && !foundJson)
28592938
{
28602939
/*

0 commit comments

Comments
 (0)