1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

SQL/JSON: add standard JSON constructor functions

This commit introduces the SQL/JSON standard-conforming constructors for
JSON types:

JSON_ARRAY()
JSON_ARRAYAGG()
JSON_OBJECT()
JSON_OBJECTAGG()

Most of the functionality was already present in PostgreSQL-specific
functions, but these include some new functionality such as the ability
to skip or include NULL values, and to allow duplicate keys or throw
error when they are found, as well as the standard specified syntax to
specify output type and format.

Author: Nikita Glukhov <n.gluhov@postgrespro.ru>
Author: Teodor Sigaev <teodor@sigaev.ru>
Author: Oleg Bartunov <obartunov@gmail.com>
Author: Alexander Korotkov <aekorotkov@gmail.com>
Author: Amit Langote <amitlangote09@gmail.com>

Reviewers have included (in no particular order) Andres Freund, Alexander
Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu,
Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby.

Discussion: https://postgr.es/m/CAF4Au4w2x-5LTnN_bxky-mq4=WOqsGsxSpENCzHRAzSnEd8+WQ@mail.gmail.com
Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru
Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de
Discussion: https://postgr.es/m/abd9b83b-aa66-f230-3d6d-734817f0995d%40postgresql.org
This commit is contained in:
Alvaro Herrera
2023-03-29 12:11:36 +02:00
parent 38b7437b90
commit 7081ac46ac
42 changed files with 4472 additions and 143 deletions

View File

@ -15,6 +15,8 @@
#include "postgres.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
@ -34,6 +36,7 @@
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/xml.h"
@ -72,6 +75,14 @@ static Node *transformWholeRowRef(ParseState *pstate,
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
static Node *transformJsonObjectConstructor(ParseState *pstate,
JsonObjectConstructor *ctor);
static Node *transformJsonArrayConstructor(ParseState *pstate,
JsonArrayConstructor *ctor);
static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
JsonArrayQueryConstructor *ctor);
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@ -294,6 +305,26 @@ transformExprRecurse(ParseState *pstate, Node *expr)
break;
}
case T_JsonObjectConstructor:
result = transformJsonObjectConstructor(pstate, (JsonObjectConstructor *) expr);
break;
case T_JsonArrayConstructor:
result = transformJsonArrayConstructor(pstate, (JsonArrayConstructor *) expr);
break;
case T_JsonArrayQueryConstructor:
result = transformJsonArrayQueryConstructor(pstate, (JsonArrayQueryConstructor *) expr);
break;
case T_JsonObjectAgg:
result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
break;
case T_JsonArrayAgg:
result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
break;
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@ -3047,3 +3078,742 @@ ParseExprKindName(ParseExprKind exprKind)
}
return "unrecognized expression kind";
}
/*
* Make string Const node from JSON encoding name.
*
* UTF8 is default encoding.
*/
static Const *
getJsonEncodingConst(JsonFormat *format)
{
JsonEncoding encoding;
const char *enc;
Name encname = palloc(sizeof(NameData));
if (!format ||
format->format_type == JS_FORMAT_DEFAULT ||
format->encoding == JS_ENC_DEFAULT)
encoding = JS_ENC_UTF8;
else
encoding = format->encoding;
switch (encoding)
{
case JS_ENC_UTF16:
enc = "UTF16";
break;
case JS_ENC_UTF32:
enc = "UTF32";
break;
case JS_ENC_UTF8:
enc = "UTF8";
break;
default:
elog(ERROR, "invalid JSON encoding: %d", encoding);
break;
}
namestrcpy(encname, enc);
return makeConst(NAMEOID, -1, InvalidOid, NAMEDATALEN,
NameGetDatum(encname), false, false);
}
/*
* Make bytea => text conversion using specified JSON format encoding.
*/
static Node *
makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
{
Const *encoding = getJsonEncodingConst(format);
FuncExpr *fexpr = makeFuncExpr(F_CONVERT_FROM, TEXTOID,
list_make2(expr, encoding),
InvalidOid, InvalidOid,
COERCE_EXPLICIT_CALL);
fexpr->location = location;
return (Node *) fexpr;
}
/*
* Make CaseTestExpr node.
*/
static Node *
makeCaseTestExpr(Node *expr)
{
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(expr);
placeholder->typeMod = exprTypmod(expr);
placeholder->collation = exprCollation(expr);
return (Node *) placeholder;
}
/*
* Transform JSON value expression using specified input JSON format or
* default format otherwise.
*/
static Node *
transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
JsonFormatType default_format)
{
Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
Node *rawexpr;
JsonFormatType format;
Oid exprtype;
int location;
char typcategory;
bool typispreferred;
/*
* Using JSON_VALUE here is slightly bogus: perhaps we need to be passed a
* JsonConstructorType so that we can use one of JSON_OBJECTAGG, etc.
*/
if (exprType(expr) == UNKNOWNOID)
expr = coerce_to_specific_type(pstate, expr, TEXTOID, "JSON_VALUE");
rawexpr = expr;
exprtype = exprType(expr);
location = exprLocation(expr);
get_type_category_preferred(exprtype, &typcategory, &typispreferred);
if (ve->format->format_type != JS_FORMAT_DEFAULT)
{
if (ve->format->encoding != JS_ENC_DEFAULT && exprtype != BYTEAOID)
ereport(ERROR,
errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("JSON ENCODING clause is only allowed for bytea input type"),
parser_errposition(pstate, ve->format->location));
if (exprtype == JSONOID || exprtype == JSONBOID)
{
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
ereport(WARNING,
errmsg("FORMAT JSON has no effect for json and jsonb types"),
parser_errposition(pstate, ve->format->location));
}
else
format = ve->format->format_type;
}
else if (exprtype == JSONOID || exprtype == JSONBOID)
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
else
format = default_format;
if (format != JS_FORMAT_DEFAULT)
{
Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
Node *orig = makeCaseTestExpr(expr);
Node *coerced;
expr = orig;
if (exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
ereport(ERROR,
errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg(ve->format->format_type == JS_FORMAT_DEFAULT ?
"cannot use non-string types with implicit FORMAT JSON clause" :
"cannot use non-string types with explicit FORMAT JSON clause"),
parser_errposition(pstate, ve->format->location >= 0 ?
ve->format->location : location));
/* Convert encoded JSON text from bytea. */
if (format == JS_FORMAT_JSON && exprtype == BYTEAOID)
{
expr = makeJsonByteaToTextConversion(expr, ve->format, location);
exprtype = TEXTOID;
}
/* Try to coerce to the target type. */
coerced = coerce_to_target_type(pstate, expr, exprtype,
targettype, -1,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
location);
if (!coerced)
{
/* If coercion failed, use to_json()/to_jsonb() functions. */
Oid fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
FuncExpr *fexpr = makeFuncExpr(fnoid, targettype,
list_make1(expr),
InvalidOid, InvalidOid,
COERCE_EXPLICIT_CALL);
fexpr->location = location;
coerced = (Node *) fexpr;
}
if (coerced == orig)
expr = rawexpr;
else
{
ve = copyObject(ve);
ve->raw_expr = (Expr *) rawexpr;
ve->formatted_expr = (Expr *) coerced;
expr = (Node *) ve;
}
}
return expr;
}
/*
* Checks specified output format for its applicability to the target type.
*/
static void
checkJsonOutputFormat(ParseState *pstate, const JsonFormat *format,
Oid targettype, bool allow_format_for_non_strings)
{
if (!allow_format_for_non_strings &&
format->format_type != JS_FORMAT_DEFAULT &&
(targettype != BYTEAOID &&
targettype != JSONOID &&
targettype != JSONBOID))
{
char typcategory;
bool typispreferred;
get_type_category_preferred(targettype, &typcategory, &typispreferred);
if (typcategory != TYPCATEGORY_STRING)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
parser_errposition(pstate, format->location),
errmsg("cannot use JSON format with non-string output types"));
}
if (format->format_type == JS_FORMAT_JSON)
{
JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
format->encoding : JS_ENC_UTF8;
if (targettype != BYTEAOID &&
format->encoding != JS_ENC_DEFAULT)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
parser_errposition(pstate, format->location),
errmsg("cannot set JSON encoding for non-bytea output types"));
if (enc != JS_ENC_UTF8)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported JSON encoding"),
errhint("Only UTF8 JSON encoding is supported."),
parser_errposition(pstate, format->location));
}
}
/*
* Transform JSON output clause.
*
* Assigns target type oid and modifier.
* Assigns default format or checks specified format for its applicability to
* the target type.
*/
static JsonReturning *
transformJsonOutput(ParseState *pstate, const JsonOutput *output,
bool allow_format)
{
JsonReturning *ret;
/* if output clause is not specified, make default clause value */
if (!output)
{
ret = makeNode(JsonReturning);
ret->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
ret->typid = InvalidOid;
ret->typmod = -1;
return ret;
}
ret = copyObject(output->returning);
typenameTypeIdAndMod(pstate, output->typeName, &ret->typid, &ret->typmod);
if (output->typeName->setof)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("returning SETOF types is not supported in SQL/JSON functions"));
if (ret->format->format_type == JS_FORMAT_DEFAULT)
/* assign JSONB format when returning jsonb, or JSON format otherwise */
ret->format->format_type =
ret->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
else
checkJsonOutputFormat(pstate, ret->format, ret->typid, allow_format);
return ret;
}
/*
* Transform JSON output clause of JSON constructor functions.
*
* Derive RETURNING type, if not specified, from argument types.
*/
static JsonReturning *
transformJsonConstructorOutput(ParseState *pstate, JsonOutput *output,
List *args)
{
JsonReturning *returning = transformJsonOutput(pstate, output, true);
if (!OidIsValid(returning->typid))
{
ListCell *lc;
bool have_jsonb = false;
foreach(lc, args)
{
Node *expr = lfirst(lc);
Oid typid = exprType(expr);
have_jsonb |= typid == JSONBOID;
if (have_jsonb)
break;
}
if (have_jsonb)
{
returning->typid = JSONBOID;
returning->format->format_type = JS_FORMAT_JSONB;
}
else
{
/* XXX TEXT is default by the standard, but we return JSON */
returning->typid = JSONOID;
returning->format->format_type = JS_FORMAT_JSON;
}
returning->typmod = -1;
}
return returning;
}
/*
* Coerce json[b]-valued function expression to the output type.
*/
static Node *
coerceJsonFuncExpr(ParseState *pstate, Node *expr,
const JsonReturning *returning, bool report_error)
{
Node *res;
int location;
Oid exprtype = exprType(expr);
/* if output type is not specified or equals to function type, return */
if (!OidIsValid(returning->typid) || returning->typid == exprtype)
return expr;
location = exprLocation(expr);
if (location < 0)
location = returning->format->location;
/* special case for RETURNING bytea FORMAT json */
if (returning->format->format_type == JS_FORMAT_JSON &&
returning->typid == BYTEAOID)
{
/* encode json text into bytea using pg_convert_to() */
Node *texpr = coerce_to_specific_type(pstate, expr, TEXTOID,
"JSON_FUNCTION");
Const *enc = getJsonEncodingConst(returning->format);
FuncExpr *fexpr = makeFuncExpr(F_CONVERT_TO, BYTEAOID,
list_make2(texpr, enc),
InvalidOid, InvalidOid,
COERCE_EXPLICIT_CALL);
fexpr->location = location;
return (Node *) fexpr;
}
/* try to coerce expression to the output type */
res = coerce_to_target_type(pstate, expr, exprtype,
returning->typid, returning->typmod,
/* XXX throwing errors when casting to char(N) */
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
location);
if (!res && report_error)
ereport(ERROR,
errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(exprtype),
format_type_be(returning->typid)),
parser_coercion_errposition(pstate, location, expr));
return res;
}
static Node *
makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
List *args, Expr *fexpr, JsonReturning *returning,
bool unique, bool absent_on_null, int location)
{
JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
Node *placeholder;
Node *coercion;
Oid intermediate_typid =
returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
jsctor->args = args;
jsctor->func = fexpr;
jsctor->type = type;
jsctor->returning = returning;
jsctor->unique = unique;
jsctor->absent_on_null = absent_on_null;
jsctor->location = location;
if (fexpr)
placeholder = makeCaseTestExpr((Node *) fexpr);
else
{
CaseTestExpr *cte = makeNode(CaseTestExpr);
cte->typeId = intermediate_typid;
cte->typeMod = -1;
cte->collation = InvalidOid;
placeholder = (Node *) cte;
}
coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true);
if (coercion != placeholder)
jsctor->coercion = (Expr *) coercion;
return (Node *) jsctor;
}
/*
* Transform JSON_OBJECT() constructor.
*
* JSON_OBJECT() is transformed into json[b]_build_object[_ext]() call
* depending on the output JSON format. The first two arguments of
* json[b]_build_object_ext() are absent_on_null and check_key_uniqueness.
*
* Then function call result is coerced to the target type.
*/
static Node *
transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
{
JsonReturning *returning;
List *args = NIL;
/* transform key-value pairs, if any */
if (ctor->exprs)
{
ListCell *lc;
/* transform and append key-value arguments */
foreach(lc, ctor->exprs)
{
JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc));
Node *key = transformExprRecurse(pstate, (Node *) kv->key);
Node *val = transformJsonValueExpr(pstate, kv->value,
JS_FORMAT_DEFAULT);
args = lappend(args, key);
args = lappend(args, val);
}
}
returning = transformJsonConstructorOutput(pstate, ctor->output, args);
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_OBJECT, args, NULL,
returning, ctor->unique,
ctor->absent_on_null, ctor->location);
}
/*
* Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
* (SELECT JSON_ARRAYAGG(a [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
*/
static Node *
transformJsonArrayQueryConstructor(ParseState *pstate,
JsonArrayQueryConstructor *ctor)
{
SubLink *sublink = makeNode(SubLink);
SelectStmt *select = makeNode(SelectStmt);
RangeSubselect *range = makeNode(RangeSubselect);
Alias *alias = makeNode(Alias);
ResTarget *target = makeNode(ResTarget);
JsonArrayAgg *agg = makeNode(JsonArrayAgg);
ColumnRef *colref = makeNode(ColumnRef);
Query *query;
ParseState *qpstate;
/* Transform query only for counting target list entries. */
qpstate = make_parsestate(pstate);
query = transformStmt(qpstate, ctor->query);
if (count_nonjunk_tlist_entries(query->targetList) != 1)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return only one column"),
parser_errposition(pstate, ctor->location));
free_parsestate(qpstate);
colref->fields = list_make2(makeString(pstrdup("q")),
makeString(pstrdup("a")));
colref->location = ctor->location;
agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
agg->absent_on_null = ctor->absent_on_null;
agg->constructor = makeNode(JsonAggConstructor);
agg->constructor->agg_order = NIL;
agg->constructor->output = ctor->output;
agg->constructor->location = ctor->location;
target->name = NULL;
target->indirection = NIL;
target->val = (Node *) agg;
target->location = ctor->location;
alias->aliasname = pstrdup("q");
alias->colnames = list_make1(makeString(pstrdup("a")));
range->lateral = false;
range->subquery = ctor->query;
range->alias = alias;
select->targetList = list_make1(target);
select->fromClause = list_make1(range);
sublink->subLinkType = EXPR_SUBLINK;
sublink->subLinkId = 0;
sublink->testexpr = NULL;
sublink->operName = NIL;
sublink->subselect = (Node *) select;
sublink->location = ctor->location;
return transformExprRecurse(pstate, (Node *) sublink);
}
/*
* Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
*/
static Node *
transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
JsonReturning *returning, List *args,
const char *aggfn, Oid aggtype,
JsonConstructorType ctor_type,
bool unique, bool absent_on_null)
{
Oid aggfnoid;
Node *node;
Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
transformWhereClause(pstate, agg_ctor->agg_filter,
EXPR_KIND_FILTER, "FILTER") : NULL;
aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
CStringGetDatum(aggfn)));
if (agg_ctor->over)
{
/* window function */
WindowFunc *wfunc = makeNode(WindowFunc);
wfunc->winfnoid = aggfnoid;
wfunc->wintype = aggtype;
/* wincollid and inputcollid will be set by parse_collate.c */
wfunc->args = args;
/* winref will be set by transformWindowFuncCall */
wfunc->winstar = false;
wfunc->winagg = true;
wfunc->aggfilter = aggfilter;
wfunc->location = agg_ctor->location;
/*
* ordered aggs not allowed in windows yet
*/
if (agg_ctor->agg_order != NIL)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aggregate ORDER BY is not implemented for window functions"),
parser_errposition(pstate, agg_ctor->location));
/* parse_agg.c does additional window-func-specific processing */
transformWindowFuncCall(pstate, wfunc, agg_ctor->over);
node = (Node *) wfunc;
}
else
{
Aggref *aggref = makeNode(Aggref);
aggref->aggfnoid = aggfnoid;
aggref->aggtype = aggtype;
/* aggcollid and inputcollid will be set by parse_collate.c */
aggref->aggtranstype = InvalidOid; /* will be set by planner */
/* aggargtypes will be set by transformAggregateCall */
/* aggdirectargs and args will be set by transformAggregateCall */
/* aggorder and aggdistinct will be set by transformAggregateCall */
aggref->aggfilter = aggfilter;
aggref->aggstar = false;
aggref->aggvariadic = false;
aggref->aggkind = AGGKIND_NORMAL;
/* agglevelsup will be set by transformAggregateCall */
aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
aggref->location = agg_ctor->location;
transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
node = (Node *) aggref;
}
return makeJsonConstructorExpr(pstate, ctor_type, NIL, (Expr *) node,
returning, unique, absent_on_null,
agg_ctor->location);
}
/*
* Transform JSON_OBJECTAGG() aggregate function.
*
* JSON_OBJECTAGG() is transformed into
* json[b]_objectagg(key, value, absent_on_null, check_unique) call depending on
* the output JSON format. Then the function call result is coerced to the
* target output type.
*/
static Node *
transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
{
JsonReturning *returning;
Node *key;
Node *val;
List *args;
const char *aggfnname;
Oid aggtype;
key = transformExprRecurse(pstate, (Node *) agg->arg->key);
val = transformJsonValueExpr(pstate, agg->arg->value, JS_FORMAT_DEFAULT);
args = list_make2(key, val);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
args);
if (returning->format->format_type == JS_FORMAT_JSONB)
{
if (agg->absent_on_null)
if (agg->unique)
aggfnname = "pg_catalog.jsonb_object_agg_unique_strict";
else
aggfnname = "pg_catalog.jsonb_object_agg_strict";
else if (agg->unique)
aggfnname = "pg_catalog.jsonb_object_agg_unique";
else
aggfnname = "pg_catalog.jsonb_object_agg";
aggtype = JSONBOID;
}
else
{
if (agg->absent_on_null)
if (agg->unique)
aggfnname = "pg_catalog.json_object_agg_unique_strict";
else
aggfnname = "pg_catalog.json_object_agg_strict";
else if (agg->unique)
aggfnname = "pg_catalog.json_object_agg_unique";
else
aggfnname = "pg_catalog.json_object_agg";
aggtype = JSONOID;
}
return transformJsonAggConstructor(pstate, agg->constructor, returning,
args, aggfnname, aggtype,
JSCTOR_JSON_OBJECTAGG,
agg->unique, agg->absent_on_null);
}
/*
* Transform JSON_ARRAYAGG() aggregate function.
*
* JSON_ARRAYAGG() is transformed into json[b]_agg[_strict]() call depending
* on the output JSON format and absent_on_null. Then the function call result
* is coerced to the target output type.
*/
static Node *
transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
{
JsonReturning *returning;
Node *arg;
const char *aggfnname;
Oid aggtype;
arg = transformJsonValueExpr(pstate, agg->arg, JS_FORMAT_DEFAULT);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
list_make1(arg));
if (returning->format->format_type == JS_FORMAT_JSONB)
{
aggfnname = agg->absent_on_null ?
"pg_catalog.jsonb_agg_strict" : "pg_catalog.jsonb_agg";
aggtype = JSONBOID;
}
else
{
aggfnname = agg->absent_on_null ?
"pg_catalog.json_agg_strict" : "pg_catalog.json_agg";
aggtype = JSONOID;
}
return transformJsonAggConstructor(pstate, agg->constructor, returning,
list_make1(arg), aggfnname, aggtype,
JSCTOR_JSON_ARRAYAGG,
false, agg->absent_on_null);
}
/*
* Transform JSON_ARRAY() constructor.
*
* JSON_ARRAY() is transformed into json[b]_build_array[_ext]() call
* depending on the output JSON format. The first argument of
* json[b]_build_array_ext() is absent_on_null.
*
* Then function call result is coerced to the target type.
*/
static Node *
transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
{
JsonReturning *returning;
List *args = NIL;
/* transform element expressions, if any */
if (ctor->exprs)
{
ListCell *lc;
/* transform and append element arguments */
foreach(lc, ctor->exprs)
{
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
Node *val = transformJsonValueExpr(pstate, jsval,
JS_FORMAT_DEFAULT);
args = lappend(args, val);
}
}
returning = transformJsonConstructorOutput(pstate, ctor->output, args);
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_ARRAY, args, NULL,
returning, false, ctor->absent_on_null,
ctor->location);
}