Skip to content

Commit cd4b585

Browse files
author
Nikita Glukhov
committed
Add JSON_OBJECTAGG() and JSON_ARRAYAGG() transformation
1 parent 1233b33 commit cd4b585

File tree

15 files changed

+853
-76
lines changed

15 files changed

+853
-76
lines changed

src/backend/executor/execExpr.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,12 @@ ExecInitExprRec(Expr *node, ExprState *state,
21382138
int nargs = list_length(args);
21392139
int argno = 0;
21402140

2141+
if (ctor->func)
2142+
{
2143+
ExecInitExprRec(ctor->func, state, resv, resnull);
2144+
break;
2145+
}
2146+
21412147
scratch.opcode = EEOP_JSON_CTOR;
21422148
scratch.d.json_ctor.ctor = ctor;
21432149
scratch.d.json_ctor.arg_values = palloc(sizeof(Datum) * nargs);

src/backend/nodes/copyfuncs.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,26 @@ _copyJsonObjectCtor(const JsonObjectCtor *from)
23442344
return newnode;
23452345
}
23462346

2347+
/*
2348+
* _copyJsonObjectAgg
2349+
*/
2350+
static JsonObjectAgg *
2351+
_copyJsonObjectAgg(const JsonObjectAgg *from)
2352+
{
2353+
JsonObjectAgg *newnode = makeNode(JsonObjectAgg);
2354+
2355+
COPY_NODE_FIELD(ctor.output);
2356+
COPY_NODE_FIELD(ctor.agg_filter);
2357+
COPY_NODE_FIELD(ctor.agg_order);
2358+
COPY_NODE_FIELD(ctor.over);
2359+
COPY_LOCATION_FIELD(ctor.location);
2360+
COPY_NODE_FIELD(arg);
2361+
COPY_SCALAR_FIELD(absent_on_null);
2362+
COPY_SCALAR_FIELD(unique);
2363+
2364+
return newnode;
2365+
}
2366+
23472367
/*
23482368
* _copyJsonOutput
23492369
*/
@@ -2374,6 +2394,25 @@ _copyJsonArrayCtor(const JsonArrayCtor *from)
23742394
return newnode;
23752395
}
23762396

2397+
/*
2398+
* _copyJsonArrayAgg
2399+
*/
2400+
static JsonArrayAgg *
2401+
_copyJsonArrayAgg(const JsonArrayAgg *from)
2402+
{
2403+
JsonArrayAgg *newnode = makeNode(JsonArrayAgg);
2404+
2405+
COPY_NODE_FIELD(ctor.output);
2406+
COPY_NODE_FIELD(ctor.agg_filter);
2407+
COPY_NODE_FIELD(ctor.agg_order);
2408+
COPY_NODE_FIELD(ctor.over);
2409+
COPY_LOCATION_FIELD(ctor.location);
2410+
COPY_NODE_FIELD(arg);
2411+
COPY_SCALAR_FIELD(absent_on_null);
2412+
2413+
return newnode;
2414+
}
2415+
23772416
/* ****************************************************************
23782417
* pathnodes.h copy functions
23792418
*
@@ -5289,12 +5328,18 @@ copyObjectImpl(const void *from)
52895328
case T_JsonObjectCtor:
52905329
retval = _copyJsonObjectCtor(from);
52915330
break;
5331+
case T_JsonObjectAgg:
5332+
retval = _copyJsonObjectAgg(from);
5333+
break;
52925334
case T_JsonOutput:
52935335
retval = _copyJsonOutput(from);
52945336
break;
52955337
case T_JsonArrayCtor:
52965338
retval = _copyJsonArrayCtor(from);
52975339
break;
5340+
case T_JsonArrayAgg:
5341+
retval = _copyJsonArrayAgg(from);
5342+
break;
52985343

52995344
/*
53005345
* RELATION NODES

src/backend/nodes/nodeFuncs.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3254,7 +3254,7 @@ expression_tree_mutator(Node *node,
32543254

32553255
FLATCOPY(newnode, jve, JsonCtorExpr);
32563256
MUTATE(newnode->args, jve->args, List *);
3257-
MUTATE(newnode->func, jve->func, FuncExpr *);
3257+
MUTATE(newnode->func, jve->func, Expr *);
32583258
MUTATE(newnode->returning, jve->returning, JsonReturning *);
32593259

32603260
return (Node *) newnode;
@@ -4020,6 +4020,38 @@ raw_expression_tree_walker(Node *node,
40204020
return true;
40214021
}
40224022
break;
4023+
case T_JsonObjectAgg:
4024+
{
4025+
JsonObjectAgg *joa = (JsonObjectAgg *) node;
4026+
4027+
if (walker(joa->ctor.output, context))
4028+
return true;
4029+
if (walker(joa->ctor.agg_order, context))
4030+
return true;
4031+
if (walker(joa->ctor.agg_filter, context))
4032+
return true;
4033+
if (walker(joa->ctor.over, context))
4034+
return true;
4035+
if (walker(joa->arg, context))
4036+
return true;
4037+
}
4038+
break;
4039+
case T_JsonArrayAgg:
4040+
{
4041+
JsonArrayAgg *jaa = (JsonArrayAgg *) node;
4042+
4043+
if (walker(jaa->ctor.output, context))
4044+
return true;
4045+
if (walker(jaa->ctor.agg_order, context))
4046+
return true;
4047+
if (walker(jaa->ctor.agg_filter, context))
4048+
return true;
4049+
if (walker(jaa->ctor.over, context))
4050+
return true;
4051+
if (walker(jaa->arg, context))
4052+
return true;
4053+
}
4054+
break;
40234055
default:
40244056
elog(ERROR, "unrecognized node type: %d",
40254057
(int) nodeTag(node));

src/backend/parser/parse_expr.c

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
#include "postgres.h"
1717

18+
#include "catalog/pg_aggregate.h"
19+
#include "catalog/pg_proc.h"
1820
#include "catalog/pg_type.h"
1921
#include "commands/dbcommands.h"
2022
#include "miscadmin.h"
@@ -123,6 +125,8 @@ static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
123125
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
124126
static Node *transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor);
125127
static Node *transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor);
128+
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
129+
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
126130
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
127131
List *largs, List *rargs, int location);
128132
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -379,6 +383,14 @@ transformExprRecurse(ParseState *pstate, Node *expr)
379383
result = transformJsonArrayCtor(pstate, (JsonArrayCtor *) expr);
380384
break;
381385

386+
case T_JsonObjectAgg:
387+
result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
388+
break;
389+
390+
case T_JsonArrayAgg:
391+
result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
392+
break;
393+
382394
default:
383395
/* should not reach here */
384396
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3991,7 +4003,6 @@ transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor)
39914003

39924004
jsctor = makeNode(JsonCtorExpr);
39934005
jsctor->args = args;
3994-
jsctor->func = NULL;
39954006
jsctor->type = JSCTOR_JSON_OBJECT;
39964007
jsctor->returning = transformJsonCtorOutput(pstate, ctor->output, args);
39974008
jsctor->unique = ctor->unique;
@@ -4001,6 +4012,171 @@ transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor)
40014012
return coerceJsonFuncExpr(pstate, (Node *) jsctor, jsctor->returning, true);
40024013
}
40034014

4015+
/*
4016+
* Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
4017+
*/
4018+
static Node *
4019+
transformJsonAggCtor(ParseState *pstate, JsonAggCtor *agg_ctor,
4020+
JsonReturning *returning, List *args, const char *aggfn,
4021+
Oid aggtype, JsonCtorType ctor_type,
4022+
bool unique, bool absent_on_null)
4023+
{
4024+
Oid aggfnoid;
4025+
JsonCtorExpr *jsctor;
4026+
Node *node;
4027+
Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
4028+
transformWhereClause(pstate, agg_ctor->agg_filter,
4029+
EXPR_KIND_FILTER, "FILTER") : NULL;
4030+
4031+
aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
4032+
CStringGetDatum(aggfn)));
4033+
4034+
if (agg_ctor->over)
4035+
{
4036+
/* window function */
4037+
WindowFunc *wfunc = makeNode(WindowFunc);
4038+
4039+
wfunc->winfnoid = aggfnoid;
4040+
wfunc->wintype = aggtype;
4041+
/* wincollid and inputcollid will be set by parse_collate.c */
4042+
wfunc->args = args;
4043+
/* winref will be set by transformWindowFuncCall */
4044+
wfunc->winstar = false;
4045+
wfunc->winagg = true;
4046+
wfunc->aggfilter = aggfilter;
4047+
wfunc->location = agg_ctor->location;
4048+
4049+
/*
4050+
* ordered aggs not allowed in windows yet
4051+
*/
4052+
if (agg_ctor->agg_order != NIL)
4053+
ereport(ERROR,
4054+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4055+
errmsg("aggregate ORDER BY is not implemented for window functions"),
4056+
parser_errposition(pstate, agg_ctor->location)));
4057+
4058+
/* parse_agg.c does additional window-func-specific processing */
4059+
transformWindowFuncCall(pstate, wfunc, agg_ctor->over);
4060+
4061+
node = (Node *) wfunc;
4062+
}
4063+
else
4064+
{
4065+
Aggref *aggref = makeNode(Aggref);
4066+
4067+
aggref->aggfnoid = aggfnoid;
4068+
aggref->aggtype = aggtype;
4069+
4070+
/* aggcollid and inputcollid will be set by parse_collate.c */
4071+
aggref->aggtranstype = InvalidOid; /* will be set by planner */
4072+
/* aggargtypes will be set by transformAggregateCall */
4073+
/* aggdirectargs and args will be set by transformAggregateCall */
4074+
/* aggorder and aggdistinct will be set by transformAggregateCall */
4075+
aggref->aggfilter = aggfilter;
4076+
aggref->aggstar = false;
4077+
aggref->aggvariadic = false;
4078+
aggref->aggkind = AGGKIND_NORMAL;
4079+
/* agglevelsup will be set by transformAggregateCall */
4080+
aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
4081+
aggref->location = agg_ctor->location;
4082+
4083+
transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
4084+
4085+
node = (Node *) aggref;
4086+
}
4087+
4088+
jsctor = makeNode(JsonCtorExpr);
4089+
jsctor->func = (Expr *) node;
4090+
jsctor->type = ctor_type;
4091+
jsctor->returning = *returning;
4092+
jsctor->unique = unique;
4093+
jsctor->absent_on_null = absent_on_null;
4094+
jsctor->location = agg_ctor->location;
4095+
4096+
return coerceJsonFuncExpr(pstate, (Node *) jsctor, returning, true);
4097+
}
4098+
4099+
/*
4100+
* Transform JSON_OBJECTAGG() aggregate function.
4101+
*
4102+
* JSON_OBJECTAGG() is transformed into
4103+
* json[b]_objectagg(key, value, absent_on_null, check_unique) call depending on
4104+
* the output JSON format. Then the function call result is coerced to the
4105+
* target output type.
4106+
*/
4107+
static Node *
4108+
transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
4109+
{
4110+
JsonReturning returning;
4111+
Node *key;
4112+
Node *val;
4113+
List *args;
4114+
const char *aggfnname;
4115+
Oid aggtype;
4116+
4117+
transformJsonOutput(pstate, agg->ctor.output, true, &returning);
4118+
4119+
key = transformExprRecurse(pstate, (Node *) agg->arg->key);
4120+
val = transformJsonValueExpr(pstate, agg->arg->value, JS_FORMAT_DEFAULT);
4121+
4122+
args = list_make4(key,
4123+
val,
4124+
makeBoolConst(agg->absent_on_null, false),
4125+
makeBoolConst(agg->unique, false));
4126+
4127+
if (returning.format.type == JS_FORMAT_JSONB)
4128+
{
4129+
aggfnname = "pg_catalog.jsonb_objectagg"; /* F_JSONB_OBJECTAGG */
4130+
aggtype = JSONBOID;
4131+
}
4132+
else
4133+
{
4134+
aggfnname = "pg_catalog.json_objectagg"; /* F_JSON_OBJECTAGG; */
4135+
aggtype = JSONOID;
4136+
}
4137+
4138+
return transformJsonAggCtor(pstate, &agg->ctor, &returning, args, aggfnname,
4139+
aggtype, JSCTOR_JSON_OBJECTAGG,
4140+
agg->unique, agg->absent_on_null);
4141+
}
4142+
4143+
/*
4144+
* Transform JSON_ARRAYAGG() aggregate function.
4145+
*
4146+
* JSON_ARRAYAGG() is transformed into json[b]_agg[_strict]() call depending
4147+
* on the output JSON format and absent_on_null. Then the function call result
4148+
* is coerced to the target output type.
4149+
*/
4150+
static Node *
4151+
transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
4152+
{
4153+
JsonReturning returning;
4154+
Node *arg;
4155+
const char *aggfnname;
4156+
Oid aggtype;
4157+
4158+
transformJsonOutput(pstate, agg->ctor.output, true, &returning);
4159+
4160+
arg = transformJsonValueExpr(pstate, agg->arg, JS_FORMAT_DEFAULT);
4161+
4162+
if (returning.format.type == JS_FORMAT_JSONB)
4163+
{
4164+
aggfnname = agg->absent_on_null ?
4165+
"pg_catalog.jsonb_agg_strict" : "pg_catalog.jsonb_agg";
4166+
aggtype = JSONBOID;
4167+
}
4168+
else
4169+
{
4170+
aggfnname = agg->absent_on_null ?
4171+
"pg_catalog.json_agg_strict" : "pg_catalog.json_agg";
4172+
aggtype = JSONOID;
4173+
}
4174+
4175+
return transformJsonAggCtor(pstate, &agg->ctor, &returning, list_make1(arg),
4176+
aggfnname, aggtype, JSCTOR_JSON_ARRAYAGG,
4177+
false, agg->absent_on_null);
4178+
}
4179+
40044180
/*
40054181
* Transform JSON_ARRAY() constructor.
40064182
*

src/backend/parser/parse_target.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,12 @@ FigureColnameInternal(Node *node, char **name)
19371937
case T_JsonArrayCtor:
19381938
*name = "json_array";
19391939
return 2;
1940+
case T_JsonObjectAgg:
1941+
*name = "json_objectagg";
1942+
return 2;
1943+
case T_JsonArrayAgg:
1944+
*name = "json_arrayagg";
1945+
return 2;
19401946
default:
19411947
break;
19421948
}

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