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

Commit 0c6b642

Browse files
author
Nikita Glukhov
committed
Add jsonpath object constructors
1 parent ba458a7 commit 0c6b642

File tree

8 files changed

+228
-3
lines changed

8 files changed

+228
-3
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,38 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
217217
}
218218
}
219219
break;
220+
case jpiObject:
221+
{
222+
int32 nfields = list_length(item->value.object.fields);
223+
ListCell *lc;
224+
int offset;
225+
226+
appendBinaryStringInfo(buf, (char *) &nfields, sizeof(nfields));
227+
228+
offset = buf->len;
229+
230+
appendStringInfoSpaces(buf, sizeof(int32) * 2 * nfields);
231+
232+
foreach(lc, item->value.object.fields)
233+
{
234+
JsonPathParseItem *field = lfirst(lc);
235+
int32 keypos =
236+
flattenJsonPathParseItem(buf, field->value.args.left,
237+
allowCurrent,
238+
insideArraySubscript);
239+
int32 valpos =
240+
flattenJsonPathParseItem(buf, field->value.args.right,
241+
allowCurrent,
242+
insideArraySubscript);
243+
int32 *ppos = (int32 *) &buf->data[offset];
244+
245+
ppos[0] = keypos;
246+
ppos[1] = valpos;
247+
248+
offset += 2 * sizeof(int32);
249+
}
250+
}
251+
break;
220252
default:
221253
elog(ERROR, "Unknown jsonpath item type: %d", item->type);
222254
}
@@ -552,6 +584,26 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
552584
}
553585
appendStringInfoChar(buf, ']');
554586
break;
587+
case jpiObject:
588+
appendStringInfoChar(buf, '{');
589+
590+
for (i = 0; i < v->content.object.nfields; i++)
591+
{
592+
JsonPathItem key;
593+
JsonPathItem val;
594+
595+
jspGetObjectField(v, i, &key, &val);
596+
597+
if (i)
598+
appendBinaryStringInfo(buf, ", ", 2);
599+
600+
printJsonPathItem(buf, &key, false, false);
601+
appendBinaryStringInfo(buf, ": ", 2);
602+
printJsonPathItem(buf, &val, false, val.type == jpiSequence);
603+
}
604+
605+
appendStringInfoChar(buf, '}');
606+
break;
555607
default:
556608
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
557609
}
@@ -697,6 +749,11 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
697749
read_int32_n(v->content.sequence.elems, base, pos,
698750
v->content.sequence.nelems);
699751
break;
752+
case jpiObject:
753+
read_int32(v->content.object.nfields, base, pos);
754+
read_int32_n(v->content.object.fields, base, pos,
755+
v->content.object.nfields * 2);
756+
break;
700757
default:
701758
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
702759
}
@@ -769,7 +826,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
769826
v->type == jpiStartsWith ||
770827
v->type == jpiMap ||
771828
v->type == jpiSequence ||
772-
v->type == jpiArray
829+
v->type == jpiArray ||
830+
v->type == jpiObject
773831
);
774832

775833
if (a)
@@ -879,3 +937,11 @@ jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem)
879937

880938
jspInitByBuffer(elem, v->base, v->content.sequence.elems[i]);
881939
}
940+
941+
void
942+
jspGetObjectField(JsonPathItem *v, int i, JsonPathItem *key, JsonPathItem *val)
943+
{
944+
Assert(v->type == jpiObject);
945+
jspInitByBuffer(key, v->base, v->content.object.fields[i].key);
946+
jspInitByBuffer(val, v->base, v->content.object.fields[i].val);
947+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,6 +2026,70 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
20262026
found, false);
20272027
}
20282028
break;
2029+
case jpiObject:
2030+
{
2031+
JsonbParseState *ps = NULL;
2032+
JsonbValue *obj;
2033+
int i;
2034+
2035+
pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2036+
2037+
for (i = 0; i < jsp->content.object.nfields; i++)
2038+
{
2039+
JsonbValue *jbv;
2040+
JsonbValue jbvtmp;
2041+
JsonPathItem key;
2042+
JsonPathItem val;
2043+
JsonValueList key_list = { 0 };
2044+
JsonValueList val_list = { 0 };
2045+
2046+
jspGetObjectField(jsp, i, &key, &val);
2047+
2048+
recursiveExecute(cxt, &key, jb, &key_list);
2049+
2050+
if (JsonValueListLength(&key_list) != 1)
2051+
{
2052+
res = jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2053+
break;
2054+
}
2055+
2056+
jbv = JsonValueListHead(&key_list);
2057+
2058+
if (JsonbType(jbv) == jbvScalar)
2059+
jbv = JsonbExtractScalar(jbv->val.binary.data, &jbvtmp);
2060+
2061+
if (jbv->type != jbvString)
2062+
{
2063+
res = jperMakeError(ERRCODE_JSON_SCALAR_REQUIRED); /* XXX */
2064+
break;
2065+
}
2066+
2067+
pushJsonbValue(&ps, WJB_KEY, jbv);
2068+
2069+
recursiveExecute(cxt, &val, jb, &val_list);
2070+
2071+
if (JsonValueListLength(&val_list) != 1)
2072+
{
2073+
res = jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2074+
break;
2075+
}
2076+
2077+
jbv = JsonValueListHead(&val_list);
2078+
2079+
if (jbv->type == jbvObject || jbv->type == jbvArray)
2080+
jbv = JsonbWrapInBinary(jbv, &jbvtmp);
2081+
2082+
pushJsonbValue(&ps, WJB_VALUE, jbv);
2083+
}
2084+
2085+
if (jperIsError(res))
2086+
break;
2087+
2088+
obj = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2089+
2090+
res = recursiveExecuteNext(cxt, jsp, NULL, obj, found, false);
2091+
}
2092+
break;
20292093
default:
20302094
elog(ERROR,"2Wrong state: %d", jsp->type);
20312095
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,16 @@ makeItemSequence(List *elems)
213213
return v;
214214
}
215215

216+
static JsonPathParseItem *
217+
makeItemObject(List *fields)
218+
{
219+
JsonPathParseItem *v = makeItemType(jpiObject);
220+
221+
v->value.object.fields = fields;
222+
223+
return v;
224+
}
225+
216226
%}
217227

218228
/* BISON Declarations */
@@ -246,9 +256,9 @@ makeItemSequence(List *elems)
246256
%type <value> scalar_value path_primary expr pexpr array_accessor
247257
any_path accessor_op key predicate delimited_predicate
248258
index_elem starts_with_initial opt_datetime_template
249-
expr_or_predicate expr_or_seq expr_seq
259+
expr_or_predicate expr_or_seq expr_seq object_field
250260

251-
%type <elems> accessor_expr expr_list
261+
%type <elems> accessor_expr expr_list object_field_list
252262

253263
%type <indexs> index_list
254264

@@ -355,6 +365,18 @@ path_primary:
355365
| '(' expr_seq ')' { $$ = $2; }
356366
| '[' ']' { $$ = makeItemUnary(jpiArray, NULL); }
357367
| '[' expr_or_seq ']' { $$ = makeItemUnary(jpiArray, $2); }
368+
| '{' object_field_list '}' { $$ = makeItemObject($2); }
369+
;
370+
371+
object_field_list:
372+
/* EMPTY */ { $$ = NIL; }
373+
| object_field { $$ = list_make1($1); }
374+
| object_field_list ',' object_field { $$ = lappend($1, $3); }
375+
;
376+
377+
object_field:
378+
key_name ':' expr_or_predicate
379+
{ $$ = makeItemBinary(jpiObjectField, makeItemString(&$1), $3); }
358380
;
359381

360382
accessor_expr:

src/include/utils/jsonpath.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ typedef enum JsonPathItemType {
8686
jpiMap,
8787
jpiSequence,
8888
jpiArray,
89+
jpiObject,
90+
jpiObjectField,
8991
} JsonPathItemType;
9092

9193

@@ -139,6 +141,14 @@ typedef struct JsonPathItem {
139141
int32 *elems;
140142
} sequence;
141143

144+
struct {
145+
int32 nfields;
146+
struct {
147+
int32 key;
148+
int32 val;
149+
} *fields;
150+
} object;
151+
142152
struct {
143153
char *data; /* for bool, numeric and string/key */
144154
int32 datalen; /* filled only for string/key */
@@ -160,6 +170,8 @@ extern char * jspGetString(JsonPathItem *v, int32 *len);
160170
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
161171
JsonPathItem *to, int i);
162172
extern void jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem);
173+
extern void jspGetObjectField(JsonPathItem *v, int i,
174+
JsonPathItem *key, JsonPathItem *val);
163175

164176
/*
165177
* Parsing
@@ -202,6 +214,10 @@ struct JsonPathParseItem {
202214
List *elems;
203215
} sequence;
204216

217+
struct {
218+
List *fields;
219+
} object;
220+
205221
/* scalars */
206222
Numeric numeric;
207223
bool boolean;

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,3 +1722,31 @@ select _jsonpath_query(jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]', '[$[*].map(@ + 1
17221722
[14, 15, 16, 17]
17231723
(1 row)
17241724

1725+
-- extension: object constructors
1726+
select _jsonpath_query(jsonb '[1, 2, 3]', '{}');
1727+
_jsonpath_query
1728+
-----------------
1729+
{}
1730+
(1 row)
1731+
1732+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$[*], 4, 5]}');
1733+
_jsonpath_query
1734+
--------------------------------
1735+
{"a": 5, "b": [1, 2, 3, 4, 5]}
1736+
(1 row)
1737+
1738+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$[*], 4, 5]}.*');
1739+
_jsonpath_query
1740+
-----------------
1741+
5
1742+
[1, 2, 3, 4, 5]
1743+
(2 rows)
1744+
1745+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": ($[*], 4, 5)}');
1746+
ERROR: Singleton SQL/JSON item required
1747+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$.map({x: @, y: @ < 3})[*], {z: "foo"}]}');
1748+
_jsonpath_query
1749+
-----------------------------------------------------------------------------------------------
1750+
{"a": 5, "b": [{"x": 1, "y": true}, {"x": 2, "y": true}, {"x": 3, "y": false}, {"z": "foo"}]}
1751+
(1 row)
1752+

src/test/regress/expected/jsonpath.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,24 @@ select '[[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
509509
[[1, 2], ([(3, 4, 5), 6], []), $."a"[*]]
510510
(1 row)
511511

512+
select '{}'::jsonpath;
513+
jsonpath
514+
----------
515+
{}
516+
(1 row)
517+
518+
select '{a: 1 + 2}'::jsonpath;
519+
jsonpath
520+
--------------
521+
{"a": 1 + 2}
522+
(1 row)
523+
524+
select '{a: 1 + 2, b : (1,2), c: [$[*],4,5], d: { "e e e": "f f f" }}'::jsonpath;
525+
jsonpath
526+
-----------------------------------------------------------------------
527+
{"a": 1 + 2, "b": (1, 2), "c": [$[*], 4, 5], "d": {"e e e": "f f f"}}
528+
(1 row)
529+
512530
select '$ ? (a < 1)'::jsonpath;
513531
jsonpath
514532
-------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,10 @@ select _jsonpath_query(jsonb '[1, 2, 3]', '[(1, (2, $.map(@ + 100)[*])), (4, 5)]
398398
select _jsonpath_query(jsonb '[1, 2, 3]', '[[1, 2], [$.map(@ + 100)[*], 4], 5, [(1,2)?(@ > 5)]]');
399399
select _jsonpath_query(jsonb '[1, 2, 3]', 'strict [1, 2, $.map(@.a)[*], 4, 5]');
400400
select _jsonpath_query(jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]', '[$[*].map(@ + 10)[*] ? (@ > 13)]');
401+
402+
-- extension: object constructors
403+
select _jsonpath_query(jsonb '[1, 2, 3]', '{}');
404+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$[*], 4, 5]}');
405+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$[*], 4, 5]}.*');
406+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": ($[*], 4, 5)}');
407+
select _jsonpath_query(jsonb '[1, 2, 3]', '{a: 2 + 3, "b": [$.map({x: @, y: @ < 3})[*], {z: "foo"}]}');

src/test/regress/sql/jsonpath.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ select '$[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
9595
select '[]'::jsonpath;
9696
select '[[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
9797

98+
select '{}'::jsonpath;
99+
select '{a: 1 + 2}'::jsonpath;
100+
select '{a: 1 + 2, b : (1,2), c: [$[*],4,5], d: { "e e e": "f f f" }}'::jsonpath;
101+
98102
select '$ ? (a < 1)'::jsonpath;
99103
select '$ ? (a < -1)'::jsonpath;
100104
select '$ ? (a < +1)'::jsonpath;

0 commit comments

Comments
 (0)