Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb163914

Browse files
author
Nikita Glukhov
committed
Add jsonpath object constructors
1 parent5336bd8 commitb163914

File tree

9 files changed

+251
-2
lines changed

9 files changed

+251
-2
lines changed

‎doc/src/sgml/func.sgml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13701,6 +13701,13 @@ table2-mapping
1370113701
<entry><literal>pg [$[*], 4, 5]</literal></entry>
1370213702
<entry><literal>[1, 2, 3, 4, 5]</literal></entry>
1370313703
</row>
13704+
<row>
13705+
<entry>Object constructor</entry>
13706+
<entry>Construct a JSON object by enumeration of its fields enclosed in braces</entry>
13707+
<entry><literal>{"x": "y"}</literal></entry>
13708+
<entry><literal>pg {a: 1, "b c": $.x}</literal></entry>
13709+
<entry><literal>{"a": 1, "b c": "y"}</literal></entry>
13710+
</row>
1370413711
</tbody>
1370513712
</tgroup>
1370613713
</table>

‎src/backend/utils/adt/jsonpath.c

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,40 @@ flattenJsonPathParseItem(JsonPathEncodingContext *cxt, JsonPathParseItem *item,
479479
}
480480
}
481481
break;
482+
casejpiObject:
483+
{
484+
int32nfields=list_length(item->value.object.fields);
485+
ListCell*lc;
486+
intoffset;
487+
488+
checkJsonPathExtensionsEnabled(cxt,item->type);
489+
490+
appendBinaryStringInfo(buf, (char*)&nfields,sizeof(nfields));
491+
492+
offset=buf->len;
493+
494+
appendStringInfoSpaces(buf,sizeof(int32)*2*nfields);
495+
496+
foreach(lc,item->value.object.fields)
497+
{
498+
JsonPathParseItem*field=lfirst(lc);
499+
int32keypos=
500+
flattenJsonPathParseItem(cxt,field->value.args.left,
501+
nestingLevel,
502+
insideArraySubscript);
503+
int32valpos=
504+
flattenJsonPathParseItem(cxt,field->value.args.right,
505+
nestingLevel,
506+
insideArraySubscript);
507+
int32*ppos= (int32*)&buf->data[offset];
508+
509+
ppos[0]=keypos-pos;
510+
ppos[1]=valpos-pos;
511+
512+
offset+=2*sizeof(int32);
513+
}
514+
}
515+
break;
482516
default:
483517
elog(ERROR,"unrecognized jsonpath item type: %d",item->type);
484518
}
@@ -795,6 +829,26 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
795829
}
796830
appendStringInfoChar(buf,']');
797831
break;
832+
casejpiObject:
833+
appendStringInfoChar(buf,'{');
834+
835+
for (i=0;i<v->content.object.nfields;i++)
836+
{
837+
JsonPathItemkey;
838+
JsonPathItemval;
839+
840+
jspGetObjectField(v,i,&key,&val);
841+
842+
if (i)
843+
appendBinaryStringInfo(buf,", ",2);
844+
845+
printJsonPathItem(buf,&key, false, false);
846+
appendBinaryStringInfo(buf,": ",2);
847+
printJsonPathItem(buf,&val, false,val.type==jpiSequence);
848+
}
849+
850+
appendStringInfoChar(buf,'}');
851+
break;
798852
default:
799853
elog(ERROR,"unrecognized jsonpath item type: %d",v->type);
800854
}
@@ -1011,6 +1065,11 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
10111065
read_int32_n(v->content.sequence.elems,base,pos,
10121066
v->content.sequence.nelems);
10131067
break;
1068+
casejpiObject:
1069+
read_int32(v->content.object.nfields,base,pos);
1070+
read_int32_n(v->content.object.fields,base,pos,
1071+
v->content.object.nfields*2);
1072+
break;
10141073
default:
10151074
elog(ERROR,"unrecognized jsonpath item type: %d",v->type);
10161075
}
@@ -1078,7 +1137,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
10781137
v->type==jpiKeyValue||
10791138
v->type==jpiStartsWith||
10801139
v->type==jpiSequence||
1081-
v->type==jpiArray);
1140+
v->type==jpiArray||
1141+
v->type==jpiObject);
10821142

10831143
if (a)
10841144
jspInitByBuffer(a,v->base,v->nextPos);
@@ -1181,3 +1241,11 @@ jspGetSequenceElement(JsonPathItem *v, int i, JsonPathItem *elem)
11811241

11821242
jspInitByBuffer(elem,v->base,v->content.sequence.elems[i]);
11831243
}
1244+
1245+
void
1246+
jspGetObjectField(JsonPathItem*v,inti,JsonPathItem*key,JsonPathItem*val)
1247+
{
1248+
Assert(v->type==jpiObject);
1249+
jspInitByBuffer(key,v->base,v->content.object.fields[i].key);
1250+
jspInitByBuffer(val,v->base,v->content.object.fields[i].val);
1251+
}

‎src/backend/utils/adt/jsonpath_exec.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,62 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
11941194
break;
11951195
}
11961196

1197+
casejpiObject:
1198+
{
1199+
JsonbParseState*ps=NULL;
1200+
inti;
1201+
1202+
pushJsonbValue(&ps,WJB_BEGIN_OBJECT,NULL);
1203+
1204+
for (i=0;i<jsp->content.object.nfields;i++)
1205+
{
1206+
JsonbValue*jbv;
1207+
JsonbValuejbvtmp;
1208+
JsonPathItemkey;
1209+
JsonPathItemval;
1210+
JsonValueListkey_list= {0};
1211+
JsonValueListval_list= {0};
1212+
1213+
jspGetObjectField(jsp,i,&key,&val);
1214+
1215+
res=executeItem(cxt,&key,jb,&key_list);
1216+
if (jperIsError(res))
1217+
returnres;
1218+
1219+
if (JsonValueListLength(&key_list)!=1||
1220+
!(jbv=getScalar(JsonValueListHead(&key_list),jbvString)))
1221+
RETURN_ERROR(ereport(ERROR,
1222+
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
1223+
errmsg("key in jsonpath object constructor is not a single string"))));
1224+
1225+
res=executeItem(cxt,&val,jb,&val_list);
1226+
if (jperIsError(res))
1227+
returnres;
1228+
1229+
if (jspIgnoreStructuralErrors(cxt)&&
1230+
JsonValueListIsEmpty(&val_list))
1231+
continue;/* skip empty fields in lax mode */
1232+
1233+
if (JsonValueListLength(&val_list)!=1)
1234+
RETURN_ERROR(ereport(ERROR,
1235+
(errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1236+
errmsg("value in jsonpath object constructor is not single"),
1237+
errhint("Use jsonpath array syntax to wrap multi-item sequences into arrays"))));
1238+
1239+
pushJsonbValue(&ps,WJB_KEY,jbv);
1240+
1241+
jbv=JsonValueListHead(&val_list);
1242+
jbv=wrapJsonObjectOrArray(jbv,&jbvtmp);
1243+
1244+
pushJsonbValue(&ps,WJB_VALUE,jbv);
1245+
}
1246+
1247+
jb=pushJsonbValue(&ps,WJB_END_OBJECT,NULL);
1248+
1249+
res=executeNextItem(cxt,jsp,NULL,jb,found, false);
1250+
break;
1251+
}
1252+
11971253
default:
11981254
elog(ERROR,"unrecognized jsonpath item type: %d",jsp->type);
11991255
}

‎src/backend/utils/adt/jsonpath_gram.y

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
5757
JsonPathString *pattern,
5858
JsonPathString *flags);
5959
static JsonPathParseItem *makeItemSequence(List *elems);
60+
static JsonPathParseItem *makeItemObject(List *fields);
6061

6162
/*
6263
* Bison doesn't allocate anything that needs to live across parser calls,
@@ -103,8 +104,9 @@ static JsonPathParseItem *makeItemSequence(List *elems);
103104
any_pathaccessor_opkeypredicatedelimited_predicate
104105
index_elemstarts_with_initialexpr_or_predicate
105106
datetime_templateopt_datetime_templateexpr_seqexpr_or_seq
107+
object_field
106108

107-
%type<elems>accessor_exprexpr_list
109+
%type<elems>accessor_exprexpr_listobject_field_list
108110

109111
%type<indexs>index_list
110112

@@ -219,6 +221,18 @@ path_primary:
219221
|'('expr_seq')'{$$ =$2; }
220222
|'['']'{$$ = makeItemUnary(jpiArray,NULL); }
221223
|'['expr_or_seq']'{$$ = makeItemUnary(jpiArray,$2); }
224+
|'{'object_field_list'}'{$$ = makeItemObject($2); }
225+
;
226+
227+
object_field_list:
228+
/* EMPTY*/{$$ = NIL; }
229+
|object_field{$$ = list_make1($1); }
230+
|object_field_list','object_field{$$ = lappend($1,$3); }
231+
;
232+
233+
object_field:
234+
key_name':'expr_or_predicate
235+
{$$ = makeItemBinary(jpiObjectField, makeItemString(&$1),$3); }
222236
;
223237

224238
accessor_expr:
@@ -578,6 +592,16 @@ makeItemSequence(List *elems)
578592
return v;
579593
}
580594

595+
static JsonPathParseItem *
596+
makeItemObject(List *fields)
597+
{
598+
JsonPathParseItem *v =makeItemType(jpiObject);
599+
600+
v->value.object.fields = fields;
601+
602+
return v;
603+
}
604+
581605
/*
582606
* Convert from XQuery regex flags to those recognized by our regex library.
583607
*/

‎src/include/utils/jsonpath.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ typedef enum JsonPathItemType
8989
jpiLikeRegex,/* LIKE_REGEX predicate */
9090
jpiSequence,/* sequence constructor: 'expr, ...' */
9191
jpiArray,/* array constructor: '[expr, ...]' */
92+
jpiObject,/* object constructor: '{ key : value, ... }' */
93+
jpiObjectField,/* element of object constructor: 'key : value' */
9294
}JsonPathItemType;
9395

9496
/* XQuery regex mode flags for LIKE_REGEX predicate */
@@ -155,6 +157,16 @@ typedef struct JsonPathItem
155157
int32*elems;
156158
}sequence;
157159

160+
struct
161+
{
162+
int32nfields;
163+
struct
164+
{
165+
int32key;
166+
int32val;
167+
}*fields;
168+
}object;
169+
158170
struct
159171
{
160172
char*data;/* for bool, numeric and string/key */
@@ -185,6 +197,8 @@ extern char *jspGetString(JsonPathItem *v, int32 *len);
185197
externbooljspGetArraySubscript(JsonPathItem*v,JsonPathItem*from,
186198
JsonPathItem*to,inti);
187199
externvoidjspGetSequenceElement(JsonPathItem*v,inti,JsonPathItem*elem);
200+
externvoidjspGetObjectField(JsonPathItem*v,inti,
201+
JsonPathItem*key,JsonPathItem*val);
188202

189203
externconstchar*jspOperationName(JsonPathItemTypetype);
190204

@@ -242,6 +256,10 @@ struct JsonPathParseItem
242256
List*elems;
243257
}sequence;
244258

259+
struct {
260+
List*fields;
261+
}object;
262+
245263
/* scalars */
246264
Numericnumeric;
247265
boolboolean;

‎src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2635,3 +2635,47 @@ select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'pg [$[*][*] ? (@ > 3
26352635
[4, 5, 6, 7]
26362636
(1 row)
26372637

2638+
-- extension: object constructors
2639+
select jsonb_path_query('[1, 2, 3]', 'pg {}');
2640+
jsonb_path_query
2641+
------------------
2642+
{}
2643+
(1 row)
2644+
2645+
select jsonb_path_query('[1, 2, 3]', 'pg {a: 2 + 3, "b": [$[*], 4, 5]}');
2646+
jsonb_path_query
2647+
--------------------------------
2648+
{"a": 5, "b": [1, 2, 3, 4, 5]}
2649+
(1 row)
2650+
2651+
select jsonb_path_query('[1, 2, 3]', 'pg {a: 2 + 3, "b": [$[*], 4, 5]}.*');
2652+
jsonb_path_query
2653+
------------------
2654+
5
2655+
[1, 2, 3, 4, 5]
2656+
(2 rows)
2657+
2658+
select jsonb_path_query('[1, 2, 3]', 'pg {a: 2 + 3, "b": [$[*], 4, 5]}[*]');
2659+
jsonb_path_query
2660+
--------------------------------
2661+
{"a": 5, "b": [1, 2, 3, 4, 5]}
2662+
(1 row)
2663+
2664+
select jsonb_path_query('[1, 2, 3]', 'pg {a: 2 + 3, "b": ($[*], 4, 5)}');
2665+
ERROR: value in jsonpath object constructor is not single
2666+
HINT: Use jsonpath array syntax to wrap multi-item sequences into arrays
2667+
select jsonb_path_query('[1, 2, 3]', 'pg {a: 2 + 3, "b": {x: $, y: $[1] > 2, z: "foo"}}');
2668+
jsonb_path_query
2669+
---------------------------------------------------------
2670+
{"a": 5, "b": {"x": [1, 2, 3], "y": false, "z": "foo"}}
2671+
(1 row)
2672+
2673+
select jsonb_path_query('[1, 2, 3]', 'pg {a: 2 + 3, "b": $[*] ? (@ > 3), c: "foo"}');
2674+
jsonb_path_query
2675+
----------------------
2676+
{"a": 5, "c": "foo"}
2677+
(1 row)
2678+
2679+
select jsonb_path_query('[1, 2, 3]', 'pg strict {a: 2 + 3, "b": $[*] ? (@ > 3), c: "foo"}');
2680+
ERROR: value in jsonpath object constructor is not single
2681+
HINT: Use jsonpath array syntax to wrap multi-item sequences into arrays

‎src/test/regress/expected/jsonpath.out

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

619+
select 'pg {}'::jsonpath;
620+
jsonpath
621+
----------
622+
pg {}
623+
(1 row)
624+
625+
select 'pg {a: 1 + 2}'::jsonpath;
626+
jsonpath
627+
-----------------
628+
pg {"a": 1 + 2}
629+
(1 row)
630+
631+
select 'pg {a: 1 + 2, b : (1,2), c: [$[*],4,5], d: { "e e e": "f f f" }}'::jsonpath;
632+
jsonpath
633+
--------------------------------------------------------------------------
634+
pg {"a": 1 + 2, "b": (1, 2), "c": [$[*], 4, 5], "d": {"e e e": "f f f"}}
635+
(1 row)
636+
619637
select '$ ? (@.a < 1)'::jsonpath;
620638
jsonpath
621639
---------------

‎src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,13 @@ select jsonb_path_query('[1, 2, 3]', 'pg [(1, (2, $[*])), (4, 5)]');
598598
select jsonb_path_query('[1, 2, 3]','pg [[1, 2], [$[*], 4], 5, [(1,2)?(@ > 5)]]');
599599
select jsonb_path_query('[1, 2, 3]','pg strict [1, 2, $[*].a, 4, 5]');
600600
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]','pg [$[*][*] ? (@ > 3)]');
601+
602+
-- extension: object constructors
603+
select jsonb_path_query('[1, 2, 3]','pg {}');
604+
select jsonb_path_query('[1, 2, 3]','pg {a: 2 + 3, "b": [$[*], 4, 5]}');
605+
select jsonb_path_query('[1, 2, 3]','pg {a: 2 + 3, "b": [$[*], 4, 5]}.*');
606+
select jsonb_path_query('[1, 2, 3]','pg {a: 2 + 3, "b": [$[*], 4, 5]}[*]');
607+
select jsonb_path_query('[1, 2, 3]','pg {a: 2 + 3, "b": ($[*], 4, 5)}');
608+
select jsonb_path_query('[1, 2, 3]','pg {a: 2 + 3, "b": {x: $, y: $[1] > 2, z: "foo"}}');
609+
select jsonb_path_query('[1, 2, 3]','pg {a: 2 + 3, "b": $[*] ? (@ > 3), c: "foo"}');
610+
select jsonb_path_query('[1, 2, 3]','pg strict {a: 2 + 3, "b": $[*] ? (@ > 3), c: "foo"}');

‎src/test/regress/sql/jsonpath.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ select 'pg $[(1, (2, $.a)), 3, (4, 5)]'::jsonpath;
116116
select'pg []'::jsonpath;
117117
select'pg [[1,2], ([(3,4,5),6], []), $.a[*]]'::jsonpath;
118118
119+
select'pg {}'::jsonpath;
120+
select'pg {a:1+2}'::jsonpath;
121+
select'pg {a:1+2, b : (1,2), c: [$[*],4,5], d: {"e e e":"f f f" }}'::jsonpath;
122+
119123
select'$ ? (@.a<1)'::jsonpath;
120124
select'$ ? (@.a<-1)'::jsonpath;
121125
select'$ ? (@.a<+1)'::jsonpath;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp