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

Commit 37b1add

Browse files
author
Nikita Glukhov
committed
Add outer jsonpath item references
1 parent 4555cd0 commit 37b1add

File tree

9 files changed

+223
-26
lines changed

9 files changed

+223
-26
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 37 additions & 19 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,21 +96,21 @@ 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
}
109107
break;
110-
case jpiFilter:
111108
case jpiMap:
109+
argNestingLevel++;
110+
/* fall through */
111+
case jpiFilter:
112112
case jpiReduce:
113-
allowCurrentInArg = true;
113+
argNestingLevel++;
114114
/* fall through */
115115
case jpiIsUnknown:
116116
case jpiNot:
@@ -128,8 +128,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
128128
break;
129129

130130
chld = flattenJsonPathParseItem(buf, item->value.arg,
131-
allowCurrent ||
132-
allowCurrentInArg,
131+
nestingLevel + argNestingLevel,
133132
insideArraySubscript);
134133
*(int32*)(buf->data + arg) = chld - pos;
135134
}
@@ -141,8 +140,18 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
141140
case jpiAnyArray:
142141
case jpiAnyKey:
143142
break;
143+
case jpiCurrentN:
144+
if (item->value.current.level < 0 ||
145+
item->value.current.level >= nestingLevel)
146+
ereport(ERROR,
147+
(errcode(ERRCODE_SYNTAX_ERROR),
148+
errmsg("invalid outer item reference in jsonpath @")));
149+
150+
appendBinaryStringInfo(buf, (char *) &item->value.current.level,
151+
sizeof(item->value.current.level));
152+
break;
144153
case jpiCurrent:
145-
if (!allowCurrent)
154+
if (nestingLevel <= 0)
146155
ereport(ERROR,
147156
(errcode(ERRCODE_SYNTAX_ERROR),
148157
errmsg("@ is not allowed in root expressions")));
@@ -172,12 +181,12 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
172181
int32 frompos =
173182
flattenJsonPathParseItem(buf,
174183
item->value.array.elems[i].from,
175-
true, true) - pos;
184+
nestingLevel + 1, true) - pos;
176185

177186
if (item->value.array.elems[i].to)
178187
topos = flattenJsonPathParseItem(buf,
179188
item->value.array.elems[i].to,
180-
true, true) - pos;
189+
nestingLevel + 1, true) - pos;
181190
else
182191
topos = 0;
183192

@@ -222,7 +231,8 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
222231
{
223232
int32 elempos =
224233
flattenJsonPathParseItem(buf, lfirst(lc),
225-
allowCurrent, insideArraySubscript);
234+
nestingLevel,
235+
insideArraySubscript);
226236

227237
*(int32 *) &buf->data[offset] = elempos - pos;
228238
offset += sizeof(int32);
@@ -246,11 +256,11 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
246256
JsonPathParseItem *field = lfirst(lc);
247257
int32 keypos =
248258
flattenJsonPathParseItem(buf, field->value.args.left,
249-
allowCurrent,
259+
nestingLevel,
250260
insideArraySubscript);
251261
int32 valpos =
252262
flattenJsonPathParseItem(buf, field->value.args.right,
253-
allowCurrent,
263+
nestingLevel,
254264
insideArraySubscript);
255265
int32 *ppos = (int32 *) &buf->data[offset];
256266

@@ -267,7 +277,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
267277

268278
if (item->next)
269279
*(int32*)(buf->data + next) =
270-
flattenJsonPathParseItem(buf, item->next, allowCurrent,
280+
flattenJsonPathParseItem(buf, item->next, nestingLevel,
271281
insideArraySubscript) - pos;
272282

273283
return pos;
@@ -289,7 +299,7 @@ jsonpath_in(PG_FUNCTION_ARGS)
289299

290300
if (jsonpath != NULL)
291301
{
292-
flattenJsonPathParseItem(&buf, jsonpath->expr, false, false);
302+
flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
293303

294304
res = (JsonPath*)buf.data;
295305
SET_VARSIZE(res, buf.len);
@@ -479,6 +489,10 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
479489
Assert(!inKey);
480490
appendStringInfoChar(buf, '@');
481491
break;
492+
case jpiCurrentN:
493+
Assert(!inKey);
494+
appendStringInfo(buf, "@%d", v->content.current.level);
495+
break;
482496
case jpiRoot:
483497
Assert(!inKey);
484498
appendStringInfoChar(buf, '$');
@@ -732,6 +746,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
732746
case jpiMin:
733747
case jpiMax:
734748
break;
749+
case jpiCurrentN:
750+
read_int32(v->content.current.level, base, pos);
751+
break;
735752
case jpiKey:
736753
case jpiString:
737754
case jpiVariable:
@@ -833,6 +850,7 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
833850
v->type == jpiIndexArray ||
834851
v->type == jpiFilter ||
835852
v->type == jpiCurrent ||
853+
v->type == jpiCurrentN ||
836854
v->type == jpiExists ||
837855
v->type == jpiRoot ||
838856
v->type == jpiVariable ||

src/backend/utils/adt/jsonpath_exec.c

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

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

@@ -105,6 +114,10 @@ static inline JsonPathExecResult recursiveExecute(JsonPathExecContext *cxt,
105114
static inline JsonPathExecResult recursiveExecuteBool(JsonPathExecContext *cxt,
106115
JsonPathItem *jsp, JsonbValue *jb);
107116

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

@@ -244,6 +257,20 @@ JsonbWrapInBinary(JsonbValue *jbv, JsonbValue *out)
244257
return JsonbInitBinary(out, jb);
245258
}
246259

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

249276
/*
@@ -1053,7 +1080,7 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
10531080
JsonbValue *jbv;
10541081
JsonValueList found = { 0 };
10551082
JsonbValue tmp;
1056-
JsonPathExecResult res = recursiveExecute(cxt, jsp, jb, &found);
1083+
JsonPathExecResult res = recursiveExecuteNested(cxt, jsp, jb, &found);
10571084

10581085
if (jperIsError(res))
10591086
return res;
@@ -1201,6 +1228,25 @@ appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
12011228
return recursiveExecuteNext(cxt, jsp, &next, &jbv, found, true);
12021229
}
12031230

1231+
static inline JsonPathExecResult
1232+
recursiveExecuteNested(JsonPathExecContext *cxt, JsonPathItem *jsp,
1233+
JsonbValue *jb, JsonValueList *found)
1234+
{
1235+
JsonItemStackEntry current;
1236+
JsonPathExecResult res;
1237+
1238+
pushJsonItem(&cxt->stack, &current, jb);
1239+
1240+
/* found == NULL is used here when executing boolean filter expressions */
1241+
res = found
1242+
? recursiveExecute(cxt, jsp, jb, found)
1243+
: recursiveExecuteBool(cxt, jsp, jb);
1244+
1245+
popJsonItem(&cxt->stack);
1246+
1247+
return res;
1248+
}
1249+
12041250
/*
12051251
* Main executor function: walks on jsonpath structure and tries to find
12061252
* correspoding parts of jsonb. Note, jsonb and jsonpath values should be
@@ -1307,11 +1353,29 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
13071353
jb = cxt->root;
13081354
/* fall through */
13091355
case jpiCurrent:
1356+
case jpiCurrentN:
13101357
{
13111358
JsonbValue *v;
13121359
JsonbValue vbuf;
13131360
bool copy = true;
13141361

1362+
if (jsp->type == jpiCurrentN)
1363+
{
1364+
int i;
1365+
JsonItemStackEntry *current = cxt->stack;
1366+
1367+
for (i = 0; i < jsp->content.current.level; i++)
1368+
{
1369+
current = current->parent;
1370+
1371+
if (!current)
1372+
elog(ERROR,
1373+
"invalid jsonpath current item reference");
1374+
}
1375+
1376+
jb = current->item;
1377+
}
1378+
13151379
if (JsonbType(jb) == jbvScalar)
13161380
{
13171381
if (jspHasNext(jsp))
@@ -1517,7 +1581,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
15171581
continue;
15181582
}
15191583

1520-
res = recursiveExecute(cxt, &from, jb, &keys);
1584+
res = recursiveExecuteNested(cxt, &from, jb, &keys);
15211585

15221586
if (jperIsError(res))
15231587
return res;
@@ -1665,7 +1729,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
16651729
break;
16661730
case jpiFilter:
16671731
jspGetArg(jsp, &elem);
1668-
res = recursiveExecuteBool(cxt, &elem, jb);
1732+
res = recursiveExecuteNested(cxt, &elem, jb, NULL);
16691733
if (res != jperOk)
16701734
res = jperNotFound;
16711735
else
@@ -2075,9 +2139,14 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20752139
if (cxt->lax)
20762140
{
20772141
JsonValueList reslist = { 0 };
2142+
JsonItemStackEntry entry;
20782143

20792144
jspGetArg(jsp, &elem);
2080-
res = recursiveExecute(cxt, &elem, jb, &reslist);
2145+
2146+
/* push additional stack entry for the whole item */
2147+
pushJsonItem(&cxt->stack, &entry, jb);
2148+
res = recursiveExecuteNested(cxt, &elem, jb, &reslist);
2149+
popJsonItem(&cxt->stack);
20812150

20822151
if (jperIsError(res))
20832152
return res;
@@ -2099,6 +2168,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20992168
JsonbIterator *it = NULL;
21002169
JsonbIteratorToken tok;
21012170
JsonValueList result = { 0 };
2171+
JsonItemStackEntry entry;
21022172
int size = JsonbArraySize(jb);
21032173
int i;
21042174

@@ -2113,6 +2183,9 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
21132183
elog(ERROR, "unexpected jsonb token at the array start");
21142184
}
21152185

2186+
/* push additional stack entry for the whole array */
2187+
pushJsonItem(&cxt->stack, &entry, jb);
2188+
21162189
for (i = 0; i < size; i++)
21172190
{
21182191
JsonValueList reslist = { 0 };
@@ -2126,7 +2199,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
21262199
else
21272200
element = &jb->val.array.elems[i];
21282201

2129-
res = recursiveExecute(cxt, &elem, element, &reslist);
2202+
res = recursiveExecuteNested(cxt, &elem, element, &reslist);
21302203

21312204
if (jperIsError(res))
21322205
break;
@@ -2140,6 +2213,8 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
21402213
JsonValueListConcat(&result, reslist);
21412214
}
21422215

2216+
popJsonItem(&cxt->stack);
2217+
21432218
if (jperIsError(res))
21442219
break;
21452220

@@ -2407,7 +2482,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
24072482
v1->cb_arg = result;
24082483
v2->cb_arg = element;
24092484

2410-
res = recursiveExecute(cxt, &elem, jb, &reslist);
2485+
res = recursiveExecuteNested(cxt, &elem, jb, &reslist);
24112486

24122487
if (jperIsError(res))
24132488
return res;
@@ -2656,14 +2731,18 @@ executeJsonPath(JsonPath *path, List *vars, Jsonb *json, JsonValueList *foundJso
26562731
JsonPathExecContext cxt;
26572732
JsonPathItem jsp;
26582733
JsonbValue jbv;
2734+
JsonItemStackEntry root;
26592735

26602736
jspInit(&jsp, path);
26612737

26622738
cxt.vars = vars;
26632739
cxt.lax = (path->header & JSONPATH_LAX) != 0;
26642740
cxt.root = JsonbInitBinary(&jbv, json);
2741+
cxt.stack = NULL;
26652742
cxt.innermostArraySize = -1;
26662743

2744+
pushJsonItem(&cxt.stack, &root, cxt.root);
2745+
26672746
return recursiveExecute(&cxt, &jsp, &jbv, foundJson);
26682747
}
26692748

0 commit comments

Comments
 (0)