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

Commit c814a30

Browse files
author
Nikita Glukhov
committed
Add jsonpath object subscripting
1 parent cea7194 commit c814a30

File tree

3 files changed

+218
-2
lines changed

3 files changed

+218
-2
lines changed

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,8 +1399,126 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
13991399

14001400
cxt->innermostArraySize = innermostArraySize;
14011401
}
1402+
else if (JsonbType(jb) == jbvObject)
1403+
{
1404+
int innermostArraySize = cxt->innermostArraySize;
1405+
int i;
1406+
JsonbValue bin;
1407+
JsonbValue *wrapped = NULL;
1408+
1409+
if (jb->type == jbvBinary)
1410+
jb = JsonbWrapInBinary(jb, &bin);
1411+
1412+
cxt->innermostArraySize = 1;
1413+
1414+
for (i = 0; i < jsp->content.array.nelems; i++)
1415+
{
1416+
JsonPathItem from;
1417+
JsonPathItem to;
1418+
JsonbValue *key;
1419+
JsonbValue tmp;
1420+
JsonValueList keys = { 0 };
1421+
bool range = jspGetArraySubscript(jsp, &from, &to, i);
1422+
1423+
if (range)
1424+
{
1425+
int index_from;
1426+
int index_to;
1427+
1428+
if (!cxt->lax)
1429+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1430+
1431+
if (!wrapped)
1432+
wrapped = wrapItem(jb);
1433+
1434+
res = getArrayIndex(cxt, &from, wrapped, &index_from);
1435+
if (jperIsError(res))
1436+
return res;
1437+
1438+
res = getArrayIndex(cxt, &to, wrapped, &index_to);
1439+
if (jperIsError(res))
1440+
return res;
1441+
1442+
res = jperNotFound;
1443+
1444+
if (index_from <= 0 && index_to >= 0)
1445+
{
1446+
res = recursiveExecuteNext(cxt, jsp, NULL, jb,
1447+
found, true);
1448+
if (jperIsError(res))
1449+
return res;
1450+
1451+
}
1452+
1453+
if (res == jperOk && !found)
1454+
break;
1455+
1456+
continue;
1457+
}
1458+
1459+
res = recursiveExecute(cxt, &from, jb, &keys);
1460+
1461+
if (jperIsError(res))
1462+
return res;
1463+
1464+
if (JsonValueListLength(&keys) != 1)
1465+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1466+
1467+
key = JsonValueListHead(&keys);
1468+
1469+
if (JsonbType(key) == jbvScalar)
1470+
key = JsonbExtractScalar(key->val.binary.data, &tmp);
1471+
1472+
res = jperNotFound;
1473+
1474+
if (key->type == jbvNumeric && cxt->lax)
1475+
{
1476+
int index = DatumGetInt32(
1477+
DirectFunctionCall1(numeric_int4,
1478+
DirectFunctionCall2(numeric_trunc,
1479+
NumericGetDatum(key->val.numeric),
1480+
Int32GetDatum(0))));
1481+
1482+
if (!index)
1483+
{
1484+
res = recursiveExecuteNext(cxt, jsp, NULL, jb,
1485+
found, true);
1486+
if (jperIsError(res))
1487+
return res;
1488+
}
1489+
}
1490+
else if (key->type == jbvString)
1491+
{
1492+
key = findJsonbValueFromContainer(jb->val.binary.data,
1493+
JB_FOBJECT, key);
1494+
1495+
if (key)
1496+
{
1497+
res = recursiveExecuteNext(cxt, jsp, NULL, key,
1498+
found, false);
1499+
if (jperIsError(res))
1500+
return res;
1501+
}
1502+
else if (!cxt->lax)
1503+
return jperMakeError(ERRCODE_JSON_MEMBER_NOT_FOUND);
1504+
}
1505+
else
1506+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1507+
1508+
if (res == jperOk && !found)
1509+
break;
1510+
}
1511+
1512+
cxt->innermostArraySize = innermostArraySize;
1513+
}
14021514
else
1403-
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
1515+
{
1516+
if (cxt->lax)
1517+
res = recursiveExecuteNoUnwrap(cxt, jsp, wrapItem(jb),
1518+
found, false);
1519+
else
1520+
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
1521+
}
14041522
break;
14051523

14061524
case jpiLast:
@@ -2426,7 +2544,6 @@ recursiveExecute(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
24262544
return recursiveExecuteUnwrap(cxt, jsp, jb, found);
24272545

24282546
case jpiAnyArray:
2429-
case jpiIndexArray:
24302547
jb = wrapItem(jb);
24312548
break;
24322549

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,30 @@ select _jsonpath_exists(jsonb '[1]', '$.[1.2]');
110110

111111
select _jsonpath_exists(jsonb '[1]', 'strict $.[1.2]');
112112
ERROR: Invalid SQL/JSON subscript
113+
select _jsonpath_exists(jsonb '{}', 'strict $.[0.3]');
114+
ERROR: Invalid SQL/JSON subscript
115+
select _jsonpath_exists(jsonb '{}', 'lax $.[0.3]');
116+
_jsonpath_exists
117+
------------------
118+
t
119+
(1 row)
120+
121+
select _jsonpath_exists(jsonb '{}', 'strict $.[1.2]');
122+
ERROR: Invalid SQL/JSON subscript
123+
select _jsonpath_exists(jsonb '{}', 'lax $.[1.2]');
124+
_jsonpath_exists
125+
------------------
126+
f
127+
(1 row)
128+
129+
select _jsonpath_exists(jsonb '{}', 'strict $.[-2 to 3]');
130+
ERROR: Invalid SQL/JSON subscript
131+
select _jsonpath_exists(jsonb '{}', 'lax $.[-2 to 3]');
132+
_jsonpath_exists
133+
------------------
134+
t
135+
(1 row)
136+
113137
select _jsonpath_exists(jsonb '{"a": [1,2,3], "b": [3,4,5]}', '$ ? (@.a[*] > @.b[*])');
114138
_jsonpath_exists
115139
------------------
@@ -244,6 +268,12 @@ select * from _jsonpath_query(jsonb '1', 'lax $[*]');
244268
1
245269
(1 row)
246270

271+
select * from _jsonpath_query(jsonb '{}', 'lax $[0]');
272+
_jsonpath_query
273+
-----------------
274+
{}
275+
(1 row)
276+
247277
select * from _jsonpath_query(jsonb '[1]', 'lax $[0]');
248278
_jsonpath_query
249279
-----------------
@@ -277,6 +307,12 @@ select * from _jsonpath_query(jsonb '[1]', '$[last]');
277307
1
278308
(1 row)
279309

310+
select * from _jsonpath_query(jsonb '{}', 'lax $[last]');
311+
_jsonpath_query
312+
-----------------
313+
{}
314+
(1 row)
315+
280316
select * from _jsonpath_query(jsonb '[1,2,3]', '$[last]');
281317
_jsonpath_query
282318
-----------------
@@ -1857,3 +1893,46 @@ select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$.map({x: @, y: @ <
18571893
{"a": 5, "b": [{"x": 1, "y": true}, {"x": 2, "y": true}, {"x": 3, "y": false}, {"z": "foo"}]}
18581894
(1 row)
18591895

1896+
-- extension: object subscripting
1897+
select _jsonpath_exists(jsonb '{"a": 1}', '$["a"]');
1898+
_jsonpath_exists
1899+
------------------
1900+
t
1901+
(1 row)
1902+
1903+
select _jsonpath_exists(jsonb '{"a": 1}', '$["b"]');
1904+
_jsonpath_exists
1905+
------------------
1906+
f
1907+
(1 row)
1908+
1909+
select _jsonpath_exists(jsonb '{"a": 1}', 'strict $["b"]');
1910+
ERROR: SQL/JSON member not found
1911+
select _jsonpath_exists(jsonb '{"a": 1}', '$["b", "a"]');
1912+
_jsonpath_exists
1913+
------------------
1914+
t
1915+
(1 row)
1916+
1917+
select * from _jsonpath_query(jsonb '{"a": 1}', '$["a"]');
1918+
_jsonpath_query
1919+
-----------------
1920+
1
1921+
(1 row)
1922+
1923+
select * from _jsonpath_query(jsonb '{"a": 1}', 'strict $["b"]');
1924+
ERROR: SQL/JSON member not found
1925+
select * from _jsonpath_query(jsonb '{"a": 1}', 'lax $["b"]');
1926+
_jsonpath_query
1927+
-----------------
1928+
(0 rows)
1929+
1930+
select * from _jsonpath_query(jsonb '{"a": 1, "b": 2}', 'lax $["b", "c", "b", "a", 0 to 3]');
1931+
_jsonpath_query
1932+
------------------
1933+
2
1934+
2
1935+
1
1936+
{"a": 1, "b": 2}
1937+
(4 rows)
1938+

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ select _jsonpath_exists(jsonb '[1]', '$.[0.5]');
1818
select _jsonpath_exists(jsonb '[1]', '$.[0.9]');
1919
select _jsonpath_exists(jsonb '[1]', '$.[1.2]');
2020
select _jsonpath_exists(jsonb '[1]', 'strict $.[1.2]');
21+
select _jsonpath_exists(jsonb '{}', 'strict $.[0.3]');
22+
select _jsonpath_exists(jsonb '{}', 'lax $.[0.3]');
23+
select _jsonpath_exists(jsonb '{}', 'strict $.[1.2]');
24+
select _jsonpath_exists(jsonb '{}', 'lax $.[1.2]');
25+
select _jsonpath_exists(jsonb '{}', 'strict $.[-2 to 3]');
26+
select _jsonpath_exists(jsonb '{}', 'lax $.[-2 to 3]');
27+
2128
select _jsonpath_exists(jsonb '{"a": [1,2,3], "b": [3,4,5]}', '$ ? (@.a[*] > @.b[*])');
2229
select _jsonpath_exists(jsonb '{"a": [1,2,3], "b": [3,4,5]}', '$ ? (@.a[*] >= @.b[*])');
2330
select _jsonpath_exists(jsonb '{"a": [1,2,3], "b": [3,4,"5"]}', '$ ? (@.a[*] >= @.b[*])');
@@ -41,12 +48,14 @@ select * from _jsonpath_query(jsonb '[12, {"a": 13}, {"b": 14}]', 'lax $.[0 to 1
4148
select * from _jsonpath_query(jsonb '[12, {"a": 13}, {"b": 14}, "ccc", true]', '$.[2.5 - 1 to @.size() - 2]');
4249
select * from _jsonpath_query(jsonb '1', 'lax $[0]');
4350
select * from _jsonpath_query(jsonb '1', 'lax $[*]');
51+
select * from _jsonpath_query(jsonb '{}', 'lax $[0]');
4452
select * from _jsonpath_query(jsonb '[1]', 'lax $[0]');
4553
select * from _jsonpath_query(jsonb '[1]', 'lax $[*]');
4654
select * from _jsonpath_query(jsonb '[1,2,3]', 'lax $[*]');
4755
select * from _jsonpath_query(jsonb '[]', '$[last]');
4856
select * from _jsonpath_query(jsonb '[]', 'strict $[last]');
4957
select * from _jsonpath_query(jsonb '[1]', '$[last]');
58+
select * from _jsonpath_query(jsonb '{}', 'lax $[last]');
5059
select * from _jsonpath_query(jsonb '[1,2,3]', '$[last]');
5160
select * from _jsonpath_query(jsonb '[1,2,3]', '$[last - 1]');
5261
select * from _jsonpath_query(jsonb '[1,2,3]', '$[last ? (@.type() == "number")]');
@@ -429,3 +438,14 @@ select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$[*], 4, 5]}');
429438
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$[*], 4, 5]}.*');
430439
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": ($[*], 4, 5)}');
431440
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$.map({x: @, y: @ < 3})[*], {z: "foo"}]}');
441+
442+
-- extension: object subscripting
443+
select _jsonpath_exists(jsonb '{"a": 1}', '$["a"]');
444+
select _jsonpath_exists(jsonb '{"a": 1}', '$["b"]');
445+
select _jsonpath_exists(jsonb '{"a": 1}', 'strict $["b"]');
446+
select _jsonpath_exists(jsonb '{"a": 1}', '$["b", "a"]');
447+
448+
select * from _jsonpath_query(jsonb '{"a": 1}', '$["a"]');
449+
select * from _jsonpath_query(jsonb '{"a": 1}', 'strict $["b"]');
450+
select * from _jsonpath_query(jsonb '{"a": 1}', 'lax $["b"]');
451+
select * from _jsonpath_query(jsonb '{"a": 1, "b": 2}', 'lax $["b", "c", "b", "a", 0 to 3]');

0 commit comments

Comments
 (0)