Commit d26888bc authored by Andrew Dunstan's avatar Andrew Dunstan

Move checking an explicit VARIADIC "any" argument into the parser.

This is more efficient and simpler . It does mean that an untyped NULL
can no longer be used in such cases, which should be mentioned in
Release Notes, but doesn't seem a terrible loss. The workaround is to
cast the NULL to some array type.

Pavel Stehule, reviewed by Jeevan Chalke.
parent 405a468b
...@@ -332,6 +332,7 @@ lookup_agg_function(List *fnName, ...@@ -332,6 +332,7 @@ lookup_agg_function(List *fnName,
Oid fnOid; Oid fnOid;
bool retset; bool retset;
int nvargs; int nvargs;
Oid vatype;
Oid *true_oid_array; Oid *true_oid_array;
FuncDetailCode fdresult; FuncDetailCode fdresult;
AclResult aclresult; AclResult aclresult;
...@@ -346,7 +347,8 @@ lookup_agg_function(List *fnName, ...@@ -346,7 +347,8 @@ lookup_agg_function(List *fnName,
*/ */
fdresult = func_get_detail(fnName, NIL, NIL, fdresult = func_get_detail(fnName, NIL, NIL,
nargs, input_types, false, false, nargs, input_types, false, false,
&fnOid, rettype, &retset, &nvargs, &fnOid, rettype, &retset,
&nvargs, &vatype,
&true_oid_array, NULL); &true_oid_array, NULL);
/* only valid case is a normal function not returning a set */ /* only valid case is a normal function not returning a set */
......
...@@ -79,6 +79,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -79,6 +79,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Node *retval; Node *retval;
bool retset; bool retset;
int nvargs; int nvargs;
Oid vatype;
FuncDetailCode fdresult; FuncDetailCode fdresult;
/* /*
...@@ -214,7 +215,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -214,7 +215,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
fdresult = func_get_detail(funcname, fargs, argnames, nargs, fdresult = func_get_detail(funcname, fargs, argnames, nargs,
actual_arg_types, actual_arg_types,
!func_variadic, true, !func_variadic, true,
&funcid, &rettype, &retset, &nvargs, &funcid, &rettype, &retset,
&nvargs, &vatype,
&declared_arg_types, &argdefaults); &declared_arg_types, &argdefaults);
if (fdresult == FUNCDETAIL_COERCION) if (fdresult == FUNCDETAIL_COERCION)
{ {
...@@ -382,6 +384,22 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -382,6 +384,22 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
fargs = lappend(fargs, newa); fargs = lappend(fargs, newa);
} }
/*
* When function is called an explicit VARIADIC labeled parameter,
* and the declared_arg_type is "any", then sanity check the actual
* parameter type now - it must be an array.
*/
if (nargs > 0 && vatype == ANYOID && func_variadic)
{
Oid va_arr_typid = actual_arg_types[nargs - 1];
if (!OidIsValid(get_element_type(va_arr_typid)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("VARIADIC argument must be an array"),
parser_errposition(pstate, exprLocation((Node *) llast(fargs)))));
}
/* build the appropriate output structure */ /* build the appropriate output structure */
if (fdresult == FUNCDETAIL_NORMAL) if (fdresult == FUNCDETAIL_NORMAL)
{ {
...@@ -1033,6 +1051,7 @@ func_get_detail(List *funcname, ...@@ -1033,6 +1051,7 @@ func_get_detail(List *funcname,
Oid *rettype, /* return value */ Oid *rettype, /* return value */
bool *retset, /* return value */ bool *retset, /* return value */
int *nvargs, /* return value */ int *nvargs, /* return value */
Oid *vatype, /* return value */
Oid **true_typeids, /* return value */ Oid **true_typeids, /* return value */
List **argdefaults) /* optional return value */ List **argdefaults) /* optional return value */
{ {
...@@ -1251,6 +1270,7 @@ func_get_detail(List *funcname, ...@@ -1251,6 +1270,7 @@ func_get_detail(List *funcname,
pform = (Form_pg_proc) GETSTRUCT(ftup); pform = (Form_pg_proc) GETSTRUCT(ftup);
*rettype = pform->prorettype; *rettype = pform->prorettype;
*retset = pform->proretset; *retset = pform->proretset;
*vatype = pform->provariadic;
/* fetch default args if caller wants 'em */ /* fetch default args if caller wants 'em */
if (argdefaults && best_candidate->ndargs > 0) if (argdefaults && best_candidate->ndargs > 0)
{ {
......
...@@ -8584,6 +8584,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, ...@@ -8584,6 +8584,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
Oid p_rettype; Oid p_rettype;
bool p_retset; bool p_retset;
int p_nvargs; int p_nvargs;
Oid p_vatype;
Oid *p_true_typeids; Oid *p_true_typeids;
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
...@@ -8634,7 +8635,8 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, ...@@ -8634,7 +8635,8 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
NIL, argnames, nargs, argtypes, NIL, argnames, nargs, argtypes,
!use_variadic, true, !use_variadic, true,
&p_funcid, &p_rettype, &p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL); &p_retset, &p_nvargs, &p_vatype,
&p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL || if ((p_result == FUNCDETAIL_NORMAL ||
p_result == FUNCDETAIL_AGGREGATE || p_result == FUNCDETAIL_AGGREGATE ||
p_result == FUNCDETAIL_WINDOWFUNC) && p_result == FUNCDETAIL_WINDOWFUNC) &&
......
...@@ -3820,7 +3820,6 @@ concat_internal(const char *sepstr, int argidx, ...@@ -3820,7 +3820,6 @@ concat_internal(const char *sepstr, int argidx,
*/ */
if (get_fn_expr_variadic(fcinfo->flinfo)) if (get_fn_expr_variadic(fcinfo->flinfo))
{ {
Oid arr_typid;
ArrayType *arr; ArrayType *arr;
/* Should have just the one argument */ /* Should have just the one argument */
...@@ -3831,20 +3830,16 @@ concat_internal(const char *sepstr, int argidx, ...@@ -3831,20 +3830,16 @@ concat_internal(const char *sepstr, int argidx,
return NULL; return NULL;
/* /*
* Non-null argument had better be an array. The parser doesn't * Non-null argument had better be an array
* enforce this for VARIADIC ANY functions (maybe it should?), so that *
* check uses ereport not just elog. * Correct values are ensured by parser check, but this function
* can be called directly, bypassing the parser, so we should do
* some minimal check too - this form of call requires correctly set
* expr argtype in flinfo.
*/ */
arr_typid = get_fn_expr_argtype(fcinfo->flinfo, argidx); Assert(OidIsValid(get_fn_expr_argtype(fcinfo->flinfo, argidx)));
if (!OidIsValid(arr_typid)) Assert(OidIsValid(get_element_type(get_fn_expr_argtype(fcinfo->flinfo, argidx))));
elog(ERROR, "could not determine data type of concat() input");
if (!OidIsValid(get_element_type(arr_typid)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("VARIADIC argument must be an array")));
/* OK, safe to fetch the array value */
arr = PG_GETARG_ARRAYTYPE_P(argidx); arr = PG_GETARG_ARRAYTYPE_P(argidx);
/* /*
...@@ -4049,7 +4044,6 @@ text_format(PG_FUNCTION_ARGS) ...@@ -4049,7 +4044,6 @@ text_format(PG_FUNCTION_ARGS)
/* If argument is marked VARIADIC, expand array into elements */ /* If argument is marked VARIADIC, expand array into elements */
if (get_fn_expr_variadic(fcinfo->flinfo)) if (get_fn_expr_variadic(fcinfo->flinfo))
{ {
Oid arr_typid;
ArrayType *arr; ArrayType *arr;
int16 elmlen; int16 elmlen;
bool elmbyval; bool elmbyval;
...@@ -4065,20 +4059,16 @@ text_format(PG_FUNCTION_ARGS) ...@@ -4065,20 +4059,16 @@ text_format(PG_FUNCTION_ARGS)
else else
{ {
/* /*
* Non-null argument had better be an array. The parser doesn't * Non-null argument had better be an array
* enforce this for VARIADIC ANY functions (maybe it should?), so *
* that check uses ereport not just elog. * Correct values are ensured by parser check, but this function
* can be called directly, bypassing the parser, so we should do
* some minimal check too - this form of call requires correctly set
* expr argtype in flinfo.
*/ */
arr_typid = get_fn_expr_argtype(fcinfo->flinfo, 1); Assert(OidIsValid(get_fn_expr_argtype(fcinfo->flinfo, 1)));
if (!OidIsValid(arr_typid)) Assert(OidIsValid(get_element_type(get_fn_expr_argtype(fcinfo->flinfo, 1))));
elog(ERROR, "could not determine data type of format() input");
if (!OidIsValid(get_element_type(arr_typid)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("VARIADIC argument must be an array")));
/* OK, safe to fetch the array value */
arr = PG_GETARG_ARRAYTYPE_P(1); arr = PG_GETARG_ARRAYTYPE_P(1);
/* Get info about array element type */ /* Get info about array element type */
......
...@@ -52,8 +52,8 @@ extern FuncDetailCode func_get_detail(List *funcname, ...@@ -52,8 +52,8 @@ extern FuncDetailCode func_get_detail(List *funcname,
int nargs, Oid *argtypes, int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults, bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype, Oid *funcid, Oid *rettype,
bool *retset, int *nvargs, Oid **true_typeids, bool *retset, int *nvargs, Oid *vatype,
List **argdefaults); Oid **true_typeids, List **argdefaults);
extern int func_match_argtypes(int nargs, extern int func_match_argtypes(int nargs,
Oid *input_typeids, Oid *input_typeids,
......
...@@ -149,13 +149,13 @@ select concat_ws(',', variadic array[1,2,3]); ...@@ -149,13 +149,13 @@ select concat_ws(',', variadic array[1,2,3]);
1,2,3 1,2,3
(1 row) (1 row)
select concat_ws(',', variadic NULL); select concat_ws(',', variadic NULL::int[]);
concat_ws concat_ws
----------- -----------
(1 row) (1 row)
select concat(variadic NULL) is NULL; select concat(variadic NULL::int[]) is NULL;
?column? ?column?
---------- ----------
t t
...@@ -170,6 +170,8 @@ select concat(variadic '{}'::int[]) = ''; ...@@ -170,6 +170,8 @@ select concat(variadic '{}'::int[]) = '';
--should fail --should fail
select concat_ws(',', variadic 10); select concat_ws(',', variadic 10);
ERROR: VARIADIC argument must be an array ERROR: VARIADIC argument must be an array
LINE 1: select concat_ws(',', variadic 10);
^
/* /*
* format * format
*/ */
...@@ -315,8 +317,8 @@ select format('%2$s, %1$s', variadic array[1, 2]); ...@@ -315,8 +317,8 @@ select format('%2$s, %1$s', variadic array[1, 2]);
2, 1 2, 1
(1 row) (1 row)
-- variadic argument can be NULL, but should not be referenced -- variadic argument can be array type NULL, but should not be referenced
select format('Hello', variadic NULL); select format('Hello', variadic NULL::int[]);
format format
-------- --------
Hello Hello
......
...@@ -47,8 +47,8 @@ select quote_literal(e'\\'); ...@@ -47,8 +47,8 @@ select quote_literal(e'\\');
-- check variadic labeled argument -- check variadic labeled argument
select concat(variadic array[1,2,3]); select concat(variadic array[1,2,3]);
select concat_ws(',', variadic array[1,2,3]); select concat_ws(',', variadic array[1,2,3]);
select concat_ws(',', variadic NULL); select concat_ws(',', variadic NULL::int[]);
select concat(variadic NULL) is NULL; select concat(variadic NULL::int[]) is NULL;
select concat(variadic '{}'::int[]) = ''; select concat(variadic '{}'::int[]) = '';
--should fail --should fail
select concat_ws(',', variadic 10); select concat_ws(',', variadic 10);
...@@ -93,8 +93,8 @@ select format('%s, %s', variadic array[true, false]::text[]); ...@@ -93,8 +93,8 @@ select format('%s, %s', variadic array[true, false]::text[]);
-- check variadic with positional placeholders -- check variadic with positional placeholders
select format('%2$s, %1$s', variadic array['first', 'second']); select format('%2$s, %1$s', variadic array['first', 'second']);
select format('%2$s, %1$s', variadic array[1, 2]); select format('%2$s, %1$s', variadic array[1, 2]);
-- variadic argument can be NULL, but should not be referenced -- variadic argument can be array type NULL, but should not be referenced
select format('Hello', variadic NULL); select format('Hello', variadic NULL::int[]);
-- variadic argument allows simulating more than FUNC_MAX_ARGS parameters -- variadic argument allows simulating more than FUNC_MAX_ARGS parameters
select format(string_agg('%s',','), variadic array_agg(i)) select format(string_agg('%s',','), variadic array_agg(i))
from generate_series(1,200) g(i); from generate_series(1,200) g(i);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment