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

Commit e6aea20

Browse files
author
Nikita Glukhov
committed
Add jsonpath object subscripting
1 parent d95b4a1 commit e6aea20

File tree

4 files changed

+245
-6
lines changed

4 files changed

+245
-6
lines changed

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ static inline JsonPathExecResult recursiveExecute(JsonPathExecContext *cxt,
118118
static inline JsonPathExecResult recursiveExecuteUnwrap(JsonPathExecContext *cxt,
119119
JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
120120

121+
static inline JsonbValue *wrapItem(JsonbValue *jbv);
122+
121123
static inline JsonbValue *wrapItemsInArray(const JsonValueList *items);
122124

123125
static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt,
@@ -1765,9 +1767,127 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
17651767

17661768
cxt->innermostArraySize = innermostArraySize;
17671769
}
1768-
else if (!jspIgnoreStructuralErrors(cxt))
1770+
else if (JsonbType(jb) == jbvObject)
17691771
{
1770-
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
1772+
int innermostArraySize = cxt->innermostArraySize;
1773+
int i;
1774+
JsonbValue bin;
1775+
JsonbValue *wrapped = NULL;
1776+
1777+
if (jb->type != jbvBinary)
1778+
jb = JsonbWrapInBinary(jb, &bin);
1779+
1780+
cxt->innermostArraySize = 1;
1781+
1782+
for (i = 0; i < jsp->content.array.nelems; i++)
1783+
{
1784+
JsonPathItem from;
1785+
JsonPathItem to;
1786+
JsonbValue *key;
1787+
JsonbValue tmp;
1788+
JsonValueList keys = { 0 };
1789+
bool range = jspGetArraySubscript(jsp, &from, &to, i);
1790+
1791+
if (range)
1792+
{
1793+
int index_from;
1794+
int index_to;
1795+
1796+
if (!jspAutoWrap(cxt))
1797+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1798+
1799+
if (!wrapped)
1800+
wrapped = wrapItem(jb);
1801+
1802+
res = getArrayIndex(cxt, &from, wrapped, &index_from);
1803+
if (jperIsError(res))
1804+
return res;
1805+
1806+
res = getArrayIndex(cxt, &to, wrapped, &index_to);
1807+
if (jperIsError(res))
1808+
return res;
1809+
1810+
res = jperNotFound;
1811+
1812+
if (index_from <= 0 && index_to >= 0)
1813+
{
1814+
res = recursiveExecuteNext(cxt, jsp, NULL, jb,
1815+
found, true);
1816+
if (jperIsError(res))
1817+
return res;
1818+
1819+
}
1820+
1821+
if (res == jperOk && !found)
1822+
break;
1823+
1824+
continue;
1825+
}
1826+
1827+
res = recursiveExecute(cxt, &from, jb, &keys);
1828+
1829+
if (jperIsError(res))
1830+
return res;
1831+
1832+
if (JsonValueListLength(&keys) != 1)
1833+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1834+
1835+
key = JsonValueListHead(&keys);
1836+
1837+
if (JsonbType(key) == jbvScalar)
1838+
key = JsonbExtractScalar(key->val.binary.data, &tmp);
1839+
1840+
res = jperNotFound;
1841+
1842+
if (key->type == jbvNumeric && jspAutoWrap(cxt))
1843+
{
1844+
int index = DatumGetInt32(
1845+
DirectFunctionCall1(numeric_int4,
1846+
DirectFunctionCall2(numeric_trunc,
1847+
NumericGetDatum(key->val.numeric),
1848+
Int32GetDatum(0))));
1849+
1850+
if (!index)
1851+
{
1852+
res = recursiveExecuteNext(cxt, jsp, NULL, jb,
1853+
found, true);
1854+
if (jperIsError(res))
1855+
return res;
1856+
}
1857+
else if (!jspIgnoreStructuralErrors(cxt))
1858+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1859+
}
1860+
else if (key->type == jbvString)
1861+
{
1862+
key = findJsonbValueFromContainer(jb->val.binary.data,
1863+
JB_FOBJECT, key);
1864+
1865+
if (key)
1866+
{
1867+
res = recursiveExecuteNext(cxt, jsp, NULL, key,
1868+
found, false);
1869+
if (jperIsError(res))
1870+
return res;
1871+
}
1872+
else if (!jspIgnoreStructuralErrors(cxt))
1873+
return jperMakeError(ERRCODE_JSON_MEMBER_NOT_FOUND);
1874+
}
1875+
else if (!jspIgnoreStructuralErrors(cxt))
1876+
return jperMakeError(ERRCODE_INVALID_JSON_SUBSCRIPT);
1877+
1878+
if (res == jperOk && !found)
1879+
break;
1880+
}
1881+
1882+
cxt->innermostArraySize = innermostArraySize;
1883+
}
1884+
else
1885+
{
1886+
if (jspAutoWrap(cxt))
1887+
res = recursiveExecuteNoUnwrap(cxt, jsp, wrapItem(jb),
1888+
found);
1889+
else if (!jspIgnoreStructuralErrors(cxt))
1890+
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
17711891
}
17721892
break;
17731893

@@ -2523,7 +2643,6 @@ recursiveExecute(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
25232643
return recursiveExecuteUnwrap(cxt, jsp, jb, found);
25242644

25252645
case jpiAnyArray:
2526-
case jpiIndexArray:
25272646
jb = wrapItem(jb);
25282647
break;
25292648

src/test/regress/expected/json_jsonpath.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,23 +123,23 @@ select json '[1]' @? 'strict $[1.2]';
123123
select json '[1]' @* 'strict $[1.2]';
124124
ERROR: Invalid SQL/JSON subscript
125125
select json '{}' @* 'strict $[0.3]';
126-
ERROR: SQL/JSON array not found
126+
ERROR: Invalid SQL/JSON subscript
127127
select json '{}' @? 'lax $[0.3]';
128128
?column?
129129
----------
130130
t
131131
(1 row)
132132

133133
select json '{}' @* 'strict $[1.2]';
134-
ERROR: SQL/JSON array not found
134+
ERROR: Invalid SQL/JSON subscript
135135
select json '{}' @? 'lax $[1.2]';
136136
?column?
137137
----------
138138
f
139139
(1 row)
140140

141141
select json '{}' @* 'strict $[-2 to 3]';
142-
ERROR: SQL/JSON array not found
142+
ERROR: Invalid SQL/JSON subscript
143143
select json '{}' @? 'lax $[-2 to 3]';
144144
?column?
145145
----------

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@ select jsonb '[1]' @? 'strict $[1.2]';
120120

121121
(1 row)
122122

123+
select jsonb '[1]' @* 'strict $[1.2]';
124+
ERROR: Invalid SQL/JSON subscript
125+
select jsonb '{}' @* 'strict $[0.3]';
126+
ERROR: Invalid SQL/JSON subscript
127+
select jsonb '{}' @? 'lax $[0.3]';
128+
?column?
129+
----------
130+
t
131+
(1 row)
132+
133+
select jsonb '{}' @* 'strict $[1.2]';
134+
ERROR: Invalid SQL/JSON subscript
135+
select jsonb '{}' @? 'lax $[1.2]';
136+
?column?
137+
----------
138+
f
139+
(1 row)
140+
141+
select jsonb '{}' @* 'strict $[-2 to 3]';
142+
ERROR: Invalid SQL/JSON subscript
143+
select jsonb '{}' @? 'lax $[-2 to 3]';
144+
?column?
145+
----------
146+
t
147+
(1 row)
148+
123149
select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$ ? (@.a[*] > @.b[*])';
124150
?column?
125151
----------
@@ -254,6 +280,12 @@ select jsonb '1' @* 'lax $[*]';
254280
1
255281
(1 row)
256282

283+
select jsonb '{}' @* 'lax $[0]';
284+
?column?
285+
----------
286+
{}
287+
(1 row)
288+
257289
select jsonb '[1]' @* 'lax $[0]';
258290
?column?
259291
----------
@@ -287,6 +319,12 @@ select jsonb '[1]' @* '$[last]';
287319
1
288320
(1 row)
289321

322+
select jsonb '{}' @* 'lax $[last]';
323+
?column?
324+
----------
325+
{}
326+
(1 row)
327+
290328
select jsonb '[1,2,3]' @* '$[last]';
291329
?column?
292330
----------
@@ -1871,3 +1909,61 @@ select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": {x: $, y: $[1] > 2, z: "foo"}}';
18711909
{"a": 5, "b": {"x": [1, 2, 3], "y": false, "z": "foo"}}
18721910
(1 row)
18731911

1912+
-- extension: object subscripting
1913+
select jsonb '{"a": 1}' @? '$["a"]';
1914+
?column?
1915+
----------
1916+
t
1917+
(1 row)
1918+
1919+
select jsonb '{"a": 1}' @? '$["b"]';
1920+
?column?
1921+
----------
1922+
f
1923+
(1 row)
1924+
1925+
select jsonb '{"a": 1}' @? 'strict $["b"]';
1926+
?column?
1927+
----------
1928+
1929+
(1 row)
1930+
1931+
select jsonb '{"a": 1}' @? '$["b", "a"]';
1932+
?column?
1933+
----------
1934+
t
1935+
(1 row)
1936+
1937+
select jsonb '{"a": 1}' @* '$["a"]';
1938+
?column?
1939+
----------
1940+
1
1941+
(1 row)
1942+
1943+
select jsonb '{"a": 1}' @* 'strict $["b"]';
1944+
ERROR: SQL/JSON member not found
1945+
select jsonb '{"a": 1}' @* 'lax $["b"]';
1946+
?column?
1947+
----------
1948+
(0 rows)
1949+
1950+
select jsonb '{"a": 1, "b": 2}' @* 'lax $["b", "c", "b", "a", 0 to 3]';
1951+
?column?
1952+
------------------
1953+
2
1954+
2
1955+
1
1956+
{"a": 1, "b": 2}
1957+
(4 rows)
1958+
1959+
select jsonb 'null' @* '{"a": 1}["a"]';
1960+
?column?
1961+
----------
1962+
1
1963+
(1 row)
1964+
1965+
select jsonb 'null' @* '{"a": 1}["b"]';
1966+
?column?
1967+
----------
1968+
(0 rows)
1969+

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ select jsonb '[1]' @? '$[0.5]';
1919
select jsonb '[1]' @? '$[0.9]';
2020
select jsonb '[1]' @? '$[1.2]';
2121
select jsonb '[1]' @? 'strict $[1.2]';
22+
select jsonb '[1]' @* 'strict $[1.2]';
23+
select jsonb '{}' @* 'strict $[0.3]';
24+
select jsonb '{}' @? 'lax $[0.3]';
25+
select jsonb '{}' @* 'strict $[1.2]';
26+
select jsonb '{}' @? 'lax $[1.2]';
27+
select jsonb '{}' @* 'strict $[-2 to 3]';
28+
select jsonb '{}' @? 'lax $[-2 to 3]';
29+
2230
select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$ ? (@.a[*] > @.b[*])';
2331
select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$ ? (@.a[*] >= @.b[*])';
2432
select jsonb '{"a": [1,2,3], "b": [3,4,"5"]}' @? '$ ? (@.a[*] >= @.b[*])';
@@ -42,12 +50,14 @@ select jsonb '[12, {"a": 13}, {"b": 14}]' @* 'lax $[0 to 10].a';
4250
select jsonb '[12, {"a": 13}, {"b": 14}, "ccc", true]' @* '$[2.5 - 1 to @.size() - 2]';
4351
select jsonb '1' @* 'lax $[0]';
4452
select jsonb '1' @* 'lax $[*]';
53+
select jsonb '{}' @* 'lax $[0]';
4554
select jsonb '[1]' @* 'lax $[0]';
4655
select jsonb '[1]' @* 'lax $[*]';
4756
select jsonb '[1,2,3]' @* 'lax $[*]';
4857
select jsonb '[]' @* '$[last]';
4958
select jsonb '[]' @* 'strict $[last]';
5059
select jsonb '[1]' @* '$[last]';
60+
select jsonb '{}' @* 'lax $[last]';
5161
select jsonb '[1,2,3]' @* '$[last]';
5262
select jsonb '[1,2,3]' @* '$[last - 1]';
5363
select jsonb '[1,2,3]' @* '$[last ? (@.type() == "number")]';
@@ -415,3 +425,17 @@ select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}.*';
415425
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}[*]';
416426
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": ($[*], 4, 5)}';
417427
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": {x: $, y: $[1] > 2, z: "foo"}}';
428+
429+
-- extension: object subscripting
430+
select jsonb '{"a": 1}' @? '$["a"]';
431+
select jsonb '{"a": 1}' @? '$["b"]';
432+
select jsonb '{"a": 1}' @? 'strict $["b"]';
433+
select jsonb '{"a": 1}' @? '$["b", "a"]';
434+
435+
select jsonb '{"a": 1}' @* '$["a"]';
436+
select jsonb '{"a": 1}' @* 'strict $["b"]';
437+
select jsonb '{"a": 1}' @* 'lax $["b"]';
438+
select jsonb '{"a": 1, "b": 2}' @* 'lax $["b", "c", "b", "a", 0 to 3]';
439+
440+
select jsonb 'null' @* '{"a": 1}["a"]';
441+
select jsonb 'null' @* '{"a": 1}["b"]';

0 commit comments

Comments
 (0)