The original implementation of polymorphic aggregates didn't really get the

checking of argument compatibility right; although the problem is only exposed
with multiple-input aggregates in which some arguments are polymorphic and
some are not.  Per bug #3852 from Sokolov Yura.
This commit is contained in:
Tom Lane 2008-01-11 18:39:41 +00:00
parent df62977d00
commit 89c0a87fda
9 changed files with 133 additions and 57 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.89 2008/01/01 19:45:48 momjian Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.90 2008/01/11 18:39:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -296,7 +296,6 @@ lookup_agg_function(List *fnName,
FuncDetailCode fdresult; FuncDetailCode fdresult;
AclResult aclresult; AclResult aclresult;
int i; int i;
bool allPolyArgs = true;
/* /*
* func_get_detail looks up the function in the catalogs, does * func_get_detail looks up the function in the catalogs, does
@ -322,25 +321,15 @@ lookup_agg_function(List *fnName,
func_signature_string(fnName, nargs, input_types)))); func_signature_string(fnName, nargs, input_types))));
/* /*
* If the given type(s) are all polymorphic, there's nothing we can check. * If there are any polymorphic types involved, enforce consistency, and
* Otherwise, enforce consistency, and possibly refine the result type. * possibly refine the result type. It's OK if the result is still
* polymorphic at this point, though.
*/ */
for (i = 0; i < nargs; i++) *rettype = enforce_generic_type_consistency(input_types,
{ true_oid_array,
if (!IsPolymorphicType(input_types[i])) nargs,
{ *rettype,
allPolyArgs = false; true);
break;
}
}
if (!allPolyArgs)
{
*rettype = enforce_generic_type_consistency(input_types,
true_oid_array,
nargs,
*rettype);
}
/* /*
* func_get_detail will find functions requiring run-time argument type * func_get_detail will find functions requiring run-time argument type

View File

@ -61,7 +61,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.155 2008/01/01 19:45:49 momjian Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.156 2008/01/11 18:39:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1433,7 +1433,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
aggtranstype = enforce_generic_type_consistency(inputTypes, aggtranstype = enforce_generic_type_consistency(inputTypes,
declaredArgTypes, declaredArgTypes,
agg_nargs, agg_nargs,
aggtranstype); aggtranstype,
false);
pfree(declaredArgTypes); pfree(declaredArgTypes);
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.253 2008/01/01 19:45:50 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.254 2008/01/11 18:39:40 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
@ -448,7 +448,8 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
aggtranstype = enforce_generic_type_consistency(inputTypes, aggtranstype = enforce_generic_type_consistency(inputTypes,
declaredArgTypes, declaredArgTypes,
agg_nargs, agg_nargs,
aggtranstype); aggtranstype,
false);
pfree(declaredArgTypes); pfree(declaredArgTypes);
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.160 2008/01/01 19:45:50 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.161 2008/01/11 18:39:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1255,12 +1255,20 @@ check_generic_type_consistency(Oid *actual_arg_types,
* we add the extra condition that the ANYELEMENT type must not be an array. * we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.) * is an extra restriction if not.)
*
* When allow_poly is false, we are not expecting any of the actual_arg_types
* to be polymorphic, and we should not return a polymorphic result type
* either. When allow_poly is true, it is okay to have polymorphic "actual"
* arg types, and we can return ANYARRAY or ANYELEMENT as the result. (This
* case is currently used only to check compatibility of an aggregate's
* declaration with the underlying transfn.)
*/ */
Oid Oid
enforce_generic_type_consistency(Oid *actual_arg_types, enforce_generic_type_consistency(Oid *actual_arg_types,
Oid *declared_arg_types, Oid *declared_arg_types,
int nargs, int nargs,
Oid rettype) Oid rettype,
bool allow_poly)
{ {
int j; int j;
bool have_generics = false; bool have_generics = false;
@ -1268,9 +1276,6 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
Oid elem_typeid = InvalidOid; Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid; Oid array_typeid = InvalidOid;
Oid array_typelem; Oid array_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID); bool have_anyenum = (rettype == ANYENUMOID);
@ -1287,7 +1292,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
decl_type == ANYNONARRAYOID || decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID) decl_type == ANYENUMOID)
{ {
have_generics = have_anyelement = true; have_generics = true;
if (decl_type == ANYNONARRAYOID) if (decl_type == ANYNONARRAYOID)
have_anynonarray = true; have_anynonarray = true;
else if (decl_type == ANYENUMOID) else if (decl_type == ANYENUMOID)
@ -1297,6 +1302,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
have_unknowns = true; have_unknowns = true;
continue; continue;
} }
if (allow_poly && decl_type == actual_type)
continue; /* no new information here */
if (OidIsValid(elem_typeid) && actual_type != elem_typeid) if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
@ -1314,6 +1321,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
have_unknowns = true; have_unknowns = true;
continue; continue;
} }
if (allow_poly && decl_type == actual_type)
continue; /* no new information here */
if (OidIsValid(array_typeid) && actual_type != array_typeid) if (OidIsValid(array_typeid) && actual_type != array_typeid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
@ -1335,20 +1344,12 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
/* Get the element type based on the array type, if we have one */ /* Get the element type based on the array type, if we have one */
if (OidIsValid(array_typeid)) if (OidIsValid(array_typeid))
{ {
if (array_typeid == ANYARRAYOID && !have_anyelement) array_typelem = get_element_type(array_typeid);
{ if (!OidIsValid(array_typelem))
/* Special case for ANYARRAY input: okay iff no ANYELEMENT */ ereport(ERROR,
array_typelem = InvalidOid; (errcode(ERRCODE_DATATYPE_MISMATCH),
} errmsg("argument declared \"anyarray\" is not an array but type %s",
else format_type_be(array_typeid))));
{
array_typelem = get_element_type(array_typeid);
if (!OidIsValid(array_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared \"anyarray\" is not an array but type %s",
format_type_be(array_typeid))));
}
if (!OidIsValid(elem_typeid)) if (!OidIsValid(elem_typeid))
{ {
@ -1370,13 +1371,21 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
} }
else if (!OidIsValid(elem_typeid)) else if (!OidIsValid(elem_typeid))
{ {
/* Only way to get here is if all the generic args are UNKNOWN */ if (allow_poly)
ereport(ERROR, {
(errcode(ERRCODE_DATATYPE_MISMATCH), array_typeid = ANYARRAYOID;
errmsg("could not determine polymorphic type because input has type \"unknown\""))); elem_typeid = ANYELEMENTOID;
}
else
{
/* Only way to get here is if all the generic args are UNKNOWN */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type because input has type \"unknown\"")));
}
} }
if (have_anynonarray) if (have_anynonarray && elem_typeid != ANYELEMENTOID)
{ {
/* require the element type to not be an array */ /* require the element type to not be an array */
if (type_is_array(elem_typeid)) if (type_is_array(elem_typeid))
@ -1386,7 +1395,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
format_type_be(elem_typeid)))); format_type_be(elem_typeid))));
} }
if (have_anyenum) if (have_anyenum && elem_typeid != ANYELEMENTOID)
{ {
/* require the element type to be an enum */ /* require the element type to be an enum */
if (!type_is_enum(elem_typeid)) if (!type_is_enum(elem_typeid))

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.200 2008/01/01 19:45:51 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.201 2008/01/11 18:39:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -235,7 +235,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
rettype = enforce_generic_type_consistency(actual_arg_types, rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types, declared_arg_types,
nargs, nargs,
rettype); rettype,
false);
/* perform the necessary typecasting of arguments */ /* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.100 2008/01/01 19:45:51 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.101 2008/01/11 18:39:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1006,7 +1006,8 @@ make_scalar_array_op(ParseState *pstate, List *opname,
rettype = enforce_generic_type_consistency(actual_arg_types, rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types, declared_arg_types,
2, 2,
opform->oprresult); opform->oprresult,
false);
/* /*
* Check that operator result is boolean * Check that operator result is boolean
@ -1116,7 +1117,8 @@ make_op_expr(ParseState *pstate, Operator op,
rettype = enforce_generic_type_consistency(actual_arg_types, rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types, declared_arg_types,
nargs, nargs,
opform->oprresult); opform->oprresult,
false);
/* perform the necessary typecasting of arguments */ /* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.74 2008/01/01 19:45:58 momjian Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.75 2008/01/11 18:39:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -81,7 +81,8 @@ extern bool check_generic_type_consistency(Oid *actual_arg_types,
extern Oid enforce_generic_type_consistency(Oid *actual_arg_types, extern Oid enforce_generic_type_consistency(Oid *actual_arg_types,
Oid *declared_arg_types, Oid *declared_arg_types,
int nargs, int nargs,
Oid rettype); Oid rettype,
bool allow_poly);
extern Oid resolve_generic_type(Oid declared_type, extern Oid resolve_generic_type(Oid declared_type,
Oid context_actual_type, Oid context_actual_type,
Oid context_declared_type); Oid context_declared_type);

View File

@ -577,3 +577,39 @@ select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl;
-4567890123456789 | -4567890123456788 -4567890123456789 | -4567890123456788
(5 rows) (5 rows)
-- another kind of polymorphic aggregate
create function add_group(grp anyarray, ad anyelement, size integer)
returns anyarray
as $$
begin
if grp is null then
return array[ad];
end if;
if array_upper(grp, 1) < size then
return grp || ad;
end if;
return grp;
end;
$$
language plpgsql immutable;
create aggregate build_group(anyelement, integer) (
SFUNC = add_group,
STYPE = anyarray
);
select build_group(q1,3) from int8_tbl;
build_group
----------------------------
{123,123,4567890123456789}
(1 row)
-- this should fail because stype isn't compatible with arg
create aggregate build_group(int8, integer) (
SFUNC = add_group,
STYPE = int2[]
);
ERROR: function add_group(smallint[], bigint, integer) does not exist
-- but we can make a non-poly agg from a poly sfunc if types are OK
create aggregate build_group(int8, integer) (
SFUNC = add_group,
STYPE = int8[]
);

View File

@ -390,3 +390,39 @@ select case when $1 then $2 else $3 end $$ language sql;
select f1, sql_if(f1 > 0, bleat(f1), bleat(f1 + 1)) from int4_tbl; select f1, sql_if(f1 > 0, bleat(f1), bleat(f1 + 1)) from int4_tbl;
select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl; select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl;
-- another kind of polymorphic aggregate
create function add_group(grp anyarray, ad anyelement, size integer)
returns anyarray
as $$
begin
if grp is null then
return array[ad];
end if;
if array_upper(grp, 1) < size then
return grp || ad;
end if;
return grp;
end;
$$
language plpgsql immutable;
create aggregate build_group(anyelement, integer) (
SFUNC = add_group,
STYPE = anyarray
);
select build_group(q1,3) from int8_tbl;
-- this should fail because stype isn't compatible with arg
create aggregate build_group(int8, integer) (
SFUNC = add_group,
STYPE = int2[]
);
-- but we can make a non-poly agg from a poly sfunc if types are OK
create aggregate build_group(int8, integer) (
SFUNC = add_group,
STYPE = int8[]
);