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.
This commit is contained in:
parent
405a468b02
commit
d26888bc4d
@ -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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
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,
|
|||||||
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]);
|
|||||||
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[]) = '';
|
|||||||
--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]);
|
|||||||
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'\\');
|
|||||||
-- 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[]);
|
|||||||
-- 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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user