Skip to content

Commit 7f89fc4

Browse files
committed
Add a utility function to extract variadic function arguments
This is epecially useful in the case or "VARIADIC ANY" functions. The caller can get the artguments and types regardless of whether or not and explicit VARIADIC array argument has been used. The function also provides an option to convert arguments on type "unknown" to to "text". Michael Paquier and me, reviewed by Tom Lane. Backpatch to 9.4 in order to support the following json bug fix.
1 parent ba67fac commit 7f89fc4

File tree

2 files changed

+137
-1
lines changed

2 files changed

+137
-1
lines changed

src/backend/utils/fmgr/funcapi.c

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* funcapi.c
44
* Utility and convenience functions for fmgr functions that return
5-
* sets and/or composite types.
5+
* sets and/or composite types, or deal with VARIADIC inputs.
66
*
77
* Copyright (c) 2002-2014, PostgreSQL Global Development Group
88
*
@@ -1356,3 +1356,116 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
13561356

13571357
return tupdesc;
13581358
}
1359+
1360+
/*
1361+
* extract_variadic_args
1362+
*
1363+
* Extract a set of argument values, types and NULL markers for a given
1364+
* input function which makes use of a VARIADIC input whose argument list
1365+
* depends on the caller context. When doing a VARIADIC call, the caller
1366+
* has provided one argument made of an array of values, so deconstruct the
1367+
* array data before using it for the next processing. If no VARIADIC call
1368+
* is used, just fill in the status data based on all the arguments given
1369+
* by the caller.
1370+
*
1371+
* This function returns the number of arguments generated, or -1 in the
1372+
* case of "VARIADIC NULL".
1373+
*/
1374+
int
1375+
extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
1376+
bool convert_unknown, Datum **args, Oid **types,
1377+
bool **nulls)
1378+
{
1379+
bool variadic = get_fn_expr_variadic(fcinfo->flinfo);
1380+
Datum *args_res;
1381+
bool *nulls_res;
1382+
Oid *types_res;
1383+
int nargs, i;
1384+
1385+
*args = NULL;
1386+
*types = NULL;
1387+
*nulls = NULL;
1388+
1389+
if (variadic)
1390+
{
1391+
ArrayType *array_in;
1392+
Oid element_type;
1393+
bool typbyval;
1394+
char typalign;
1395+
int16 typlen;
1396+
1397+
Assert(PG_NARGS() == variadic_start + 1);
1398+
1399+
if (PG_ARGISNULL(variadic_start))
1400+
return -1;
1401+
1402+
array_in = PG_GETARG_ARRAYTYPE_P(variadic_start);
1403+
element_type = ARR_ELEMTYPE(array_in);
1404+
1405+
get_typlenbyvalalign(element_type,
1406+
&typlen, &typbyval, &typalign);
1407+
deconstruct_array(array_in, element_type, typlen, typbyval,
1408+
typalign, &args_res, &nulls_res,
1409+
&nargs);
1410+
1411+
/* All the elements of the array have the same type */
1412+
types_res = (Oid *) palloc0(nargs * sizeof(Oid));
1413+
for (i = 0; i < nargs; i++)
1414+
types_res[i] = element_type;
1415+
}
1416+
else
1417+
{
1418+
nargs = PG_NARGS() - variadic_start;
1419+
Assert (nargs > 0);
1420+
nulls_res = (bool *) palloc0(nargs * sizeof(bool));
1421+
args_res = (Datum *) palloc0(nargs * sizeof(Datum));
1422+
types_res = (Oid *) palloc0(nargs * sizeof(Oid));
1423+
1424+
for (i = 0; i < nargs; i++)
1425+
{
1426+
nulls_res[i] = PG_ARGISNULL(i + variadic_start);
1427+
types_res[i] = get_fn_expr_argtype(fcinfo->flinfo,
1428+
i + variadic_start);
1429+
1430+
/*
1431+
* Turn a constant (more or less literal) value that's of unknown
1432+
* type into text if required . Unknowns come in as a cstring
1433+
* pointer.
1434+
* Note: for functions declared as taking type "any", the parser
1435+
* will not do any type conversion on unknown-type literals (that
1436+
* is, undecorated strings or NULLs).
1437+
*/
1438+
if (convert_unknown &&
1439+
types_res[i] == UNKNOWNOID &&
1440+
get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start))
1441+
{
1442+
types_res[i] = TEXTOID;
1443+
1444+
if (PG_ARGISNULL(i + variadic_start))
1445+
args_res[i] = (Datum) 0;
1446+
else
1447+
args_res[i] =
1448+
CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start));
1449+
}
1450+
else
1451+
{
1452+
/* no conversion needed, just take the datum as given */
1453+
args_res[i] = PG_GETARG_DATUM(i + variadic_start);
1454+
}
1455+
1456+
if (!OidIsValid(types_res[i]) ||
1457+
(convert_unknown && types_res[i] == UNKNOWNOID))
1458+
ereport(ERROR,
1459+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1460+
errmsg("could not determine data type for argument %d",
1461+
i + 1)));
1462+
}
1463+
}
1464+
1465+
/* Fill in results */
1466+
*args = args_res;
1467+
*nulls = nulls_res;
1468+
*types = types_res;
1469+
1470+
return nargs;
1471+
}

src/include/funcapi.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
*
33
* funcapi.h
44
* Definitions for functions which return composite type and/or sets
5+
* or work on VARIADIC inputs.
56
*
67
* This file must be included by all Postgres modules that either define
78
* or call FUNCAPI-callable functions or macros.
@@ -314,4 +315,26 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
314315
PG_RETURN_NULL(); \
315316
} while (0)
316317

318+
/*----------
319+
* Support to ease writing of functions dealing with VARIADIC inputs
320+
*----------
321+
*
322+
* This function extracts a set of argument values, types and NULL markers
323+
* for a given input function. This returns a set of data:
324+
* - **values includes the set of Datum values extracted.
325+
* - **types the data type OID for each element.
326+
* - **nulls tracks if an element is NULL.
327+
*
328+
* variadic_start indicates the argument number where the VARIADIC argument
329+
* starts.
330+
* convert_unknown set to true will enforce the conversion of arguments
331+
* with unknown data type to text.
332+
*
333+
* The return result is the number of elements stored, or -1 in the case of
334+
* "VARIADIC NULL".
335+
*/
336+
extern int extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
337+
bool convert_unknown, Datum **values,
338+
Oid **types, bool **nulls);
339+
317340
#endif /* FUNCAPI_H */

0 commit comments

Comments
 (0)