Skip to content

Commit 98efa5e

Browse files
committed
Process variadic arguments consistently in json functions
json_build_object and json_build_array and the jsonb equivalents did not correctly process explicit VARIADIC arguments. They are modified to use the new extract_variadic_args() utility function which abstracts away the details of the call method. Michael Paquier, reviewed by Tom Lane and Dmitry Dolgov. Backpatch to 9.5 for the jsonb fixes and 9.4 for the json fixes, as that's where they originated.
1 parent 5c3a1bb commit 98efa5e

File tree

6 files changed

+305
-131
lines changed

6 files changed

+305
-131
lines changed

src/backend/utils/adt/json.c

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "access/transam.h"
1818
#include "catalog/pg_type.h"
1919
#include "executor/spi.h"
20+
#include "funcapi.h"
2021
#include "lib/stringinfo.h"
2122
#include "libpq/pqformat.h"
2223
#include "mb/pg_wchar.h"
@@ -2106,10 +2107,17 @@ json_build_object(PG_FUNCTION_ARGS)
21062107
{
21072108
int nargs = PG_NARGS();
21082109
int i;
2109-
Datum arg;
21102110
const char *sep = "";
21112111
StringInfo result;
2112-
Oid val_type;
2112+
Datum *args;
2113+
bool *nulls;
2114+
Oid *types;
2115+
2116+
/* fetch argument values to build the object */
2117+
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
2118+
2119+
if (nargs < 0)
2120+
PG_RETURN_NULL();
21132121

21142122
if (nargs % 2 != 0)
21152123
ereport(ERROR,
@@ -2123,52 +2131,22 @@ json_build_object(PG_FUNCTION_ARGS)
21232131

21242132
for (i = 0; i < nargs; i += 2)
21252133
{
2126-
/*
2127-
* Note: since json_build_object() is declared as taking type "any",
2128-
* the parser will not do any type conversion on unknown-type literals
2129-
* (that is, undecorated strings or NULLs). Such values will arrive
2130-
* here as type UNKNOWN, which fortunately does not matter to us,
2131-
* since unknownout() works fine.
2132-
*/
21332134
appendStringInfoString(result, sep);
21342135
sep = ", ";
21352136

21362137
/* process key */
2137-
val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
2138-
2139-
if (val_type == InvalidOid)
2140-
ereport(ERROR,
2141-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2142-
errmsg("could not determine data type for argument %d",
2143-
i + 1)));
2144-
2145-
if (PG_ARGISNULL(i))
2138+
if (nulls[i])
21462139
ereport(ERROR,
21472140
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21482141
errmsg("argument %d cannot be null", i + 1),
21492142
errhint("Object keys should be text.")));
21502143

2151-
arg = PG_GETARG_DATUM(i);
2152-
2153-
add_json(arg, false, result, val_type, true);
2144+
add_json(args[i], false, result, types[i], true);
21542145

21552146
appendStringInfoString(result, " : ");
21562147

21572148
/* process value */
2158-
val_type = get_fn_expr_argtype(fcinfo->flinfo, i + 1);
2159-
2160-
if (val_type == InvalidOid)
2161-
ereport(ERROR,
2162-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2163-
errmsg("could not determine data type for argument %d",
2164-
i + 2)));
2165-
2166-
if (PG_ARGISNULL(i + 1))
2167-
arg = (Datum) 0;
2168-
else
2169-
arg = PG_GETARG_DATUM(i + 1);
2170-
2171-
add_json(arg, PG_ARGISNULL(i + 1), result, val_type, false);
2149+
add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
21722150
}
21732151

21742152
appendStringInfoChar(result, '}');
@@ -2191,43 +2169,29 @@ json_build_object_noargs(PG_FUNCTION_ARGS)
21912169
Datum
21922170
json_build_array(PG_FUNCTION_ARGS)
21932171
{
2194-
int nargs = PG_NARGS();
2172+
int nargs;
21952173
int i;
2196-
Datum arg;
21972174
const char *sep = "";
21982175
StringInfo result;
2199-
Oid val_type;
2176+
Datum *args;
2177+
bool *nulls;
2178+
Oid *types;
2179+
2180+
/* fetch argument values to build the array */
2181+
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
2182+
2183+
if (nargs < 0)
2184+
PG_RETURN_NULL();
22002185

22012186
result = makeStringInfo();
22022187

22032188
appendStringInfoChar(result, '[');
22042189

22052190
for (i = 0; i < nargs; i++)
22062191
{
2207-
/*
2208-
* Note: since json_build_array() is declared as taking type "any",
2209-
* the parser will not do any type conversion on unknown-type literals
2210-
* (that is, undecorated strings or NULLs). Such values will arrive
2211-
* here as type UNKNOWN, which fortunately does not matter to us,
2212-
* since unknownout() works fine.
2213-
*/
22142192
appendStringInfoString(result, sep);
22152193
sep = ", ";
2216-
2217-
val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
2218-
2219-
if (val_type == InvalidOid)
2220-
ereport(ERROR,
2221-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2222-
errmsg("could not determine data type for argument %d",
2223-
i + 1)));
2224-
2225-
if (PG_ARGISNULL(i))
2226-
arg = (Datum) 0;
2227-
else
2228-
arg = PG_GETARG_DATUM(i);
2229-
2230-
add_json(arg, PG_ARGISNULL(i), result, val_type, false);
2194+
add_json(args[i], nulls[i], result, types[i], false);
22312195
}
22322196

22332197
appendStringInfoChar(result, ']');

src/backend/utils/adt/jsonb.c

Lines changed: 27 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "access/htup_details.h"
1717
#include "access/transam.h"
1818
#include "catalog/pg_type.h"
19+
#include "funcapi.h"
1920
#include "libpq/pqformat.h"
2021
#include "parser/parse_coerce.h"
2122
#include "utils/builtins.h"
@@ -1170,16 +1171,24 @@ to_jsonb(PG_FUNCTION_ARGS)
11701171
Datum
11711172
jsonb_build_object(PG_FUNCTION_ARGS)
11721173
{
1173-
int nargs = PG_NARGS();
1174+
int nargs;
11741175
int i;
1175-
Datum arg;
1176-
Oid val_type;
11771176
JsonbInState result;
1177+
Datum *args;
1178+
bool *nulls;
1179+
Oid *types;
1180+
1181+
/* build argument values to build the object */
1182+
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
1183+
1184+
if (nargs < 0)
1185+
PG_RETURN_NULL();
11781186

11791187
if (nargs % 2 != 0)
11801188
ereport(ERROR,
11811189
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1182-
errmsg("invalid number of arguments: object must be matched key value pairs")));
1190+
errmsg("argument list must have even number of elements"),
1191+
errhint("The arguments of jsonb_build_object() must consist of alternating keys and values.")));
11831192

11841193
memset(&result, 0, sizeof(JsonbInState));
11851194

@@ -1188,54 +1197,15 @@ jsonb_build_object(PG_FUNCTION_ARGS)
11881197
for (i = 0; i < nargs; i += 2)
11891198
{
11901199
/* process key */
1191-
1192-
if (PG_ARGISNULL(i))
1200+
if (nulls[i])
11931201
ereport(ERROR,
11941202
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
11951203
errmsg("argument %d: key must not be null", i + 1)));
1196-
val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
1197-
1198-
/*
1199-
* turn a constant (more or less literal) value that's of unknown type
1200-
* into text. Unknowns come in as a cstring pointer.
1201-
*/
1202-
if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i))
1203-
{
1204-
val_type = TEXTOID;
1205-
arg = CStringGetTextDatum(PG_GETARG_POINTER(i));
1206-
}
1207-
else
1208-
{
1209-
arg = PG_GETARG_DATUM(i);
1210-
}
1211-
if (val_type == InvalidOid || val_type == UNKNOWNOID)
1212-
ereport(ERROR,
1213-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1214-
errmsg("could not determine data type for argument %d", i + 1)));
12151204

1216-
add_jsonb(arg, false, &result, val_type, true);
1205+
add_jsonb(args[i], false, &result, types[i], true);
12171206

12181207
/* process value */
1219-
1220-
val_type = get_fn_expr_argtype(fcinfo->flinfo, i + 1);
1221-
/* see comments above */
1222-
if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i + 1))
1223-
{
1224-
val_type = TEXTOID;
1225-
if (PG_ARGISNULL(i + 1))
1226-
arg = (Datum) 0;
1227-
else
1228-
arg = CStringGetTextDatum(PG_GETARG_POINTER(i + 1));
1229-
}
1230-
else
1231-
{
1232-
arg = PG_GETARG_DATUM(i + 1);
1233-
}
1234-
if (val_type == InvalidOid || val_type == UNKNOWNOID)
1235-
ereport(ERROR,
1236-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1237-
errmsg("could not determine data type for argument %d", i + 2)));
1238-
add_jsonb(arg, PG_ARGISNULL(i + 1), &result, val_type, false);
1208+
add_jsonb(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
12391209
}
12401210

12411211
result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
@@ -1265,38 +1235,25 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
12651235
Datum
12661236
jsonb_build_array(PG_FUNCTION_ARGS)
12671237
{
1268-
int nargs = PG_NARGS();
1238+
int nargs;
12691239
int i;
1270-
Datum arg;
1271-
Oid val_type;
12721240
JsonbInState result;
1241+
Datum *args;
1242+
bool *nulls;
1243+
Oid *types;
1244+
1245+
/* build argument values to build the array */
1246+
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
1247+
1248+
if (nargs < 0)
1249+
PG_RETURN_NULL();
12731250

12741251
memset(&result, 0, sizeof(JsonbInState));
12751252

12761253
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
12771254

12781255
for (i = 0; i < nargs; i++)
1279-
{
1280-
val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
1281-
/* see comments in jsonb_build_object above */
1282-
if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i))
1283-
{
1284-
val_type = TEXTOID;
1285-
if (PG_ARGISNULL(i))
1286-
arg = (Datum) 0;
1287-
else
1288-
arg = CStringGetTextDatum(PG_GETARG_POINTER(i));
1289-
}
1290-
else
1291-
{
1292-
arg = PG_GETARG_DATUM(i);
1293-
}
1294-
if (val_type == InvalidOid || val_type == UNKNOWNOID)
1295-
ereport(ERROR,
1296-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1297-
errmsg("could not determine data type for argument %d", i + 1)));
1298-
add_jsonb(arg, PG_ARGISNULL(i), &result, val_type, false);
1299-
}
1256+
add_jsonb(args[i], nulls[i], &result, types[i], false);
13001257

13011258
result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
13021259

0 commit comments

Comments
 (0)