Skip to content

Commit ef30f6f

Browse files
committed
add jsonb any element
1 parent a4656b3 commit ef30f6f

File tree

6 files changed

+236
-25
lines changed

6 files changed

+236
-25
lines changed

src/backend/parser/gram.y

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
176176
bool *deferrable, bool *initdeferred, bool *not_valid,
177177
bool *no_inherit, core_yyscan_t yyscanner);
178178
static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
179-
static SelectStmt * makeElementSubselect(Node *of, const char *aliasname,
180-
Node *clause, int location);
179+
static SelectStmt * makeElementSubselect(int kind, bool recursive, Node *of,
180+
const char *aliasname, Node *clause, int location);
181181

182182
%}
183183

@@ -291,6 +291,7 @@ static SelectStmt * makeElementSubselect(Node *of, const char *aliasname,
291291
%type <boolean> opt_or_replace
292292
opt_grant_grant_option opt_grant_admin_option
293293
opt_nowait opt_if_exists opt_with_data
294+
opt_anywhere
294295
%type <ival> opt_nowait_or_skip
295296

296297
%type <list> OptRoleList AlterOptRoleList
@@ -448,6 +449,7 @@ static SelectStmt * makeElementSubselect(Node *of, const char *aliasname,
448449
%type <node> case_expr case_arg when_clause case_default
449450
%type <list> when_clause_list
450451
%type <ival> sub_type
452+
%type <ival> any_or_each any_or_each_kind
451453
%type <node> ctext_expr
452454
%type <value> NumericOnly
453455
%type <list> NumericOnly_list
@@ -563,7 +565,7 @@ static SelectStmt * makeElementSubselect(Node *of, const char *aliasname,
563565

564566
/* ordinary key words in alphabetical order */
565567
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
566-
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
568+
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ANYWHERE ARRAY AS ASC
567569
ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
568570

569571
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
@@ -12058,30 +12060,42 @@ c_expr: columnref { $$ = $1; }
1205812060
g->location = @1;
1205912061
$$ = (Node *)g;
1206012062
}
12061-
| ANY_EL ELEMENT OF b_expr AS ColId SATISFIES '(' a_expr ')'
12063+
| any_or_each any_or_each_kind opt_anywhere OF b_expr AS ColId SATISFIES '(' a_expr ')'
1206212064
{
1206312065
SubLink *n = makeNode(SubLink);
1206412066

1206512067
n->subLinkType = EXISTS_SUBLINK;
1206612068
n->subLinkId = 0;
1206712069
n->testexpr = NULL;
1206812070
n->operName = NIL;
12069-
n->subselect = (Node*)makeElementSubselect($4, $6, $9, @1);
12071+
n->subselect = (Node*)makeElementSubselect(
12072+
$2, $3,
12073+
$5, $7,
12074+
($1 == EACH) ? makeNotExpr($10, @1) : $10,
12075+
@1
12076+
);
1207012077
n->location = @1;
12071-
$$ = (Node *)n;
12078+
if ($1 == EACH)
12079+
$$ = makeNotExpr((Node *)n, @1);
12080+
else
12081+
$$ = (Node *)n;
1207212082
}
12073-
| EACH ELEMENT OF b_expr AS ColId SATISFIES '(' a_expr ')'
12074-
{
12075-
SubLink *n = makeNode(SubLink);
12083+
;
1207612084

12077-
n->subLinkType = EXISTS_SUBLINK;
12078-
n->subLinkId = 0;
12079-
n->testexpr = NULL;
12080-
n->operName = NIL;
12081-
n->subselect = (Node*)makeElementSubselect($4, $6, makeNotExpr($9, @1), @1);
12082-
n->location = @1;
12083-
$$ = makeNotExpr((Node *)n, @1);;
12084-
}
12085+
any_or_each:
12086+
ANY_EL { $$ = ANY_EL; }
12087+
| EACH { $$ = EACH; }
12088+
;
12089+
12090+
any_or_each_kind:
12091+
ELEMENT { $$ = ELEMENT; }
12092+
| KEY { $$ = KEY; }
12093+
| VALUE_P { $$ = VALUE_P; }
12094+
;
12095+
12096+
opt_anywhere:
12097+
ANYWHERE { $$ = true; }
12098+
| /* empty */ { $$ = false; }
1208512099
;
1208612100

1208712101
func_application: func_name '(' ')'
@@ -13614,6 +13628,7 @@ unreserved_keyword:
1361413628
| ALSO
1361513629
| ALTER
1361613630
| ALWAYS
13631+
| ANYWHERE
1361713632
| ASSERTION
1361813633
| ASSIGNMENT
1361913634
| AT
@@ -14858,17 +14873,36 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
1485814873
}
1485914874

1486014875
static SelectStmt *
14861-
makeElementSubselect(Node *of, const char *aliasname, Node *clause, int location)
14876+
makeElementSubselect(int kind, bool recursive, Node *of,
14877+
const char *aliasname, Node *clause, int location)
1486214878
{
1486314879
ResTarget *target = makeNode(ResTarget);
1486414880
FuncCall *func_call;
1486514881
RangeFunction *table_ref = makeNode(RangeFunction);
1486614882
SelectStmt *subselect = makeNode(SelectStmt);
14883+
char *funcname;
1486714884

1486814885
target->val = (Node*)makeIntConst(1, location);
1486914886
target->location = location;
1487014887

14871-
func_call = makeFuncCall(SystemFuncName("unnest"), list_make1(of), location);
14888+
switch(kind)
14889+
{
14890+
case ELEMENT:
14891+
funcname = "unnest_element";
14892+
break;
14893+
case KEY:
14894+
funcname = "unnest_key";
14895+
break;
14896+
case VALUE_P:
14897+
funcname = "unnest_value";
14898+
break;
14899+
default:
14900+
elog(ERROR, "unkown ANY_EL");
14901+
}
14902+
14903+
func_call = makeFuncCall(SystemFuncName(funcname),
14904+
list_make2(of, makeBoolAConst(recursive, location)),
14905+
location);
1487214906

1487314907
table_ref->functions = list_make1(list_make2(func_call, NIL));
1487414908
table_ref->alias = makeAlias(aliasname, NIL);

src/backend/parser/parser.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,15 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
191191
}
192192
break;
193193
case ANY:
194-
if (next_token == ELEMENT)
195-
cur_token = ANY_EL;
194+
/* Replace ANY by ANY_EL if it's followed by ELEMENT or KEY or VALUE */
195+
switch (next_token)
196+
{
197+
case ELEMENT:
198+
case KEY:
199+
case VALUE_P:
200+
cur_token = ANY_EL;
201+
break;
202+
}
196203
break;
197204
}
198205

src/backend/utils/adt/jsonfuncs.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3898,3 +3898,156 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
38983898
}
38993899
}
39003900
}
3901+
3902+
typedef struct UnnestState
3903+
{
3904+
MemoryContext ctx;
3905+
Jsonb *jb;
3906+
JsonbIterator *it;
3907+
bool skipNested;
3908+
JsonbIteratorToken type;
3909+
}
3910+
UnnestState;
3911+
3912+
static void finiUnnest(UnnestState *state);
3913+
3914+
static void
3915+
initUnnest(FuncCallContext *funcctx, Datum jsonb, bool recursive,
3916+
JsonbIteratorToken type)
3917+
{
3918+
MemoryContext oldcontext;
3919+
UnnestState *state;
3920+
JsonbValue v;
3921+
int r;
3922+
3923+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
3924+
3925+
state = palloc0(sizeof(*state));
3926+
state->ctx = funcctx->multi_call_memory_ctx;
3927+
state->jb = DatumGetJsonbCopy(jsonb);
3928+
state->it = JsonbIteratorInit(&state->jb->root);
3929+
state->skipNested = !recursive;
3930+
state->type = type;
3931+
3932+
r = JsonbIteratorNext(&state->it, &v, false);
3933+
3934+
if (JB_ROOT_IS_SCALAR(state->jb))
3935+
{
3936+
r = JsonbIteratorNext(&state->it, &v, true);
3937+
r = JsonbIteratorNext(&state->it, &v, true);
3938+
Assert(r == WJB_DONE);
3939+
}
3940+
3941+
MemoryContextSwitchTo(oldcontext);
3942+
3943+
if (r == WJB_DONE)
3944+
{
3945+
finiUnnest(state);
3946+
state = NULL;
3947+
}
3948+
3949+
funcctx->user_fctx = (void *) state;
3950+
}
3951+
3952+
static Jsonb*
3953+
nextUnnest(UnnestState *state)
3954+
{
3955+
MemoryContext oldcontext;
3956+
JsonbValue v;
3957+
int r;
3958+
3959+
/*
3960+
* Iterator should work in long-lived memory context
3961+
*/
3962+
oldcontext = MemoryContextSwitchTo(state->ctx);
3963+
3964+
while((r = JsonbIteratorNext(&state->it, &v, state->skipNested)) != WJB_DONE)
3965+
{
3966+
if (r == state->type)
3967+
break;
3968+
}
3969+
3970+
MemoryContextSwitchTo(oldcontext);
3971+
3972+
return (r == state->type) ? JsonbValueToJsonb(&v) : NULL;
3973+
}
3974+
3975+
static void
3976+
finiUnnest(UnnestState *state)
3977+
{
3978+
if (state)
3979+
{
3980+
pfree(state->jb);
3981+
pfree(state);
3982+
}
3983+
}
3984+
3985+
Datum
3986+
jsonb_unnest_element(PG_FUNCTION_ARGS)
3987+
{
3988+
FuncCallContext *funcctx;
3989+
UnnestState *state;
3990+
Jsonb *r;
3991+
3992+
if (SRF_IS_FIRSTCALL())
3993+
initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0),
3994+
PG_GETARG_BOOL(1), WJB_ELEM);
3995+
3996+
funcctx = SRF_PERCALL_SETUP();
3997+
state = funcctx->user_fctx;
3998+
3999+
if (state == NULL || (r = nextUnnest(state)) == NULL)
4000+
{
4001+
finiUnnest(state);
4002+
SRF_RETURN_DONE(funcctx);
4003+
}
4004+
4005+
SRF_RETURN_NEXT(funcctx, JsonbGetDatum(r));
4006+
}
4007+
4008+
Datum
4009+
jsonb_unnest_value(PG_FUNCTION_ARGS)
4010+
{
4011+
FuncCallContext *funcctx;
4012+
UnnestState *state;
4013+
Jsonb *r;
4014+
4015+
if (SRF_IS_FIRSTCALL())
4016+
initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0),
4017+
PG_GETARG_BOOL(1), WJB_VALUE);
4018+
4019+
funcctx = SRF_PERCALL_SETUP();
4020+
state = funcctx->user_fctx;
4021+
4022+
if (state == NULL || (r = nextUnnest(state)) == NULL)
4023+
{
4024+
finiUnnest(state);
4025+
SRF_RETURN_DONE(funcctx);
4026+
}
4027+
4028+
SRF_RETURN_NEXT(funcctx, JsonbGetDatum(r));
4029+
}
4030+
4031+
Datum
4032+
jsonb_unnest_key(PG_FUNCTION_ARGS)
4033+
{
4034+
FuncCallContext *funcctx;
4035+
UnnestState *state;
4036+
Jsonb *r;
4037+
4038+
if (SRF_IS_FIRSTCALL())
4039+
initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0),
4040+
PG_GETARG_BOOL(1), WJB_KEY);
4041+
4042+
funcctx = SRF_PERCALL_SETUP();
4043+
state = funcctx->user_fctx;
4044+
4045+
if (state == NULL || (r = nextUnnest(state)) == NULL)
4046+
{
4047+
finiUnnest(state);
4048+
SRF_RETURN_DONE(funcctx);
4049+
}
4050+
4051+
SRF_RETURN_NEXT(funcctx, JsonbGetDatum(r));
4052+
}
4053+

src/include/catalog/pg_proc.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,9 @@ DATA(insert OID = 1286 ( array_fill PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 22
924924
DESCR("array constructor with value");
925925
DATA(insert OID = 2331 ( unnest PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2283 "2277" _null_ _null_ _null_ _null_ _null_ array_unnest _null_ _null_ _null_ ));
926926
DESCR("expand array to set of rows");
927+
/* just for compatibility with jsonb_unnest* for any/each element clause */
928+
DATA(insert OID = 7644 ( unnest_element PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 2283 "2277 16" _null_ _null_ _null_ _null_ _null_ array_unnest _null_ _null_ _null_ ));
929+
DESCR("expand array to set of rows");
927930
DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2277 2283" _null_ _null_ _null_ _null_ _null_ array_remove _null_ _null_ _null_ ));
928931
DESCR("remove any occurrences of an element from an array");
929932
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
@@ -4863,6 +4866,12 @@ DATA(insert OID = 3305 ( jsonb_replace PGNSP PGUID 12 1 0 0 0 f f f f t f i
48634866
DESCR("Replace part of a jsonb");
48644867
DATA(insert OID = 3306 ( jsonb_pretty PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_pretty _null_ _null_ _null_ ));
48654868
DESCR("Indented text from jsonb");
4869+
DATA(insert OID = 7645 ( unnest_element PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 3802 "3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_unnest_element _null_ _null_ _null_ ));
4870+
DESCR("expand elements from jsonb");
4871+
DATA(insert OID = 7646 ( unnest_value PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 3802 "3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_unnest_value _null_ _null_ _null_ ));
4872+
DESCR("expand values from jsonb");
4873+
DATA(insert OID = 7647 ( unnest_key PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 3802 "3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_unnest_key _null_ _null_ _null_ ));
4874+
DESCR("expand keys from jsonb");
48664875
/* txid */
48674876
DATA(insert OID = 2939 ( txid_snapshot_in PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
48684877
DESCR("I/O");

src/include/parser/kwlist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ PG_KEYWORD("analyse", ANALYSE, RESERVED_KEYWORD) /* British spelling */
4242
PG_KEYWORD("analyze", ANALYZE, RESERVED_KEYWORD)
4343
PG_KEYWORD("and", AND, RESERVED_KEYWORD)
4444
PG_KEYWORD("any", ANY, RESERVED_KEYWORD)
45+
PG_KEYWORD("anywhere", ANYWHERE, UNRESERVED_KEYWORD)
4546
PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD)
4647
PG_KEYWORD("as", AS, RESERVED_KEYWORD)
4748
PG_KEYWORD("asc", ASC, RESERVED_KEYWORD)

src/include/utils/jsonb.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ typedef enum
6565
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
6666

6767
/* Convenience macros */
68-
#define DatumGetJsonb(d) ((Jsonb *) PG_DETOAST_DATUM(d))
69-
#define JsonbGetDatum(p) PointerGetDatum(p)
70-
#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
71-
#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
68+
#define DatumGetJsonb(d) ((Jsonb *) PG_DETOAST_DATUM(d))
69+
#define DatumGetJsonbCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
70+
#define JsonbGetDatum(p) PointerGetDatum(p)
71+
#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
72+
#define PG_GETARG_JSONB_COPY(x) DatumGetJsonbCopy(PG_GETARG_DATUM(x))
73+
#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
7274

7375
typedef struct JsonbPair JsonbPair;
7476
typedef struct JsonbValue JsonbValue;
@@ -400,6 +402,11 @@ extern Datum jsonb_pretty(PG_FUNCTION_ARGS);
400402
/* concatenation */
401403
extern Datum jsonb_concat(PG_FUNCTION_ARGS);
402404

405+
/* unnesting function */
406+
extern Datum jsonb_unnest_element(PG_FUNCTION_ARGS);
407+
extern Datum jsonb_unnest_value(PG_FUNCTION_ARGS);
408+
extern Datum jsonb_unnest_key(PG_FUNCTION_ARGS);
409+
403410
/* deletion */
404411
Datum jsonb_delete(PG_FUNCTION_ARGS);
405412
Datum jsonb_delete_idx(PG_FUNCTION_ARGS);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy