Fix a thinko in my patch of a couple months ago for bug #3116: it did the
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 May 2007 18:54:24 +0000 (18:54 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 May 2007 18:54:24 +0000 (18:54 +0000)
wrong thing when inlining polymorphic SQL functions, because it was using the
function's declared return type where it should have used the actual result
type of the current call.  In 8.1 and 8.2 this causes obvious failures even if
you don't have assertions turned on; in 8.0 and 7.4 it would only be a problem
if the inlined expression were used as an input to a function that did
run-time type determination on its inputs.  Add a regression test, since this
is evidently an under-tested area.

src/backend/optimizer/util/clauses.c
src/test/regress/expected/polymorphism.out [new file with mode: 0644]
src/test/regress/sql/polymorphism.sql [new file with mode: 0644]

index d00e326290a6c2ce251b853cb7be449da24e34c8..06d006bd4c70ee2341dc1cd951b8a53201f2cdc3 100644 (file)
@@ -1987,11 +1987,11 @@ inline_function(Oid funcid, Oid result_type, List *args,
         * compatible with the original expression result type.  To avoid
         * confusing matters, insert a RelabelType in such cases.
         */
-       if (exprType(newexpr) != funcform->prorettype)
+       if (exprType(newexpr) != result_type)
        {
-               Assert(IsBinaryCoercible(exprType(newexpr), funcform->prorettype));
+               Assert(IsBinaryCoercible(exprType(newexpr), result_type));
                newexpr = (Node *) makeRelabelType((Expr *) newexpr,
-                                                                                  funcform->prorettype,
+                                                                                  result_type,
                                                                                   -1,
                                                                                   COERCE_IMPLICIT_CAST);
        }
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
new file mode 100644 (file)
index 0000000..651ae0b
--- /dev/null
@@ -0,0 +1,567 @@
+-- Currently this tests polymorphic aggregates and indirectly does some
+-- testing of polymorphic SQL functions.  It ought to be extended.
+-- Legend:
+-----------
+-- A = type is ANY
+-- P = type is polymorphic
+-- N = type is non-polymorphic
+-- B = aggregate base type
+-- S = aggregate state type
+-- R = aggregate return type
+-- 1 = arg1 of a function
+-- 2 = arg2 of a function
+-- ag = aggregate
+-- tf = trans (state) function
+-- ff = final function
+-- rt = return type of a function
+-- -> = implies
+-- => = allowed
+-- !> = not allowed
+-- E  = exists
+-- NE = not-exists
+-- 
+-- Possible states:
+-- ----------------
+-- B = (A || P || N)
+--   when (B = A) -> (tf2 = NE)
+-- S = (P || N)
+-- ff = (E || NE)
+-- tf1 = (P || N)
+-- tf2 = (NE || P || N)
+-- R = (P || N)
+-- create functions for use as tf and ff with the needed combinations of
+-- argument polymorphism, but within the constraints of valid aggregate
+-- functions, i.e. tf arg1 and tf return type must match
+-- polymorphic single arg transfn
+CREATE FUNCTION stfp(anyarray) returns anyarray as
+'select $1' language 'sql';
+-- non-polymorphic single arg transfn
+CREATE FUNCTION stfnp(int[]) returns int[] as
+'select $1' language 'sql';
+-- dual polymorphic transfn
+CREATE FUNCTION tfp(anyarray,anyelement) returns anyarray as
+'select $1 || $2' language 'sql';
+-- dual non-polymorphic transfn
+CREATE FUNCTION tfnp(int[],int) returns int[] as
+'select $1 || $2' language 'sql';
+-- arg1 only polymorphic transfn
+CREATE FUNCTION tf1p(anyarray,int) returns anyarray as
+'select $1' language 'sql';
+-- arg2 only polymorphic transfn
+CREATE FUNCTION tf2p(int[],anyelement) returns int[] as
+'select $1' language 'sql';
+-- finalfn polymorphic
+CREATE FUNCTION ffp(anyarray) returns anyarray as
+'select $1' language 'sql';
+-- finalfn non-polymorphic
+CREATE FUNCTION ffnp(int[]) returns int[] as
+'select $1' language 'sql';
+-- Try to cover all the possible states:
+-- 
+-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
+-- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
+-- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
+-- return N. Therefore, if the transfn is stfp, tfp, or tf1p, we must use ffnp
+-- as finalfn, because stfp, tfp, and tf1p do not return N.
+--
+--     Case1 (R = P) && (B = A)
+--     ------------------------
+--     S    tf1
+--     -------
+--     N    N
+-- should CREATE
+CREATE AGGREGATE myaggp01a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[],
+  FINALFUNC = ffp, INITCOND = '{}');
+--     P    N
+-- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
+CREATE AGGREGATE myaggp02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--     N    P
+-- should CREATE
+CREATE AGGREGATE myaggp03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp03b(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[],
+  INITCOND = '{}');
+--     P    P
+-- should ERROR: we have no way to resolve S
+CREATE AGGREGATE myaggp04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+CREATE AGGREGATE myaggp04b(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
+  INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    Case2 (R = P) && ((B = P) || (B = N))
+--    -------------------------------------
+--    S    tf1      B    tf2
+--    -----------------------
+--    N    N        N    N
+-- should CREATE
+CREATE AGGREGATE myaggp05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+--    N    N        N    P
+-- should CREATE
+CREATE AGGREGATE myaggp06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+--    N    N        P    N
+-- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int)
+CREATE AGGREGATE myaggp07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  function tfnp(integer[], anyelement) does not exist
+--    N    N        P    P
+-- should CREATE
+CREATE AGGREGATE myaggp08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+--    N    P        N    N
+-- should CREATE
+CREATE AGGREGATE myaggp09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp09b(BASETYPE = int, SFUNC = tf1p, STYPE = int[],
+  INITCOND = '{}');
+--    N    P        N    P
+-- should CREATE
+CREATE AGGREGATE myaggp10a(BASETYPE = int, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp10b(BASETYPE = int, SFUNC = tfp, STYPE = int[],
+  INITCOND = '{}');
+--    N    P        P    N
+-- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int)
+CREATE AGGREGATE myaggp11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  function tf1p(integer[], anyelement) does not exist
+CREATE AGGREGATE myaggp11b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[],
+  INITCOND = '{}');
+ERROR:  function tf1p(integer[], anyelement) does not exist
+--    N    P        P    P
+-- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement)
+CREATE AGGREGATE myaggp12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  function tfp(integer[], anyelement) does not exist
+CREATE AGGREGATE myaggp12b(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[],
+  INITCOND = '{}');
+ERROR:  function tfp(integer[], anyelement) does not exist
+--    P    N        N    N
+-- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    N        N    P
+-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    N        P    N
+-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  function tfnp(anyarray, anyelement) does not exist
+--    P    N        P    P
+-- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggp16a(BASETYPE = anyelement, SFUNC = tf2p,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  function tf2p(anyarray, anyelement) does not exist
+--    P    P        N    N
+-- should ERROR: we have no way to resolve S
+CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
+  INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    P        N    P
+-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
+CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
+  INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    P        P    N
+-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
+CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+ERROR:  function tf1p(anyarray, anyelement) does not exist
+CREATE AGGREGATE myaggp19b(BASETYPE = anyelement, SFUNC = tf1p,
+  STYPE = anyarray, INITCOND = '{}');
+ERROR:  function tf1p(anyarray, anyelement) does not exist
+--    P    P        P    P
+-- should CREATE
+CREATE AGGREGATE myaggp20a(BASETYPE = anyelement, SFUNC = tfp,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp,
+  STYPE = anyarray, INITCOND = '{}');
+--     Case3 (R = N) && (B = A)
+--     ------------------------
+--     S    tf1
+--     -------
+--     N    N
+-- should CREATE
+CREATE AGGREGATE myaggn01a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn01b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[],
+  INITCOND = '{}');
+--     P    N
+-- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
+CREATE AGGREGATE myaggn02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+CREATE AGGREGATE myaggn02b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
+  INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--     N    P
+-- should CREATE
+CREATE AGGREGATE myaggn03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+--     P    P
+-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
+CREATE AGGREGATE myaggn04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    Case4 (R = N) && ((B = P) || (B = N))
+--    -------------------------------------
+--    S    tf1      B    tf2
+--    -----------------------
+--    N    N        N    N
+-- should CREATE
+CREATE AGGREGATE myaggn05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn05b(BASETYPE = int, SFUNC = tfnp, STYPE = int[],
+  INITCOND = '{}');
+--    N    N        N    P
+-- should CREATE
+CREATE AGGREGATE myaggn06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn06b(BASETYPE = int, SFUNC = tf2p, STYPE = int[],
+  INITCOND = '{}');
+--    N    N        P    N
+-- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int)
+CREATE AGGREGATE myaggn07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function tfnp(integer[], anyelement) does not exist
+CREATE AGGREGATE myaggn07b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[],
+  INITCOND = '{}');
+ERROR:  function tfnp(integer[], anyelement) does not exist
+--    N    N        P    P
+-- should CREATE
+CREATE AGGREGATE myaggn08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn08b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[],
+  INITCOND = '{}');
+--    N    P        N    N
+-- should CREATE
+CREATE AGGREGATE myaggn09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+--    N    P        N    P
+-- should CREATE
+CREATE AGGREGATE myaggn10a(BASETYPE = int, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+--    N    P        P    N
+-- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int)
+CREATE AGGREGATE myaggn11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function tf1p(integer[], anyelement) does not exist
+--    N    P        P    P
+-- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement)
+CREATE AGGREGATE myaggn12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function tfp(integer[], anyelement) does not exist
+--    P    N        N    N
+-- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
+  INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    N        N    P
+-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
+  INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    N        P    N
+-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function tfnp(anyarray, anyelement) does not exist
+CREATE AGGREGATE myaggn15b(BASETYPE = anyelement, SFUNC = tfnp,
+  STYPE = anyarray, INITCOND = '{}');
+ERROR:  function tfnp(anyarray, anyelement) does not exist
+--    P    N        P    P
+-- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggn16a(BASETYPE = anyelement, SFUNC = tf2p,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function tf2p(anyarray, anyelement) does not exist
+CREATE AGGREGATE myaggn16b(BASETYPE = anyelement, SFUNC = tf2p,
+  STYPE = anyarray, INITCOND = '{}');
+ERROR:  function tf2p(anyarray, anyelement) does not exist
+--    P    P        N    N
+-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
+CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    P        N    P
+-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
+CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  cannot determine transition data type
+DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have one of them as its base type.
+--    P    P        P    N
+-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
+CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function tf1p(anyarray, anyelement) does not exist
+--    P    P        P    P
+-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
+CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+ERROR:  function ffnp(anyarray) does not exist
+-- create test data for polymorphic aggregates
+create temp table t(f1 int, f2 int[], f3 text);
+insert into t values(1,array[1],'a');
+insert into t values(1,array[11],'b');
+insert into t values(1,array[111],'c');
+insert into t values(2,array[2],'a');
+insert into t values(2,array[22],'b');
+insert into t values(2,array[222],'c');
+insert into t values(3,array[3],'a');
+insert into t values(3,array[3],'b');
+-- test the successfully created polymorphic aggregates
+select f3, myaggp01a(*) from t group by f3;
+ f3 | myaggp01a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp03a(*) from t group by f3;
+ f3 | myaggp03a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp03b(*) from t group by f3;
+ f3 | myaggp03b 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp05a(f1) from t group by f3;
+ f3 | myaggp05a 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggp06a(f1) from t group by f3;
+ f3 | myaggp06a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp08a(f1) from t group by f3;
+ f3 | myaggp08a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp09a(f1) from t group by f3;
+ f3 | myaggp09a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp09b(f1) from t group by f3;
+ f3 | myaggp09b 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggp10a(f1) from t group by f3;
+ f3 | myaggp10a 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggp10b(f1) from t group by f3;
+ f3 | myaggp10b 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggp20a(f1) from t group by f3;
+ f3 | myaggp20a 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggp20b(f1) from t group by f3;
+ f3 | myaggp20b 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggn01a(*) from t group by f3;
+ f3 | myaggn01a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn01b(*) from t group by f3;
+ f3 | myaggn01b 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn03a(*) from t group by f3;
+ f3 | myaggn03a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn05a(f1) from t group by f3;
+ f3 | myaggn05a 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggn05b(f1) from t group by f3;
+ f3 | myaggn05b 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+select f3, myaggn06a(f1) from t group by f3;
+ f3 | myaggn06a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn06b(f1) from t group by f3;
+ f3 | myaggn06b 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn08a(f1) from t group by f3;
+ f3 | myaggn08a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn08b(f1) from t group by f3;
+ f3 | myaggn08b 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn09a(f1) from t group by f3;
+ f3 | myaggn09a 
+----+-----------
+ b  | {}
+ c  | {}
+ a  | {}
+(3 rows)
+
+select f3, myaggn10a(f1) from t group by f3;
+ f3 | myaggn10a 
+----+-----------
+ b  | {1,2,3}
+ c  | {1,2}
+ a  | {1,2,3}
+(3 rows)
+
+-- test inlining of polymorphic SQL functions
+create function bleat(int) returns int as '
+begin
+  raise notice ''bleat %'', $1;
+  return $1;
+end' language plpgsql;
+create function sql_if(bool, anyelement, anyelement) returns anyelement as '
+select case when $1 then $2 else $3 end ' language sql;
+-- Note this would fail with integer overflow, never mind wrong bleat() output,
+-- if the CASE expression were not successfully inlined
+select f1, sql_if(f1 > 0, bleat(f1), bleat(f1 + 1)) from int4_tbl;
+NOTICE:  bleat 1
+NOTICE:  bleat 123456
+NOTICE:  bleat -123455
+NOTICE:  bleat 2147483647
+NOTICE:  bleat -2147483646
+     f1      |   sql_if    
+-------------+-------------
+           0 |           1
+      123456 |      123456
+     -123456 |     -123455
+  2147483647 |  2147483647
+ -2147483647 | -2147483646
+(5 rows)
+
+select q2, sql_if(q2 > 0, q2, q2 + 1) from int8_tbl;
+        q2         |      sql_if       
+-------------------+-------------------
+               456 |               456
+  4567890123456789 |  4567890123456789
+               123 |               123
+  4567890123456789 |  4567890123456789
+ -4567890123456789 | -4567890123456788
+(5 rows)
+
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
new file mode 100644 (file)
index 0000000..d3a5cd1
--- /dev/null
@@ -0,0 +1,383 @@
+-- Currently this tests polymorphic aggregates and indirectly does some
+-- testing of polymorphic SQL functions.  It ought to be extended.
+
+
+-- Legend:
+-----------
+-- A = type is ANY
+-- P = type is polymorphic
+-- N = type is non-polymorphic
+-- B = aggregate base type
+-- S = aggregate state type
+-- R = aggregate return type
+-- 1 = arg1 of a function
+-- 2 = arg2 of a function
+-- ag = aggregate
+-- tf = trans (state) function
+-- ff = final function
+-- rt = return type of a function
+-- -> = implies
+-- => = allowed
+-- !> = not allowed
+-- E  = exists
+-- NE = not-exists
+-- 
+-- Possible states:
+-- ----------------
+-- B = (A || P || N)
+--   when (B = A) -> (tf2 = NE)
+-- S = (P || N)
+-- ff = (E || NE)
+-- tf1 = (P || N)
+-- tf2 = (NE || P || N)
+-- R = (P || N)
+
+-- create functions for use as tf and ff with the needed combinations of
+-- argument polymorphism, but within the constraints of valid aggregate
+-- functions, i.e. tf arg1 and tf return type must match
+
+-- polymorphic single arg transfn
+CREATE FUNCTION stfp(anyarray) returns anyarray as
+'select $1' language 'sql';
+-- non-polymorphic single arg transfn
+CREATE FUNCTION stfnp(int[]) returns int[] as
+'select $1' language 'sql';
+
+-- dual polymorphic transfn
+CREATE FUNCTION tfp(anyarray,anyelement) returns anyarray as
+'select $1 || $2' language 'sql';
+-- dual non-polymorphic transfn
+CREATE FUNCTION tfnp(int[],int) returns int[] as
+'select $1 || $2' language 'sql';
+
+-- arg1 only polymorphic transfn
+CREATE FUNCTION tf1p(anyarray,int) returns anyarray as
+'select $1' language 'sql';
+-- arg2 only polymorphic transfn
+CREATE FUNCTION tf2p(int[],anyelement) returns int[] as
+'select $1' language 'sql';
+
+-- finalfn polymorphic
+CREATE FUNCTION ffp(anyarray) returns anyarray as
+'select $1' language 'sql';
+-- finalfn non-polymorphic
+CREATE FUNCTION ffnp(int[]) returns int[] as
+'select $1' language 'sql';
+
+-- Try to cover all the possible states:
+-- 
+-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
+-- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
+-- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
+-- return N. Therefore, if the transfn is stfp, tfp, or tf1p, we must use ffnp
+-- as finalfn, because stfp, tfp, and tf1p do not return N.
+--
+--     Case1 (R = P) && (B = A)
+--     ------------------------
+--     S    tf1
+--     -------
+--     N    N
+-- should CREATE
+CREATE AGGREGATE myaggp01a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[],
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--     P    N
+-- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
+CREATE AGGREGATE myaggp02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--     N    P
+-- should CREATE
+CREATE AGGREGATE myaggp03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp03b(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[],
+  INITCOND = '{}');
+
+--     P    P
+-- should ERROR: we have no way to resolve S
+CREATE AGGREGATE myaggp04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp04b(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
+  INITCOND = '{}');
+
+
+--    Case2 (R = P) && ((B = P) || (B = N))
+--    -------------------------------------
+--    S    tf1      B    tf2
+--    -----------------------
+--    N    N        N    N
+-- should CREATE
+CREATE AGGREGATE myaggp05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--    N    N        N    P
+-- should CREATE
+CREATE AGGREGATE myaggp06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--    N    N        P    N
+-- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int)
+CREATE AGGREGATE myaggp07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--    N    N        P    P
+-- should CREATE
+CREATE AGGREGATE myaggp08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--    N    P        N    N
+-- should CREATE
+CREATE AGGREGATE myaggp09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp09b(BASETYPE = int, SFUNC = tf1p, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    P        N    P
+-- should CREATE
+CREATE AGGREGATE myaggp10a(BASETYPE = int, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp10b(BASETYPE = int, SFUNC = tfp, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    P        P    N
+-- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int)
+CREATE AGGREGATE myaggp11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp11b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    P        P    P
+-- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement)
+CREATE AGGREGATE myaggp12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp12b(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[],
+  INITCOND = '{}');
+
+--    P    N        N    N
+-- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--    P    N        N    P
+-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+
+--    P    N        P    N
+-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+
+--    P    N        P    P
+-- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggp16a(BASETYPE = anyelement, SFUNC = tf2p,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+
+--    P    P        N    N
+-- should ERROR: we have no way to resolve S
+CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
+  INITCOND = '{}');
+
+--    P    P        N    P
+-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
+CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
+  FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
+  INITCOND = '{}');
+
+--    P    P        P    N
+-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
+CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp19b(BASETYPE = anyelement, SFUNC = tf1p,
+  STYPE = anyarray, INITCOND = '{}');
+
+--    P    P        P    P
+-- should CREATE
+CREATE AGGREGATE myaggp20a(BASETYPE = anyelement, SFUNC = tfp,
+  STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
+CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp,
+  STYPE = anyarray, INITCOND = '{}');
+
+--     Case3 (R = N) && (B = A)
+--     ------------------------
+--     S    tf1
+--     -------
+--     N    N
+-- should CREATE
+CREATE AGGREGATE myaggn01a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn01b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[],
+  INITCOND = '{}');
+
+--     P    N
+-- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
+CREATE AGGREGATE myaggn02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn02b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray,
+  INITCOND = '{}');
+
+--     N    P
+-- should CREATE
+CREATE AGGREGATE myaggn03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--     P    P
+-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
+CREATE AGGREGATE myaggn04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+
+--    Case4 (R = N) && ((B = P) || (B = N))
+--    -------------------------------------
+--    S    tf1      B    tf2
+--    -----------------------
+--    N    N        N    N
+-- should CREATE
+CREATE AGGREGATE myaggn05a(BASETYPE = int, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn05b(BASETYPE = int, SFUNC = tfnp, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    N        N    P
+-- should CREATE
+CREATE AGGREGATE myaggn06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn06b(BASETYPE = int, SFUNC = tf2p, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    N        P    N
+-- should ERROR: tfnp(int[], anyelement) not matched by tfnp(int[], int)
+CREATE AGGREGATE myaggn07a(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn07b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    N        P    P
+-- should CREATE
+CREATE AGGREGATE myaggn08a(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn08b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[],
+  INITCOND = '{}');
+
+--    N    P        N    N
+-- should CREATE
+CREATE AGGREGATE myaggn09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--    N    P        N    P
+-- should CREATE
+CREATE AGGREGATE myaggn10a(BASETYPE = int, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--    N    P        P    N
+-- should ERROR: tf1p(int[],anyelement) not matched by tf1p(anyarray,int)
+CREATE AGGREGATE myaggn11a(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--    N    P        P    P
+-- should ERROR: tfp(int[],anyelement) not matched by tfp(anyarray,anyelement)
+CREATE AGGREGATE myaggn12a(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[],
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--    P    N        N    N
+-- should ERROR: tfnp(anyarray, int) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
+  INITCOND = '{}');
+
+--    P    N        N    P
+-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
+  INITCOND = '{}');
+
+--    P    N        P    N
+-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
+CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn15b(BASETYPE = anyelement, SFUNC = tfnp,
+  STYPE = anyarray, INITCOND = '{}');
+
+--    P    N        P    P
+-- should ERROR: tf2p(anyarray, anyelement) not matched by tf2p(int[],anyelement)
+CREATE AGGREGATE myaggn16a(BASETYPE = anyelement, SFUNC = tf2p,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+CREATE AGGREGATE myaggn16b(BASETYPE = anyelement, SFUNC = tf2p,
+  STYPE = anyarray, INITCOND = '{}');
+
+--    P    P        N    N
+-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
+CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--    P    P        N    P
+-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
+CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
+  FINALFUNC = ffnp, INITCOND = '{}');
+
+--    P    P        P    N
+-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
+CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+
+--    P    P        P    P
+-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
+CREATE AGGREGATE myaggn20a(BASETYPE = anyelement, SFUNC = tfp,
+  STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
+
+-- create test data for polymorphic aggregates
+create temp table t(f1 int, f2 int[], f3 text);
+insert into t values(1,array[1],'a');
+insert into t values(1,array[11],'b');
+insert into t values(1,array[111],'c');
+insert into t values(2,array[2],'a');
+insert into t values(2,array[22],'b');
+insert into t values(2,array[222],'c');
+insert into t values(3,array[3],'a');
+insert into t values(3,array[3],'b');
+
+-- test the successfully created polymorphic aggregates
+select f3, myaggp01a(*) from t group by f3;
+select f3, myaggp03a(*) from t group by f3;
+select f3, myaggp03b(*) from t group by f3;
+select f3, myaggp05a(f1) from t group by f3;
+select f3, myaggp06a(f1) from t group by f3;
+select f3, myaggp08a(f1) from t group by f3;
+select f3, myaggp09a(f1) from t group by f3;
+select f3, myaggp09b(f1) from t group by f3;
+select f3, myaggp10a(f1) from t group by f3;
+select f3, myaggp10b(f1) from t group by f3;
+select f3, myaggp20a(f1) from t group by f3;
+select f3, myaggp20b(f1) from t group by f3;
+select f3, myaggn01a(*) from t group by f3;
+select f3, myaggn01b(*) from t group by f3;
+select f3, myaggn03a(*) from t group by f3;
+select f3, myaggn05a(f1) from t group by f3;
+select f3, myaggn05b(f1) from t group by f3;
+select f3, myaggn06a(f1) from t group by f3;
+select f3, myaggn06b(f1) from t group by f3;
+select f3, myaggn08a(f1) from t group by f3;
+select f3, myaggn08b(f1) from t group by f3;
+select f3, myaggn09a(f1) from t group by f3;
+select f3, myaggn10a(f1) from t group by f3;
+
+-- test inlining of polymorphic SQL functions
+create function bleat(int) returns int as '
+begin
+  raise notice ''bleat %'', $1;
+  return $1;
+end' language plpgsql;
+
+create function sql_if(bool, anyelement, anyelement) returns anyelement as '
+select case when $1 then $2 else $3 end ' language sql;
+
+-- Note this would fail with integer overflow, never mind wrong bleat() output,
+-- if the CASE expression were not successfully inlined
+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;