Skip to content

Commit 40fa70f

Browse files
author
Nikita Glukhov
committed
Add JSON_ARRAY() transformation
1 parent c9e0662 commit 40fa70f

File tree

10 files changed

+334
-12
lines changed

10 files changed

+334
-12
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,22 @@ _copyJsonOutput(const JsonOutput *from)
22702270
return newnode;
22712271
}
22722272

2273+
/*
2274+
* _copyJsonArrayCtor
2275+
*/
2276+
static JsonArrayCtor *
2277+
_copyJsonArrayCtor(const JsonArrayCtor *from)
2278+
{
2279+
JsonArrayCtor *newnode = makeNode(JsonArrayCtor);
2280+
2281+
COPY_NODE_FIELD(exprs);
2282+
COPY_NODE_FIELD(output);
2283+
COPY_SCALAR_FIELD(absent_on_null);
2284+
COPY_LOCATION_FIELD(location);
2285+
2286+
return newnode;
2287+
}
2288+
22732289
/* ****************************************************************
22742290
* relation.h copy functions
22752291
*
@@ -5172,6 +5188,9 @@ copyObjectImpl(const void *from)
51725188
case T_JsonOutput:
51735189
retval = _copyJsonOutput(from);
51745190
break;
5191+
case T_JsonArrayCtor:
5192+
retval = _copyJsonArrayCtor(from);
5193+
break;
51755194

51765195
/*
51775196
* RELATION NODES

src/backend/nodes/nodeFuncs.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3755,6 +3755,16 @@ raw_expression_tree_walker(Node *node,
37553755
return true;
37563756
}
37573757
break;
3758+
case T_JsonArrayCtor:
3759+
{
3760+
JsonArrayCtor *jac = (JsonArrayCtor *) node;
3761+
3762+
if (walker(jac->output, context))
3763+
return true;
3764+
if (walker(jac->exprs, context))
3765+
return true;
3766+
}
3767+
break;
37583768
default:
37593769
elog(ERROR, "unrecognized node type: %d",
37603770
(int) nodeTag(node));

src/backend/parser/parse_expr.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
123123
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
124124
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
125125
static Node *transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor);
126+
static Node *transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor);
126127
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
127128
List *largs, List *rargs, int location);
128129
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -375,6 +376,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
375376
result = transformJsonObjectCtor(pstate, (JsonObjectCtor *) expr);
376377
break;
377378

379+
case T_JsonArrayCtor:
380+
result = transformJsonArrayCtor(pstate, (JsonArrayCtor *) expr);
381+
break;
382+
378383
default:
379384
/* should not reach here */
380385
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3864,3 +3869,63 @@ transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor)
38643869

38653870
return coerceJsonFuncExpr(pstate, (Node *) fexpr, &returning, true);
38663871
}
3872+
3873+
/*
3874+
* Transform JSON_ARRAY() constructor.
3875+
*
3876+
* JSON_ARRAY() is transformed into json[b]_build_array[_ext]() call
3877+
* depending on the output JSON format. The first argument of
3878+
* json[b]_build_array_ext() is absent_on_null.
3879+
*
3880+
* Then function call result is coerced to the target type.
3881+
*/
3882+
static Node *
3883+
transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor)
3884+
{
3885+
JsonReturning returning;
3886+
FuncExpr *fexpr;
3887+
List *args = NIL;
3888+
Oid funcid;
3889+
Oid funcrettype;
3890+
3891+
/* transform element expressions, if any */
3892+
if (ctor->exprs)
3893+
{
3894+
ListCell *lc;
3895+
3896+
/* append the first absent_on_null argument */
3897+
args = lappend(args, makeBoolConst(ctor->absent_on_null, false));
3898+
3899+
/* transform and append element arguments */
3900+
foreach(lc, ctor->exprs)
3901+
{
3902+
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
3903+
Node *val = transformJsonValueExpr(pstate, jsval,
3904+
JS_FORMAT_DEFAULT);
3905+
3906+
args = lappend(args, val);
3907+
}
3908+
}
3909+
3910+
transformJsonOutput(pstate, ctor->output, true, &returning);
3911+
3912+
if (returning.format.type == JS_FORMAT_JSONB)
3913+
{
3914+
funcid = args ? F_JSONB_BUILD_ARRAY_EXT : F_JSONB_BUILD_ARRAY_NOARGS;
3915+
funcrettype = JSONBOID;
3916+
}
3917+
else
3918+
{
3919+
funcid = args ? F_JSON_BUILD_ARRAY_EXT : F_JSON_BUILD_ARRAY_NOARGS;
3920+
funcrettype = JSONOID;
3921+
}
3922+
3923+
fexpr = makeFuncExpr(funcid, funcrettype, args,
3924+
InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
3925+
fexpr->location = ctor->location;
3926+
fexpr->funcformat2 = FUNCFMT_JSON_ARRAY;
3927+
fexpr->funcformatopts = (Node *) makeJsonCtorOpts(&returning, false,
3928+
ctor->absent_on_null);
3929+
3930+
return coerceJsonFuncExpr(pstate, (Node *) fexpr, &returning, true);
3931+
}

src/backend/parser/parse_target.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,9 @@ FigureColnameInternal(Node *node, char **name)
19211921
case T_JsonObjectCtor:
19221922
*name = "json_object";
19231923
return 2;
1924+
case T_JsonArrayCtor:
1925+
*name = "json_array";
1926+
return 2;
19241927
default:
19251928
break;
19261929
}

src/backend/utils/adt/json.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,11 +2423,9 @@ json_build_object_noargs(PG_FUNCTION_ARGS)
24232423
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
24242424
}
24252425

2426-
/*
2427-
* SQL function json_build_array(variadic "any")
2428-
*/
2429-
Datum
2430-
json_build_array(PG_FUNCTION_ARGS)
2426+
static Datum
2427+
json_build_array_worker(FunctionCallInfo fcinfo, int first_vararg,
2428+
bool absent_on_null)
24312429
{
24322430
int nargs;
24332431
int i;
@@ -2438,7 +2436,8 @@ json_build_array(PG_FUNCTION_ARGS)
24382436
Oid *types;
24392437

24402438
/* fetch argument values to build the array */
2441-
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
2439+
nargs = extract_variadic_args(fcinfo, first_vararg, false,
2440+
&args, &types, &nulls);
24422441

24432442
if (nargs < 0)
24442443
PG_RETURN_NULL();
@@ -2449,6 +2448,9 @@ json_build_array(PG_FUNCTION_ARGS)
24492448

24502449
for (i = 0; i < nargs; i++)
24512450
{
2451+
if (absent_on_null && nulls[i])
2452+
continue;
2453+
24522454
appendStringInfoString(result, sep);
24532455
sep = ", ";
24542456
add_json(args[i], nulls[i], result, types[i], false);
@@ -2459,6 +2461,24 @@ json_build_array(PG_FUNCTION_ARGS)
24592461
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
24602462
}
24612463

2464+
/*
2465+
* SQL function json_build_array(variadic "any")
2466+
*/
2467+
Datum
2468+
json_build_array(PG_FUNCTION_ARGS)
2469+
{
2470+
return json_build_array_worker(fcinfo, 0, false);
2471+
}
2472+
2473+
/*
2474+
* SQL function json_build_array_ext(absent_on_null bool, variadic "any")
2475+
*/
2476+
Datum
2477+
json_build_array_ext(PG_FUNCTION_ARGS)
2478+
{
2479+
return json_build_array_worker(fcinfo, 1, PG_GETARG_BOOL(0));
2480+
}
2481+
24622482
/*
24632483
* degenerate case of json_build_array where it gets 0 arguments.
24642484
*/

src/backend/utils/adt/jsonb.c

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,11 +1350,9 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
13501350
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
13511351
}
13521352

1353-
/*
1354-
* SQL function jsonb_build_array(variadic "any")
1355-
*/
1356-
Datum
1357-
jsonb_build_array(PG_FUNCTION_ARGS)
1353+
static Datum
1354+
jsonb_build_array_worker(FunctionCallInfo fcinfo, int first_vararg,
1355+
bool absent_on_null)
13581356
{
13591357
int nargs;
13601358
int i;
@@ -1364,7 +1362,8 @@ jsonb_build_array(PG_FUNCTION_ARGS)
13641362
Oid *types;
13651363

13661364
/* build argument values to build the array */
1367-
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
1365+
nargs = extract_variadic_args(fcinfo, first_vararg, true,
1366+
&args, &types, &nulls);
13681367

13691368
if (nargs < 0)
13701369
PG_RETURN_NULL();
@@ -1374,13 +1373,36 @@ jsonb_build_array(PG_FUNCTION_ARGS)
13741373
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
13751374

13761375
for (i = 0; i < nargs; i++)
1376+
{
1377+
if (absent_on_null && nulls[i])
1378+
continue;
1379+
13771380
add_jsonb(args[i], nulls[i], &result, types[i], false);
1381+
}
13781382

13791383
result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
13801384

13811385
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
13821386
}
13831387

1388+
/*
1389+
* SQL function jsonb_build_array(variadic "any")
1390+
*/
1391+
Datum
1392+
jsonb_build_array(PG_FUNCTION_ARGS)
1393+
{
1394+
return jsonb_build_array_worker(fcinfo, 0, false);
1395+
}
1396+
1397+
/*
1398+
* SQL function jsonb_build_array_ext(absent_on_null bool, variadic "any")
1399+
*/
1400+
Datum
1401+
jsonb_build_array_ext(PG_FUNCTION_ARGS)
1402+
{
1403+
return jsonb_build_array_worker(fcinfo, 1, PG_GETARG_BOOL(0));
1404+
}
1405+
13841406
/*
13851407
* degenerate case of jsonb_build_array where it gets 0 arguments.
13861408
*/

src/backend/utils/adt/ruleutils.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9141,6 +9141,13 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
91419141
firstarg = 2;
91429142
use_variadic = false;
91439143
break;
9144+
9145+
case FUNCFMT_JSON_ARRAY:
9146+
funcname = "JSON_ARRAY";
9147+
firstarg = 1;
9148+
use_variadic = false;
9149+
break;
9150+
91449151
default:
91459152
funcname = generate_function_name(funcoid, nargs,
91469153
argnames, argtypes,

src/include/catalog/pg_proc.dat

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8252,6 +8252,11 @@
82528252
proname => 'json_build_array', proisstrict => 'f', provolatile => 's',
82538253
prorettype => 'json', proargtypes => '',
82548254
prosrc => 'json_build_array_noargs' },
8255+
{ oid => '3998', descr => 'build a json array from any inputs',
8256+
proname => 'json_build_array_ext', provariadic => 'any', proisstrict => 'f',
8257+
provolatile => 's', prorettype => 'json', proargtypes => 'bool any',
8258+
proallargtypes => '{bool,any}', proargmodes => '{i,v}',
8259+
prosrc => 'json_build_array_ext' },
82558260
{ oid => '3200',
82568261
descr => 'build a json object from pairwise key/value inputs',
82578262
proname => 'json_build_object', provariadic => 'any', proisstrict => 'f',
@@ -9111,6 +9116,11 @@
91119116
proname => 'jsonb_build_array', proisstrict => 'f', provolatile => 's',
91129117
prorettype => 'jsonb', proargtypes => '',
91139118
prosrc => 'jsonb_build_array_noargs' },
9119+
{ oid => '6068', descr => 'build a jsonb array from any inputs',
9120+
proname => 'jsonb_build_array_ext', provariadic => 'any', proisstrict => 'f',
9121+
provolatile => 's', prorettype => 'jsonb', proargtypes => 'bool any',
9122+
proallargtypes => '{bool,any}', proargmodes => '{i,v}',
9123+
prosrc => 'jsonb_build_array_ext' },
91149124
{ oid => '3273',
91159125
descr => 'build a jsonb object from pairwise key/value inputs',
91169126
proname => 'jsonb_build_object', provariadic => 'any', proisstrict => 'f',

0 commit comments

Comments
 (0)