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

Commit 03cb891

Browse files
author
Nikita Glukhov
committed
Add jsonpath object constructors
1 parent 777b82b commit 03cb891

File tree

8 files changed

+235
-3
lines changed

8 files changed

+235
-3
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,38 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
240240
}
241241
}
242242
break;
243+
case jpiObject:
244+
{
245+
int32 nfields = list_length(item->value.object.fields);
246+
ListCell *lc;
247+
int offset;
248+
249+
appendBinaryStringInfo(buf, (char *) &nfields, sizeof(nfields));
250+
251+
offset = buf->len;
252+
253+
appendStringInfoSpaces(buf, sizeof(int32) * 2 * nfields);
254+
255+
foreach(lc, item->value.object.fields)
256+
{
257+
JsonPathParseItem *field = lfirst(lc);
258+
int32 keypos =
259+
flattenJsonPathParseItem(buf, field->value.args.left,
260+
allowCurrent,
261+
insideArraySubscript);
262+
int32 valpos =
263+
flattenJsonPathParseItem(buf, field->value.args.right,
264+
allowCurrent,
265+
insideArraySubscript);
266+
int32 *ppos = (int32 *) &buf->data[offset];
267+
268+
ppos[0] = keypos;
269+
ppos[1] = valpos;
270+
271+
offset += 2 * sizeof(int32);
272+
}
273+
}
274+
break;
243275
default:
244276
elog(ERROR, "Unknown jsonpath item type: %d", item->type);
245277
}
@@ -607,6 +639,26 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
607639
}
608640
appendStringInfoChar(buf, ']');
609641
break;
642+
case jpiObject:
643+
appendStringInfoChar(buf, '{');
644+
645+
for (i = 0; i < v->content.object.nfields; i++)
646+
{
647+
JsonPathItem key;
648+
JsonPathItem val;
649+
650+
jspGetObjectField(v, i, &key, &val);
651+
652+
if (i)
653+
appendBinaryStringInfo(buf, ", ", 2);
654+
655+
printJsonPathItem(buf, &key, false, false);
656+
appendBinaryStringInfo(buf, ": ", 2);
657+
printJsonPathItem(buf, &val, false, val.type == jpiSequence);
658+
}
659+
660+
appendStringInfoChar(buf, '}');
661+
break;
610662
default:
611663
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
612664
}
@@ -758,6 +810,11 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
758810
read_int32_n(v->content.sequence.elems, base, pos,
759811
v->content.sequence.nelems);
760812
break;
813+
case jpiObject:
814+
read_int32(v->content.object.nfields, base, pos);
815+
read_int32_n(v->content.object.fields, base, pos,
816+
v->content.object.nfields * 2);
817+
break;
761818
default:
762819
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
763820
}
@@ -830,7 +887,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
830887
v->type == jpiStartsWith ||
831888
v->type == jpiMap ||
832889
v->type == jpiSequence ||
833-
v->type == jpiArray
890+
v->type == jpiArray ||
891+
v->type == jpiObject
834892
);
835893

836894
if (a)
@@ -940,3 +998,11 @@ jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem)
940998

941999
jspInitByBuffer(elem, v->base, v->content.sequence.elems[i]);
9421000
}
1001+
1002+
void
1003+
jspGetObjectField(JsonPathItem *v, int i, JsonPathItem *key, JsonPathItem *val)
1004+
{
1005+
Assert(v->type == jpiObject);
1006+
jspInitByBuffer(key, v->base, v->content.object.fields[i].key);
1007+
jspInitByBuffer(val, v->base, v->content.object.fields[i].val);
1008+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,70 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
21992199
found, false);
22002200
}
22012201
break;
2202+
case jpiObject:
2203+
{
2204+
JsonbParseState *ps = NULL;
2205+
JsonbValue *obj;
2206+
int i;
2207+
2208+
pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2209+
2210+
for (i = 0; i < jsp->content.object.nfields; i++)
2211+
{
2212+
JsonbValue *jbv;
2213+
JsonbValue jbvtmp;
2214+
JsonPathItem key;
2215+
JsonPathItem val;
2216+
JsonValueList key_list = { 0 };
2217+
JsonValueList val_list = { 0 };
2218+
2219+
jspGetObjectField(jsp, i, &key, &val);
2220+
2221+
recursiveExecute(cxt, &key, jb, &key_list);
2222+
2223+
if (JsonValueListLength(&key_list) != 1)
2224+
{
2225+
res = jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2226+
break;
2227+
}
2228+
2229+
jbv = JsonValueListHead(&key_list);
2230+
2231+
if (JsonbType(jbv) == jbvScalar)
2232+
jbv = JsonbExtractScalar(jbv->val.binary.data, &jbvtmp);
2233+
2234+
if (jbv->type != jbvString)
2235+
{
2236+
res = jperMakeError(ERRCODE_JSON_SCALAR_REQUIRED); /* XXX */
2237+
break;
2238+
}
2239+
2240+
pushJsonbValue(&ps, WJB_KEY, jbv);
2241+
2242+
recursiveExecute(cxt, &val, jb, &val_list);
2243+
2244+
if (JsonValueListLength(&val_list) != 1)
2245+
{
2246+
res = jperMakeError(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED);
2247+
break;
2248+
}
2249+
2250+
jbv = JsonValueListHead(&val_list);
2251+
2252+
if (jbv->type == jbvObject || jbv->type == jbvArray)
2253+
jbv = JsonbWrapInBinary(jbv, &jbvtmp);
2254+
2255+
pushJsonbValue(&ps, WJB_VALUE, jbv);
2256+
}
2257+
2258+
if (jperIsError(res))
2259+
break;
2260+
2261+
obj = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2262+
2263+
res = recursiveExecuteNext(cxt, jsp, NULL, obj, found, false);
2264+
}
2265+
break;
22022266
default:
22032267
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
22042268
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,16 @@ makeItemSequence(List *elems)
251251
return v;
252252
}
253253

254+
static JsonPathParseItem *
255+
makeItemObject(List *fields)
256+
{
257+
JsonPathParseItem *v = makeItemType(jpiObject);
258+
259+
v->value.object.fields = fields;
260+
261+
return v;
262+
}
263+
254264
%}
255265

256266
/* BISON Declarations */
@@ -283,9 +293,9 @@ makeItemSequence(List *elems)
283293
%type <value> scalar_value path_primary expr pexpr array_accessor
284294
any_path accessor_op key predicate delimited_predicate
285295
index_elem starts_with_initial opt_datetime_template
286-
expr_or_predicate expr_or_seq expr_seq
296+
expr_or_predicate expr_or_seq expr_seq object_field
287297

288-
%type <elems> accessor_expr expr_list
298+
%type <elems> accessor_expr expr_list object_field_list
289299

290300
%type <indexs> index_list
291301

@@ -392,6 +402,18 @@ path_primary:
392402
| '(' expr_seq ')' { $$ = $2; }
393403
| '[' ']' { $$ = makeItemUnary(jpiArray, NULL); }
394404
| '[' expr_or_seq ']' { $$ = makeItemUnary(jpiArray, $2); }
405+
| '{' object_field_list '}' { $$ = makeItemObject($2); }
406+
;
407+
408+
object_field_list:
409+
/* EMPTY */ { $$ = NIL; }
410+
| object_field { $$ = list_make1($1); }
411+
| object_field_list ',' object_field { $$ = lappend($1, $3); }
412+
;
413+
414+
object_field:
415+
key_name ':' expr_or_predicate
416+
{ $$ = makeItemBinary(jpiObjectField, makeItemString(&$1), $3); }
395417
;
396418

397419
accessor_expr:

src/include/utils/jsonpath.h

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

9294
/* XQuery regex mode flags for LIKE_REGEX predicate */
@@ -145,6 +147,14 @@ typedef struct JsonPathItem {
145147
int32 *elems;
146148
} sequence;
147149

150+
struct {
151+
int32 nfields;
152+
struct {
153+
int32 key;
154+
int32 val;
155+
} *fields;
156+
} object;
157+
148158
struct {
149159
char *data; /* for bool, numeric and string/key */
150160
int32 datalen; /* filled only for string/key */
@@ -173,6 +183,8 @@ extern char * jspGetString(JsonPathItem *v, int32 *len);
173183
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
174184
JsonPathItem *to, int i);
175185
extern void jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem);
186+
extern void jspGetObjectField(JsonPathItem *v, int i,
187+
JsonPathItem *key, JsonPathItem *val);
176188

177189
/*
178190
* Parsing
@@ -222,6 +234,10 @@ struct JsonPathParseItem {
222234
List *elems;
223235
} sequence;
224236

237+
struct {
238+
List *fields;
239+
} object;
240+
225241
/* scalars */
226242
Numeric numeric;
227243
bool boolean;

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,3 +1771,37 @@ select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '[$[*].map(@ + 10)[*] ? (@ > 1
17711771
[14, 15, 16, 17]
17721772
(1 row)
17731773

1774+
-- extension: object constructors
1775+
select jsonb '[1, 2, 3]' @* '{}';
1776+
?column?
1777+
----------
1778+
{}
1779+
(1 row)
1780+
1781+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}';
1782+
?column?
1783+
--------------------------------
1784+
{"a": 5, "b": [1, 2, 3, 4, 5]}
1785+
(1 row)
1786+
1787+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}.*';
1788+
?column?
1789+
-----------------
1790+
5
1791+
[1, 2, 3, 4, 5]
1792+
(2 rows)
1793+
1794+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}[*]';
1795+
?column?
1796+
--------------------------------
1797+
{"a": 5, "b": [1, 2, 3, 4, 5]}
1798+
(1 row)
1799+
1800+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": ($[*], 4, 5)}';
1801+
ERROR: Singleton SQL/JSON item required
1802+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$.map({x: @, y: @ < 3})[*], {z: "foo"}]}';
1803+
?column?
1804+
-----------------------------------------------------------------------------------------------
1805+
{"a": 5, "b": [{"x": 1, "y": true}, {"x": 2, "y": true}, {"x": 3, "y": false}, {"z": "foo"}]}
1806+
(1 row)
1807+

src/test/regress/expected/jsonpath.out

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

545+
select '{}'::jsonpath;
546+
jsonpath
547+
----------
548+
{}
549+
(1 row)
550+
551+
select '{a: 1 + 2}'::jsonpath;
552+
jsonpath
553+
--------------
554+
{"a": 1 + 2}
555+
(1 row)
556+
557+
select '{a: 1 + 2, b : (1,2), c: [$[*],4,5], d: { "e e e": "f f f" }}'::jsonpath;
558+
jsonpath
559+
-----------------------------------------------------------------------
560+
{"a": 1 + 2, "b": (1, 2), "c": [$[*], 4, 5], "d": {"e e e": "f f f"}}
561+
(1 row)
562+
545563
select '$ ? (@.a < 1)'::jsonpath;
546564
jsonpath
547565
---------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,11 @@ select jsonb '[1, 2, 3]' @* '[(1, (2, $.map(@ + 100)[*])), (4, 5)]';
393393
select jsonb '[1, 2, 3]' @* '[[1, 2], [$.map(@ + 100)[*], 4], 5, [(1,2)?(@ > 5)]]';
394394
select jsonb '[1, 2, 3]' @* 'strict [1, 2, $.map(@.a)[*], 4, 5]';
395395
select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '[$[*].map(@ + 10)[*] ? (@ > 13)]';
396+
397+
-- extension: object constructors
398+
select jsonb '[1, 2, 3]' @* '{}';
399+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}';
400+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}.*';
401+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": [$[*], 4, 5]}[*]';
402+
select jsonb '[1, 2, 3]' @* '{a: 2 + 3, "b": ($[*], 4, 5)}';
403+
select 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
@@ -102,6 +102,10 @@ select '$[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
102102
select '[]'::jsonpath;
103103
select '[[1, 2], ([(3, 4, 5), 6], []), $.a[*]]'::jsonpath;
104104

105+
select '{}'::jsonpath;
106+
select '{a: 1 + 2}'::jsonpath;
107+
select '{a: 1 + 2, b : (1,2), c: [$[*],4,5], d: { "e e e": "f f f" }}'::jsonpath;
108+
105109
select '$ ? (@.a < 1)'::jsonpath;
106110
select '$ ? (@.a < -1)'::jsonpath;
107111
select '$ ? (@.a < +1)'::jsonpath;

0 commit comments

Comments
 (0)