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

Commit 670aa77

Browse files
author
Nikita Glukhov
committed
Fix current item references in jsonpath array subscripts
1 parent e4905c9 commit 670aa77

File tree

6 files changed

+84
-23
lines changed

6 files changed

+84
-23
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
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;
3333
int32 next;
34+
int argNestingLevel = 0;
3435

3536
check_stack_depth();
3637
CHECK_FOR_INTERRUPTS();
@@ -94,12 +95,12 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
9495

9596
chld = !item->value.args.left ? pos :
9697
flattenJsonPathParseItem(buf, item->value.args.left,
97-
allowCurrent,
98+
nestingLevel + argNestingLevel,
9899
insideArraySubscript);
99100
*(int32*)(buf->data + left) = chld - pos;
100101
chld = !item->value.args.right ? pos :
101102
flattenJsonPathParseItem(buf, item->value.args.right,
102-
allowCurrent,
103+
nestingLevel + argNestingLevel,
103104
insideArraySubscript);
104105
*(int32*)(buf->data + right) = chld - pos;
105106
}
@@ -122,12 +123,14 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
122123
appendStringInfoChar(buf, '\0');
123124

124125
chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
125-
allowCurrent,
126+
nestingLevel,
126127
insideArraySubscript);
127128
*(int32 *)(buf->data + offs) = chld - pos;
128129
}
129130
break;
130131
case jpiFilter:
132+
argNestingLevel++;
133+
/* fall through */
131134
case jpiIsUnknown:
132135
case jpiNot:
133136
case jpiPlus:
@@ -140,8 +143,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
140143
appendBinaryStringInfo(buf, (char*)&arg /* fake value */, sizeof(arg));
141144

142145
chld = flattenJsonPathParseItem(buf, item->value.arg,
143-
item->type == jpiFilter ||
144-
allowCurrent,
146+
nestingLevel + argNestingLevel,
145147
insideArraySubscript);
146148
*(int32*)(buf->data + arg) = chld - pos;
147149
}
@@ -154,7 +156,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
154156
case jpiAnyKey:
155157
break;
156158
case jpiCurrent:
157-
if (!allowCurrent)
159+
if (nestingLevel <= 0)
158160
ereport(ERROR,
159161
(errcode(ERRCODE_SYNTAX_ERROR),
160162
errmsg("@ is not allowed in root expressions")));
@@ -184,12 +186,12 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
184186
int32 frompos =
185187
flattenJsonPathParseItem(buf,
186188
item->value.array.elems[i].from,
187-
true, true) - pos;
189+
nestingLevel, true) - pos;
188190

189191
if (item->value.array.elems[i].to)
190192
topos = flattenJsonPathParseItem(buf,
191193
item->value.array.elems[i].to,
192-
true, true) - pos;
194+
nestingLevel, true) - pos;
193195
else
194196
topos = 0;
195197

@@ -222,7 +224,7 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
222224

223225
if (item->next)
224226
{
225-
chld = flattenJsonPathParseItem(buf, item->next, allowCurrent,
227+
chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
226228
insideArraySubscript) - pos;
227229
*(int32 *)(buf->data + next) = chld;
228230
}
@@ -249,7 +251,7 @@ jsonpath_in(PG_FUNCTION_ARGS)
249251
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
250252
errmsg("invalid input syntax for jsonpath: \"%s\"", in)));
251253

252-
flattenJsonPathParseItem(&buf, jsonpath->expr, false, false);
254+
flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
253255

254256
res = (JsonPath*)buf.data;
255257
SET_VARSIZE(res, buf.len);

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ typedef struct JsonBaseObjectInfo
3333
int id;
3434
} JsonBaseObjectInfo;
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
JsonbValue *root; /* for $ evaluation */
48+
JsonItemStack stack; /* for @N evaluation */
4049
JsonBaseObjectInfo baseObject; /* for .keyvalue().id evaluation */
4150
int generatedObjectId;
4251
int innermostArraySize; /* for LAST array index evaluation */
@@ -61,6 +70,10 @@ static inline JsonPathExecResult recursiveExecute(JsonPathExecContext *cxt,
6170
JsonPathItem *jsp, JsonbValue *jb,
6271
JsonValueList *found);
6372

73+
static inline JsonPathExecResult recursiveExecuteNested(JsonPathExecContext *cxt,
74+
JsonPathItem *jsp, JsonbValue *jb,
75+
JsonValueList *found);
76+
6477
static inline JsonPathExecResult recursiveExecuteUnwrap(JsonPathExecContext *cxt,
6578
JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
6679

@@ -157,6 +170,20 @@ JsonbWrapInBinary(JsonbValue *jbv, JsonbValue *out)
157170
return JsonbInitBinary(out, jb);
158171
}
159172

173+
static inline void
174+
pushJsonItem(JsonItemStack *stack, JsonItemStackEntry *entry, JsonbValue *item)
175+
{
176+
entry->item = item;
177+
entry->parent = *stack;
178+
*stack = entry;
179+
}
180+
181+
static inline void
182+
popJsonItem(JsonItemStack *stack)
183+
{
184+
*stack = (*stack)->parent;
185+
}
186+
160187
/********************Execute functions for JsonPath***************************/
161188

162189
/*
@@ -1409,6 +1436,34 @@ recursiveExecuteBool(JsonPathExecContext *cxt, JsonPathItem *jsp,
14091436
}
14101437
}
14111438

1439+
static inline JsonPathExecResult
1440+
recursiveExecuteNested(JsonPathExecContext *cxt, JsonPathItem *jsp,
1441+
JsonbValue *jb, JsonValueList *found)
1442+
{
1443+
JsonItemStackEntry current;
1444+
JsonPathExecResult res;
1445+
1446+
pushJsonItem(&cxt->stack, &current, jb);
1447+
res = recursiveExecute(cxt, jsp, jb, found);
1448+
popJsonItem(&cxt->stack);
1449+
1450+
return res;
1451+
}
1452+
1453+
static inline JsonPathBool
1454+
recursiveExecuteBoolNested(JsonPathExecContext *cxt, JsonPathItem *jsp,
1455+
JsonbValue *jb)
1456+
{
1457+
JsonItemStackEntry current;
1458+
JsonPathBool res;
1459+
1460+
pushJsonItem(&cxt->stack, &current, jb);
1461+
res = recursiveExecuteBool(cxt, jsp, jb, false);
1462+
popJsonItem(&cxt->stack);
1463+
1464+
return res;
1465+
}
1466+
14121467
static inline JsonPathExecResult
14131468
recursiveExecuteBase(JsonPathExecContext *cxt, JsonPathItem *jsp,
14141469
JsonbValue *jbv, JsonValueList *found)
@@ -1534,7 +1589,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
15341589
break;
15351590

15361591
case jpiCurrent:
1537-
res = recursiveExecuteBase(cxt, jsp, jb, found);
1592+
res = recursiveExecuteBase(cxt, jsp, cxt->stack->item, found);
15381593
break;
15391594

15401595
case jpiAnyArray:
@@ -1775,7 +1830,7 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
17751830
JsonPathBool st;
17761831

17771832
jspGetArg(jsp, &elem);
1778-
st = recursiveExecuteBool(cxt, &elem, jb, false);
1833+
st = recursiveExecuteBoolNested(cxt, &elem, jb);
17791834
if (st != jpbTrue)
17801835
res = jperNotFound;
17811836
else
@@ -2325,18 +2380,22 @@ executeJsonPath(JsonPath *path, List *vars, Jsonb *json, JsonValueList *foundJso
23252380
JsonPathExecContext cxt;
23262381
JsonPathItem jsp;
23272382
JsonbValue jbv;
2383+
JsonItemStackEntry root;
23282384

23292385
jspInit(&jsp, path);
23302386

23312387
cxt.vars = vars;
23322388
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
23332389
cxt.ignoreStructuralErrors = cxt.laxMode;
23342390
cxt.root = JsonbInitBinary(&jbv, json);
2391+
cxt.stack = NULL;
23352392
cxt.baseObject.jbc = NULL;
23362393
cxt.baseObject.id = 0;
23372394
cxt.generatedObjectId = list_length(vars) + 1;
23382395
cxt.innermostArraySize = -1;
23392396

2397+
pushJsonItem(&cxt.stack, &root, cxt.root);
2398+
23402399
if (jspStrictAbsenseOfErrors(&cxt) && !foundJson)
23412400
{
23422401
/*

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ select jsonb '[12, {"a": 13}, {"b": 14}]' @* 'lax $[0 to 10].a';
234234
13
235235
(1 row)
236236

237-
select jsonb '[12, {"a": 13}, {"b": 14}, "ccc", true]' @* '$[2.5 - 1 to @.size() - 2]';
237+
select jsonb '[12, {"a": 13}, {"b": 14}, "ccc", true]' @* '$[2.5 - 1 to $.size() - 2]';
238238
?column?
239239
-----------
240240
{"a": 13}

src/test/regress/expected/jsonpath.out

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,10 @@ select '$.a[1,2, 3 to 16]'::jsonpath;
303303
$."a"[1,2,3 to 16]
304304
(1 row)
305305

306-
select '$.a[$a + 1, ($b[*]) to -(@[0] * 2)]'::jsonpath;
306+
select '$.a[$a + 1, ($b[*]) to -($[0] * 2)]'::jsonpath;
307307
jsonpath
308308
----------------------------------------
309-
$."a"[$"a" + 1,$"b"[*] to -(@[0] * 2)]
309+
$."a"[$"a" + 1,$"b"[*] to -($[0] * 2)]
310310
(1 row)
311311

312312
select '$.a[$.a.size() - 3]'::jsonpath;
@@ -341,10 +341,10 @@ select '$[last]'::jsonpath;
341341
$[last]
342342
(1 row)
343343

344-
select '$[@ ? (last > 0)]'::jsonpath;
345-
jsonpath
346-
-----------------
347-
$[@?(last > 0)]
344+
select '$[$[0] ? (last > 0)]'::jsonpath;
345+
jsonpath
346+
--------------------
347+
$[$[0]?(last > 0)]
348348
(1 row)
349349

350350
select 'null.type()'::jsonpath;

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ select jsonb '[12, {"a": 13}, {"b": 14}]' @* 'lax $[1].a';
3939
select jsonb '[12, {"a": 13}, {"b": 14}]' @* 'lax $[2].a';
4040
select jsonb '[12, {"a": 13}, {"b": 14}]' @* 'lax $[0,1].a';
4141
select jsonb '[12, {"a": 13}, {"b": 14}]' @* 'lax $[0 to 10].a';
42-
select jsonb '[12, {"a": 13}, {"b": 14}, "ccc", true]' @* '$[2.5 - 1 to @.size() - 2]';
42+
select jsonb '[12, {"a": 13}, {"b": 14}, "ccc", true]' @* '$[2.5 - 1 to $.size() - 2]';
4343
select jsonb '1' @* 'lax $[0]';
4444
select jsonb '1' @* 'lax $[*]';
4545
select jsonb '[1]' @* 'lax $[0]';

src/test/regress/sql/jsonpath.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@ select '$a.b'::jsonpath;
5454
select '$a[*]'::jsonpath;
5555
select '$.g ? (@.zip == $zip)'::jsonpath;
5656
select '$.a[1,2, 3 to 16]'::jsonpath;
57-
select '$.a[$a + 1, ($b[*]) to -(@[0] * 2)]'::jsonpath;
57+
select '$.a[$a + 1, ($b[*]) to -($[0] * 2)]'::jsonpath;
5858
select '$.a[$.a.size() - 3]'::jsonpath;
5959
select 'last'::jsonpath;
6060
select '"last"'::jsonpath;
6161
select '$.last'::jsonpath;
6262
select '$ ? (last > 0)'::jsonpath;
6363
select '$[last]'::jsonpath;
64-
select '$[@ ? (last > 0)]'::jsonpath;
64+
select '$[$[0] ? (last > 0)]'::jsonpath;
6565
6666
select 'null.type()'::jsonpath;
6767
select '1.type()'::jsonpath;

0 commit comments

Comments
 (0)