The original implementation of polymorphic aggregates didn't really get the
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Jan 2008 18:39:41 +0000 (18:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Jan 2008 18:39:41 +0000 (18:39 +0000)
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.

src/backend/catalog/pg_aggregate.c
src/backend/executor/nodeAgg.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_func.c
src/backend/parser/parse_oper.c
src/include/parser/parse_coerce.h
src/test/regress/expected/polymorphism.out
src/test/regress/sql/polymorphism.sql

index 449a3256ad37bef992d6b78c2d3c48b41ae9477f..bfdd429b23ae3b467365896e39ca84ee0f18b625 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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;
        AclResult       aclresult;
        int                     i;
-       bool            allPolyArgs = true;
 
        /*
         * 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))));
 
        /*
-        * If the given type(s) are all polymorphic, there's nothing we can check.
-        * Otherwise, enforce consistency, and possibly refine the result type.
+        * If there are any polymorphic types involved, enforce consistency, and
+        * possibly refine the result type.  It's OK if the result is still
+        * polymorphic at this point, though.
         */
-       for (i = 0; i < nargs; i++)
-       {
-               if (!IsPolymorphicType(input_types[i]))
-               {
-                       allPolyArgs = false;
-                       break;
-               }
-       }
-
-       if (!allPolyArgs)
-       {
-               *rettype = enforce_generic_type_consistency(input_types,
-                                                                                                       true_oid_array,
-                                                                                                       nargs,
-                                                                                                       *rettype);
-       }
+       *rettype = enforce_generic_type_consistency(input_types,
+                                                                                               true_oid_array,
+                                                                                               nargs,
+                                                                                               *rettype,
+                                                                                               true);
 
        /*
         * func_get_detail will find functions requiring run-time argument type
index 9551e9ddd2341aa3a5391c0da230491c05251d86..0fac11a53718cb66a74c2802d4ec6a2413b3550d 100644 (file)
@@ -61,7 +61,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,
                                                                                                                        declaredArgTypes,
                                                                                                                        agg_nargs,
-                                                                                                                       aggtranstype);
+                                                                                                                       aggtranstype,
+                                                                                                                       false);
                        pfree(declaredArgTypes);
                }
 
index 01f3be3ba349c256456774699ef9b662f0717cb7..bee9c7a9dda21e6f592e6250382fdea0082cad76 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -448,7 +448,8 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
                        aggtranstype = enforce_generic_type_consistency(inputTypes,
                                                                                                                        declaredArgTypes,
                                                                                                                        agg_nargs,
-                                                                                                                       aggtranstype);
+                                                                                                                       aggtranstype,
+                                                                                                                       false);
                        pfree(declaredArgTypes);
                }
 
index 388d98b34368781d0daba918dc2ea80c71e4c4d4..24676404340449664e1fe9911ca11a37e0ca7a7f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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.
  *       (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
  *       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
 enforce_generic_type_consistency(Oid *actual_arg_types,
                                                                 Oid *declared_arg_types,
                                                                 int nargs,
-                                                                Oid rettype)
+                                                                Oid rettype,
+                                                                bool allow_poly)
 {
        int                     j;
        bool            have_generics = false;
@@ -1268,9 +1276,6 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
        Oid                     elem_typeid = InvalidOid;
        Oid                     array_typeid = InvalidOid;
        Oid                     array_typelem;
-       bool            have_anyelement = (rettype == ANYELEMENTOID ||
-                                                                  rettype == ANYNONARRAYOID ||
-                                                                  rettype == ANYENUMOID);
        bool            have_anynonarray = (rettype == ANYNONARRAYOID);
        bool            have_anyenum = (rettype == ANYENUMOID);
 
@@ -1287,7 +1292,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
                        decl_type == ANYNONARRAYOID ||
                        decl_type == ANYENUMOID)
                {
-                       have_generics = have_anyelement = true;
+                       have_generics = true;
                        if (decl_type == ANYNONARRAYOID)
                                have_anynonarray = true;
                        else if (decl_type == ANYENUMOID)
@@ -1297,6 +1302,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
                                have_unknowns = true;
                                continue;
                        }
+                       if (allow_poly && decl_type == actual_type)
+                               continue;               /* no new information here */
                        if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
                                ereport(ERROR,
                                                (errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1314,6 +1321,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
                                have_unknowns = true;
                                continue;
                        }
+                       if (allow_poly && decl_type == actual_type)
+                               continue;               /* no new information here */
                        if (OidIsValid(array_typeid) && actual_type != array_typeid)
                                ereport(ERROR,
                                                (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 */
        if (OidIsValid(array_typeid))
        {
-               if (array_typeid == ANYARRAYOID && !have_anyelement)
-               {
-                       /* Special case for ANYARRAY input: okay iff no ANYELEMENT */
-                       array_typelem = InvalidOid;
-               }
-               else
-               {
-                       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))));
-               }
+               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))
                {
@@ -1370,13 +1371,21 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
        }
        else if (!OidIsValid(elem_typeid))
        {
-               /* 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 (allow_poly)
+               {
+                       array_typeid = ANYARRAYOID;
+                       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 */
                if (type_is_array(elem_typeid))
@@ -1386,7 +1395,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
                                                  format_type_be(elem_typeid))));
        }
 
-       if (have_anyenum)
+       if (have_anyenum && elem_typeid != ANYELEMENTOID)
        {
                /* require the element type to be an enum */
                if (!type_is_enum(elem_typeid))
index a7c1425ef3e583d29d8bd68ea2416aba10b18eba..a66d714838bd820cf2d0540962844764cf3428c5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
                                                                                           declared_arg_types,
                                                                                           nargs,
-                                                                                          rettype);
+                                                                                          rettype,
+                                                                                          false);
 
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
index 1bd58cdbcebaa1ccd8914d879b836093965d58a3..6b23fbb9e9a05af761d1b5262d40a1034be4ea3d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
                                                                                           declared_arg_types,
                                                                                           2,
-                                                                                          opform->oprresult);
+                                                                                          opform->oprresult,
+                                                                                          false);
 
        /*
         * 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,
                                                                                           declared_arg_types,
                                                                                           nargs,
-                                                                                          opform->oprresult);
+                                                                                          opform->oprresult,
+                                                                                          false);
 
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
index c5b2b48ec3b71053860a0895a08292f6ba2804b6..ab36a0c1969bd77db544d3a47cd92eadbaf6459a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * 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,
                                                                 Oid *declared_arg_types,
                                                                 int nargs,
-                                                                Oid rettype);
+                                                                Oid rettype,
+                                                                bool allow_poly);
 extern Oid resolve_generic_type(Oid declared_type,
                                         Oid context_actual_type,
                                         Oid context_declared_type);
index de393a052880ec75e1928f9417f06b7013c4139f..a208203c6d39881a911919f9a221ce5c0e96a7b0 100644 (file)
@@ -577,3 +577,39 @@ select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl;
  -4567890123456789 | -4567890123456788
 (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[]
+);
index c2bf14b48f007969c8388f314c0dd1aeaaa45f20..2df963952f4e2ff9583a5ad63a4520cbb6df3d51 100644 (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 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[]
+);