Обсуждение: Re: [HACKERS] Missing array support

Поиск
Список
Период
Сортировка

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> If I were you I'd file this on the to-fix-later list and concentrate
> on polymorphic aggregates during the next couple days.  If that's not
> done by Tuesday it will be a tough sell to put in during beta.

Attached is a patch that implements polymorphic aggregates.

Included in the patch, I changed SQL language functions so that they
could be declared with and use polymorphic types. This was necessary to
facilitate my testing, and I had wanted to implement that all along
anyway (in fact I'd still like to allow PL/pgSQL to use polymorphic
types, but I'll try to do that separately).

The attached compiles cleanly and passes all regression tests. I've also
attached a test script and its output that I used to verify appropriate
behavior of CREATE AGGREGATE. The script attempts to cover all possible
combinations of inputs and outputs (wrt polymorphic vs non-polymorphic).
As far as I can see, the behaviors look correct.

Not sure if it makes sense to do so, but I could either add the test
script to an existing regression test, or create a new one with it if
desired.

If there are no objections, please apply.

Thanks,

Joe
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.58
diff -c -r1.58 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    25 Jun 2003 21:30:25 -0000    1.58
--- src/backend/catalog/pg_aggregate.c    29 Jun 2003 19:17:47 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,137 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     /*
!      * func_get_detail will find functions requiring argument type coercion,
!      * but we aren't prepared to deal with that
!      */
!     if (true_oid_array_transfn[0] != ANYARRAYOID &&
!         true_oid_array_transfn[0] != ANYELEMENTOID &&
!         !IsBinaryCoercible(fnArgs[0], true_oid_array_transfn[0]))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     if (nargs_transfn == 2 &&
!         true_oid_array_transfn[1] != ANYARRAYOID &&
!         true_oid_array_transfn[1] != ANYELEMENTOID &&
!         !IsBinaryCoercible(fnArgs[1], true_oid_array_transfn[1]))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
          if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
-         proc = (Form_pg_proc) GETSTRUCT(tup);
-         finaltype = proc->prorettype;
-         ReleaseSysCache(tup);
      }
      else
      {
--- 151,185 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
!             func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
          if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
!
!         /*
!          * func_get_detail will find functions requiring argument type coercion,
!          * but we aren't prepared to deal with that
!          */
!         if (true_oid_array_finalfn[0] != ANYARRAYOID &&
!             true_oid_array_finalfn[0] != ANYELEMENTOID &&
!             !IsBinaryCoercible(fnArgs[0], true_oid_array_finalfn[0]))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
      }
      else
      {
***************
*** 125,130 ****
--- 189,222 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)  if finaltype (i.e. aggregate return type) is polymorphic,
+      *     basetype must be polymorphic also
+      * 2)  if finaltype (i.e. aggregate return type) is non-polymorphic,
+      *     and transition function's second argument is non-polymorphic, then
+      *     the transition function's first argument may not be polymorphic
+      *     unless the state type is non-polymorphic
+      */
+     if ((finaltype == ANYARRAYOID ||
+          finaltype == ANYELEMENTOID) &&
+         (aggBaseType != ANYARRAYOID &&
+          aggBaseType != ANYELEMENTOID))
+         elog(ERROR, "an aggregate returning ANYARRAY or ANYELEMENT " \
+                     "must also have either of the them as its base type");
+
+
+     if ((finaltype != ANYARRAYOID &&
+          finaltype != ANYELEMENTOID) &&                    /* rt non-poly */
+         (true_oid_array_transfn[0] == ANYARRAYOID ||
+          true_oid_array_transfn[0] == ANYELEMENTOID) && /* tf arg1 poly */
+         (true_oid_array_transfn[1] != ANYARRAYOID &&
+          true_oid_array_transfn[1] != ANYELEMENTOID) && /* tf arg2 non-poly */
+         (aggTransType == ANYARRAYOID ||
+          aggTransType == ANYELEMENTOID))                /* st arg1 poly */
+         elog(ERROR, "the state function's first argument is ambiguous in a " \
+                     "context that cannot support it");

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/catalog/pg_proc.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_proc.c,v
retrieving revision 1.97
diff -c -r1.97 pg_proc.c
*** src/backend/catalog/pg_proc.c    15 Jun 2003 17:59:10 -0000    1.97
--- src/backend/catalog/pg_proc.c    29 Jun 2003 16:26:36 -0000
***************
*** 377,383 ****

      typerelid = typeidTypeRelid(rettype);

!     if (fn_typtype == 'b' || fn_typtype == 'd')
      {
          /* Shouldn't have a typerelid */
          Assert(typerelid == InvalidOid);
--- 377,386 ----

      typerelid = typeidTypeRelid(rettype);

!     if (fn_typtype == 'b' ||
!         fn_typtype == 'd' ||
!         (fn_typtype == 'p' && rettype == ANYARRAYOID) ||
!         (fn_typtype == 'p' && rettype == ANYELEMENTOID))
      {
          /* Shouldn't have a typerelid */
          Assert(typerelid == InvalidOid);
***************
*** 595,610 ****
      functyptype = get_typtype(proc->prorettype);

      /* Disallow pseudotypes in arguments and result */
!     /* except that return type can be RECORD or VOID */
      if (functyptype == 'p' &&
          proc->prorettype != RECORDOID &&
!         proc->prorettype != VOIDOID)
          elog(ERROR, "SQL functions cannot return type %s",
               format_type_be(proc->prorettype));

      for (i = 0; i < proc->pronargs; i++)
      {
!         if (get_typtype(proc->proargtypes[i]) == 'p')
              elog(ERROR, "SQL functions cannot have arguments of type %s",
                   format_type_be(proc->proargtypes[i]));
      }
--- 598,617 ----
      functyptype = get_typtype(proc->prorettype);

      /* Disallow pseudotypes in arguments and result */
!     /* except that return type can be RECORD, VOID, ANYARRAY, or ANYELEMENT */
      if (functyptype == 'p' &&
          proc->prorettype != RECORDOID &&
!         proc->prorettype != VOIDOID &&
!         proc->prorettype != ANYARRAYOID &&
!         proc->prorettype != ANYELEMENTOID)
          elog(ERROR, "SQL functions cannot return type %s",
               format_type_be(proc->prorettype));

      for (i = 0; i < proc->pronargs; i++)
      {
!         if (get_typtype(proc->proargtypes[i]) == 'p' &&
!                         proc->proargtypes[i] != ANYARRAYOID &&
!                         proc->proargtypes[i] != ANYELEMENTOID)
              elog(ERROR, "SQL functions cannot have arguments of type %s",
                   format_type_be(proc->proargtypes[i]));
      }
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.8
diff -c -r1.8 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    27 Jun 2003 14:45:27 -0000    1.8
--- src/backend/commands/aggregatecmds.c    29 Jun 2003 16:26:36 -0000
***************
*** 120,126 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 120,128 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/functions.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/functions.c,v
retrieving revision 1.66
diff -c -r1.66 functions.c
*** src/backend/executor/functions.c    12 Jun 2003 17:29:26 -0000    1.66
--- src/backend/executor/functions.c    29 Jun 2003 16:26:36 -0000
***************
*** 20,25 ****
--- 20,26 ----
  #include "executor/execdefs.h"
  #include "executor/executor.h"
  #include "executor/functions.h"
+ #include "parser/parse_expr.h"
  #include "tcop/pquery.h"
  #include "tcop/tcopprot.h"
  #include "tcop/utility.h"
***************
*** 212,221 ****

      if (nargs > 0)
      {
          argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
!         memcpy(argOidVect,
!                procedureStruct->proargtypes,
!                nargs * sizeof(Oid));
      }
      else
          argOidVect = (Oid *) NULL;
--- 213,235 ----

      if (nargs > 0)
      {
+         List   *p;
+         int        argnum = 0;
+
          argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
!         if (finfo->fn_expr)
!         {
!             /*
!              * If we have a function expression node available to us
!              * use it, as any polymorphic types should have been
!              * disambiguated for us already
!              */
!             foreach(p, ((FuncExpr *) finfo->fn_expr)->args)
!                 argOidVect[argnum++] = exprType((Node *) lfirst(p));
!         }
!         else
!             memcpy(argOidVect, procedureStruct->proargtypes,
!                                             nargs * sizeof(Oid));
      }
      else
          argOidVect = (Oid *) NULL;
Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.109
diff -c -r1.109 nodeAgg.c
*** src/backend/executor/nodeAgg.c    25 Jun 2003 21:30:28 -0000    1.109
--- src/backend/executor/nodeAgg.c    29 Jun 2003 16:26:36 -0000
***************
*** 59,64 ****
--- 59,65 ----
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
  #include "optimizer/clauses.h"
+ #include "parser/parse_agg.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
  #include "parser/parse_oper.h"
***************
*** 1187,1193 ****
--- 1188,1199 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_basetype;
+         Oid            transfn_arg1_type;
          int            i;

          /* Planner should have assigned aggregate to correct level */
***************
*** 1238,1243 ****
--- 1244,1274 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_basetype = exprType((Node *) nth(0, fargs));
+
+         expand_aggregate(agg_rt_basetype,
+                          aggform->aggtranstype,
+                          aggref->aggfnoid,
+                          transfn_oid,
+                          finalfn_oid,
+                          &transfnexpr,
+                          &finalfnexpr,
+                          &transfn_arg1_type);
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1250,1263 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1281,1287 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg1_type);

          /*
           * If the transfn is strict and the initval is NULL, make sure
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.258
diff -c -r1.258 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    29 Jun 2003 00:33:43 -0000    1.258
--- src/backend/nodes/copyfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 728,733 ****
--- 728,734 ----
      COPY_SCALAR_FIELD(agglevelsup);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.201
diff -c -r1.201 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    29 Jun 2003 00:33:43 -0000    1.201
--- src/backend/nodes/equalfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 205,210 ****
--- 205,211 ----
      COMPARE_SCALAR_FIELD(agglevelsup);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.211
diff -c -r1.211 outfuncs.c
*** src/backend/nodes/outfuncs.c    29 Jun 2003 00:33:43 -0000    1.211
--- src/backend/nodes/outfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 616,621 ****
--- 616,622 ----
      WRITE_UINT_FIELD(agglevelsup);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.157
diff -c -r1.157 readfuncs.c
*** src/backend/nodes/readfuncs.c    29 Jun 2003 00:33:43 -0000    1.157
--- src/backend/nodes/readfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 416,421 ****
--- 416,422 ----
      READ_UINT_FIELD(agglevelsup);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.142
diff -c -r1.142 clauses.c
*** src/backend/optimizer/util/clauses.c    29 Jun 2003 00:33:43 -0000    1.142
--- src/backend/optimizer/util/clauses.c    29 Jun 2003 16:26:36 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/parse_agg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_agg.c,v
retrieving revision 1.53
diff -c -r1.53 parse_agg.c
*** src/backend/parser/parse_agg.c    6 Jun 2003 15:04:02 -0000    1.53
--- src/backend/parser/parse_agg.c    29 Jun 2003 16:26:36 -0000
***************
*** 14,25 ****
--- 14,29 ----
   */
  #include "postgres.h"

+ #include "catalog/pg_type.h"
+ #include "nodes/params.h"
  #include "optimizer/clauses.h"
  #include "optimizer/tlist.h"
  #include "optimizer/var.h"
  #include "parser/parse_agg.h"
+ #include "parser/parse_type.h"
  #include "parser/parsetree.h"
  #include "rewrite/rewriteManip.h"
+ #include "utils/lsyscache.h"


  typedef struct
***************
*** 312,314 ****
--- 316,539 ----
      return expression_tree_walker(node, check_ungrouped_columns_walker,
                                    (void *) context);
  }
+
+ /*
+  * Create function expressions for the transition and final functions
+  * of an aggregate so that they can be attached to the FmgrInfo nodes
+  * of AggStatePerAgg. If we didn't, the functions would not be able to
+  * know what argument and return data types to use for any that are
+  * polymorphic in its definition.
+  */
+ void
+ expand_aggregate(Oid agg_rt_basetype,
+                  Oid agg_statetype,
+                  Oid agg_fnoid,
+                  Oid transfn_oid,
+                  Oid finalfn_oid,
+                  FuncExpr **transfnexpr,
+                  FuncExpr **finalfnexpr,
+                  Oid *transfn_arg1_type)
+ {
+     Oid           *transfn_arg_types;
+     List       *transfn_args = NIL;
+     int            transfn_nargs;
+     Oid            transfn_ret_type;
+     Oid           *finalfn_arg_types = NULL;
+     List       *finalfn_args = NIL;
+     Oid            finalfn_ret_type = InvalidOid;
+     int            finalfn_nargs = 0;
+     Param       *arg0;
+     Param       *arg1;
+
+     /* get the transition function argument and return types */
+     transfn_ret_type = get_func_rettype(transfn_oid);
+     transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+     /* resolve any polymorphic types */
+     if (transfn_nargs == 2)
+     {
+         /* base type was not ANY */
+         if ((transfn_arg_types[0] == ANYARRAYOID ||
+              transfn_arg_types[0] == ANYELEMENTOID) &&
+             (transfn_arg_types[1] == ANYARRAYOID ||
+              transfn_arg_types[1] == ANYELEMENTOID))
+         {
+             /*
+              * If both transfn args are polymorphic, we can
+              * resolve transfn arg 1 using base type as context
+              */
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                     agg_rt_basetype);
+         }
+         else if ((transfn_arg_types[0] == ANYARRAYOID ||
+                   transfn_arg_types[0] == ANYELEMENTOID))
+         {
+             /*
+              * Otherwise, if transfn arg 1 is polymorphic, we can
+              * resolve it using state type as context. This is only
+              * safe because we prevented the situation where both
+              * state type and transfn arg 1 are polymorphic with a
+              * non-polymorphic transfn arg 2, during aggregate creation.
+              */
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_statetype);
+         }
+
+         /*
+          * Now, if transfn arg 2 is polymorphic, we can set it to the runtime
+          * base type without further adieu
+          */
+         if (transfn_arg_types[1] == ANYARRAYOID ||
+             transfn_arg_types[1] == ANYELEMENTOID)
+             transfn_arg_types[1] = agg_rt_basetype;
+
+         /*
+          * Build arg list to use on the transfn FuncExpr node. We really
+          * only care that transfn can discover the actual argument types
+          * at runtime using get_fn_expr_argtype()
+          */
+         arg0 = makeNode(Param);
+         arg0->paramkind = PARAM_EXEC;
+         arg0->paramid = -1;
+         arg0->paramtype = transfn_arg_types[0];
+
+         arg1 = makeNode(Param);
+         arg1->paramkind = PARAM_EXEC;
+         arg1->paramid = -1;
+         arg1->paramtype = transfn_arg_types[1];
+
+         transfn_args = makeList2(arg0, arg1);
+
+         /*
+          * the state transition function always returns the same type
+          * as its first argument
+          */
+         if (transfn_ret_type == ANYARRAYOID ||
+             transfn_ret_type == ANYELEMENTOID)
+             transfn_ret_type = transfn_arg_types[0];
+     }
+     else if (transfn_nargs == 1)
+     /*
+      * base type was ANY, therefore the aggregate return type should
+      * be non-polymorphic
+      */
+     {
+         Oid    finaltype = get_func_rettype(agg_fnoid);
+
+         /*
+          * this should have been prevented in AggregateCreate,
+          * but check anyway
+          */
+         if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+             elog(ERROR, "an aggregate returning ANYARRAY or ANYELEMENT " \
+                         "must also have either of the them as its base type");
+
+         /* see if we have a final function */
+         if (OidIsValid(finalfn_oid))
+         {
+             finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+             if (finalfn_nargs != 1)
+                 elog(ERROR, "final function takes unexpected number " \
+                             "of arguments: %d", finalfn_nargs);
+
+             /*
+              * final function argument is always the same as the state
+              * function return type
+              */
+             if (finalfn_arg_types[0] != ANYARRAYOID &&
+                 finalfn_arg_types[0] != ANYELEMENTOID)
+             {
+                 /* if it is not ambiguous, use it */
+                 transfn_ret_type = finalfn_arg_types[0];
+             }
+             else
+             {
+                 /* if it is ambiguous, try to derive it */
+                 finalfn_ret_type = finaltype;
+                 finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                         finalfn_ret_type);
+                 transfn_ret_type = finalfn_arg_types[0];
+             }
+         }
+         else
+             transfn_ret_type = finaltype;
+
+         transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                 transfn_ret_type);
+
+         /*
+          * Build arg list to use on the transfn FuncExpr node. We really
+          * only care that transfn can discover the actual argument types
+          * at runtime using get_fn_expr_argtype()
+          */
+         arg0 = makeNode(Param);
+         arg0->paramkind = PARAM_EXEC;
+         arg0->paramid = -1;
+         arg0->paramtype = transfn_arg_types[0];
+
+         transfn_args = makeList1(arg0);
+     }
+     else
+         elog(ERROR, "state transition function takes unexpected number " \
+                     "of arguments: %d", transfn_nargs);
+
+     if (OidIsValid(finalfn_oid))
+     {
+         /* get the final function argument and return types */
+         if (finalfn_ret_type == InvalidOid)
+             finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+         if (!finalfn_arg_types)
+         {
+             finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+             if (finalfn_nargs != 1)
+                 elog(ERROR, "final function takes unexpected number " \
+                             "of arguments: %d", finalfn_nargs);
+         }
+
+         /*
+          * final function argument is always the same as the state
+          * function return type, which by now should have been resolved
+          */
+         if (finalfn_arg_types[0] == ANYARRAYOID ||
+             finalfn_arg_types[0] == ANYELEMENTOID)
+             finalfn_arg_types[0] = transfn_ret_type;
+
+         /*
+          * Build arg list to use on the finalfn FuncExpr node. We really
+          * only care that finalfn can discover the actual argument types
+          * at runtime using get_fn_expr_argtype()
+          */
+         arg0 = makeNode(Param);
+         arg0->paramkind = PARAM_EXEC;
+         arg0->paramid = -1;
+         arg0->paramtype = finalfn_arg_types[0];
+
+         finalfn_args = makeList1(arg0);
+
+         finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                         finalfn_arg_types[0]);
+     }
+
+    *transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                                                transfn_ret_type,
+                                                false,
+                                                COERCE_DONTCARE,
+                                                transfn_args);
+
+     if (OidIsValid(finalfn_oid))
+     {
+        *finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                                                    finalfn_ret_type,
+                                                    false,
+                                                    COERCE_DONTCARE,
+                                                    finalfn_args);
+     }
+
+     /*
+      * we need to return the resolved transfn arg1 type to be used
+      * by GetAggInitVal
+      */
+    *transfn_arg1_type = transfn_arg_types[0];
+ }
+
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.101
diff -c -r2.101 parse_coerce.c
*** src/backend/parser/parse_coerce.c    27 Jun 2003 00:33:25 -0000    2.101
--- src/backend/parser/parse_coerce.c    29 Jun 2003 16:26:36 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.152
diff -c -r1.152 parse_func.c
*** src/backend/parser/parse_func.c    25 Jun 2003 21:30:31 -0000    1.152
--- src/backend/parser/parse_func.c    29 Jun 2003 16:26:36 -0000
***************
*** 336,341 ****
--- 336,342 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          /* parse_agg.c does additional aggregate-specific processing */
          transformAggregateCall(pstate, aggref);
Index: src/backend/parser/parse_type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_type.c,v
retrieving revision 1.57
diff -c -r1.57 parse_type.c
*** src/backend/parser/parse_type.c    29 Apr 2003 22:13:10 -0000    1.57
--- src/backend/parser/parse_type.c    29 Jun 2003 16:26:36 -0000
***************
*** 484,486 ****
--- 484,536 ----

      pfree(buf.data);
  }
+
+ /*
+  * Given a type_to_resolve oid, typically defined at function creation
+  * (e.g. a function argument or return type), and context_type oid,
+  * typically gleaned by the parser as one of the actual arguments
+  * at function call time, derive the runtime type of type_to_resolve.
+  * The intent is to use runtime context to determine what type we should
+  * assign to a polymorphic argument or return type.
+  *
+  * The rules for this resolution are as follows:
+  * 1) if the context type is polymorphic, punt and return type_to_resolve
+  *    unchanged
+  * 2) if type_to_resolve is ANYARRAY (polymorphic), then return context_type
+  *    if it is already an array type, or get its array type if not
+  * 3) if type_to_resolve is ANYELEMENT (polymorphic), then return context_type
+  *    if it is already an elemental type, or get its element type if not
+  * 4) if type_to_resolve is non-polymorphic, return it unchanged
+  */
+ Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
+ }
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.100
diff -c -r1.100 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    27 Jun 2003 00:33:25 -0000    1.100
--- src/backend/utils/cache/lsyscache.c    29 Jun 2003 16:26:36 -0000
***************
*** 719,724 ****
--- 719,758 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.86
diff -c -r1.86 primnodes.h
*** src/include/nodes/primnodes.h    29 Jun 2003 00:33:44 -0000    1.86
--- src/include/nodes/primnodes.h    29 Jun 2003 16:26:36 -0000
***************
*** 226,231 ****
--- 226,232 ----
      Index        agglevelsup;    /* > 0 if agg belongs to outer query */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.65
diff -c -r1.65 clauses.h
*** src/include/optimizer/clauses.h    25 Jun 2003 21:30:33 -0000    1.65
--- src/include/optimizer/clauses.h    29 Jun 2003 16:26:36 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_agg.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_agg.h,v
retrieving revision 1.26
diff -c -r1.26 parse_agg.h
*** src/include/parser/parse_agg.h    6 Jun 2003 15:04:03 -0000    1.26
--- src/include/parser/parse_agg.h    29 Jun 2003 16:26:36 -0000
***************
*** 18,22 ****
--- 18,30 ----
  extern void transformAggregateCall(ParseState *pstate, Aggref *agg);

  extern void parseCheckAggregates(ParseState *pstate, Query *qry);
+ extern void expand_aggregate(Oid agg_rt_basetype,
+                              Oid agg_statetype,
+                              Oid agg_fnoid,
+                              Oid transfn_oid,
+                              Oid finalfn_oid,
+                              FuncExpr **transfnexpr,
+                              FuncExpr **finalfnexpr,
+                              Oid *transfn_arg1_type);

  #endif   /* PARSE_AGG_H */
Index: src/include/parser/parse_type.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_type.h,v
retrieving revision 1.24
diff -c -r1.24 parse_type.h
*** src/include/parser/parse_type.h    31 Aug 2002 22:10:47 -0000    1.24
--- src/include/parser/parse_type.h    29 Jun 2003 16:26:36 -0000
***************
*** 40,45 ****
--- 40,46 ----
  extern Oid    typeidTypeRelid(Oid type_id);

  extern void parseTypeString(const char *str, Oid *type_id, int32 *typmod);
+ extern Oid resolve_type(Oid type_to_resolve, Oid context_type);

  #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)

Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.75
diff -c -r1.75 lsyscache.h
*** src/include/utils/lsyscache.h    27 Jun 2003 00:33:26 -0000    1.75
--- src/include/utils/lsyscache.h    29 Jun 2003 16:26:36 -0000
***************
*** 50,55 ****
--- 50,56 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
-- 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 OR REPLACE FUNCTION stfp(anyarray) returns anyarray as 'select $1' language 'sql';
-- non-polymorphic single arg transfn
CREATE OR REPLACE FUNCTION stfnp(int[]) returns int[] as 'select $1' language 'sql';

-- dual polymorphic transfn
CREATE OR REPLACE FUNCTION tfp(anyarray,anyelement) returns anyarray as 'select $1 || $2' language 'sql';
-- dual non-polymorphic transfn
CREATE OR REPLACE FUNCTION tfnp(int[],int) returns int[] as 'select $1 || $2' language 'sql';

-- arg1 only polymorphic transfn
CREATE OR REPLACE FUNCTION tf1p(anyarray,int) returns anyarray as 'select $1' language 'sql';
-- arg2 only polymorphic transfn
CREATE OR REPLACE FUNCTION tf2p(int[],anyelement) returns int[] as 'select $1' language 'sql';

-- finalfn polymorphic
CREATE OR REPLACE FUNCTION ffp(anyarray) returns anyarray as 'select $1' language 'sql';
-- finalfn non-polymorphic
CREATE OR REPLACE 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 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;

-- 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 OR REPLACE FUNCTION stfp(anyarray) returns anyarray as 'select $1' language 'sql';
CREATE FUNCTION
-- non-polymorphic single arg transfn
CREATE OR REPLACE FUNCTION stfnp(int[]) returns int[] as 'select $1' language 'sql';
CREATE FUNCTION
-- dual polymorphic transfn
CREATE OR REPLACE FUNCTION tfp(anyarray,anyelement) returns anyarray as 'select $1 || $2' language 'sql';
CREATE FUNCTION
-- dual non-polymorphic transfn
CREATE OR REPLACE FUNCTION tfnp(int[],int) returns int[] as 'select $1 || $2' language 'sql';
CREATE FUNCTION
-- arg1 only polymorphic transfn
CREATE OR REPLACE FUNCTION tf1p(anyarray,int) returns anyarray as 'select $1' language 'sql';
CREATE FUNCTION
-- arg2 only polymorphic transfn
CREATE OR REPLACE FUNCTION tf2p(int[],anyelement) returns int[] as 'select $1' language 'sql';
CREATE FUNCTION
-- finalfn polymorphic
CREATE OR REPLACE FUNCTION ffp(anyarray) returns anyarray as 'select $1' language 'sql';
CREATE FUNCTION
-- finalfn non-polymorphic
CREATE OR REPLACE FUNCTION ffnp(int[]) returns int[] as 'select $1' language 'sql';
CREATE FUNCTION
-- 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 = '{}');
CREATE AGGREGATE
--     P    N
-- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
CREATE AGGREGATE myaggp02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
ERROR:  AggregateCreate: function stfnp(anyarray) does not exist
--     N    P
-- should CREATE
CREATE AGGREGATE myaggp03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[], FINALFUNC = ffp, INITCOND = '{}');
CREATE AGGREGATE
CREATE AGGREGATE myaggp03b(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[], INITCOND = '{}');
CREATE AGGREGATE
--     P    P
-- should ERROR: we have no way to resolve S
CREATE AGGREGATE myaggp04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}');
ERROR:  an aggregate returning ANYARRAY or ANYELEMENT must also have either of the them as its base type
CREATE AGGREGATE myaggp04b(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray, INITCOND = '{}');
ERROR:  an aggregate returning ANYARRAY or ANYELEMENT must also have either of the 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 = '{}');
CREATE AGGREGATE
--    N    N        N    P
-- should CREATE
CREATE AGGREGATE myaggp06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}');
CREATE AGGREGATE
--    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:  AggregateCreate: 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 = '{}');
CREATE AGGREGATE
--    N    P        N    N
-- should CREATE
CREATE AGGREGATE myaggp09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}');
CREATE AGGREGATE
CREATE AGGREGATE myaggp09b(BASETYPE = int, SFUNC = tf1p, STYPE = int[], INITCOND = '{}');
CREATE AGGREGATE
--    N    P        N    P
-- should CREATE
CREATE AGGREGATE myaggp10a(BASETYPE = int, SFUNC = tfp, STYPE = int[], FINALFUNC = ffp, INITCOND = '{}');
CREATE AGGREGATE
CREATE AGGREGATE myaggp10b(BASETYPE = int, SFUNC = tfp, STYPE = int[], INITCOND = '{}');
CREATE AGGREGATE
--    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:  AggregateCreate: function tf1p(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp11b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = int[], INITCOND = '{}');
ERROR:  AggregateCreate: 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:  AggregateCreate: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp12b(BASETYPE = anyelement, SFUNC = tfp, STYPE = int[], INITCOND = '{}');
ERROR:  AggregateCreate: 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:  AggregateCreate: function tfnp(anyarray, integer) does not exist
--    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:  AggregateCreate: function tf2p(anyarray, integer) does not exist
--    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:  AggregateCreate: 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:  AggregateCreate: 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:  an aggregate returning ANYARRAY or ANYELEMENT must also have either of the them as its base type
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}');
ERROR:  an aggregate returning ANYARRAY or ANYELEMENT must also have either of the 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:  AggregateCreate: function tfp(anyarray, integer) does not exist
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: function tfp(anyarray, integer) does not exist
--    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:  AggregateCreate: function tf1p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp19b(BASETYPE = anyelement, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: 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
CREATE AGGREGATE myaggp20b(BASETYPE = anyelement, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}');
CREATE AGGREGATE
--     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
CREATE AGGREGATE myaggn01b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = int4[], INITCOND = '{}');
CREATE AGGREGATE
--     P    N
-- should ERROR: stfnp(anyarray) not matched by stfnp(int[])
CREATE AGGREGATE myaggn02a(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
ERROR:  AggregateCreate: function stfnp(anyarray) does not exist
CREATE AGGREGATE myaggn02b(BASETYPE = "ANY", SFUNC = stfnp, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: function stfnp(anyarray) does not exist
--     N    P
-- should CREATE
CREATE AGGREGATE myaggn03a(BASETYPE = "ANY", SFUNC = stfp, STYPE = int4[], FINALFUNC = ffnp, INITCOND = '{}');
CREATE AGGREGATE
--     P    P
-- should ERROR: ffnp(anyarray) not matched by ffnp(int[])
CREATE AGGREGATE myaggn04a(BASETYPE = "ANY", SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}');
ERROR:  AggregateCreate: function ffnp(anyarray) does not exist
--    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
CREATE AGGREGATE myaggn05b(BASETYPE = int, SFUNC = tfnp, STYPE = int[], INITCOND = '{}');
CREATE AGGREGATE
--    N    N        N    P
-- should CREATE
CREATE AGGREGATE myaggn06a(BASETYPE = int, SFUNC = tf2p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}');
CREATE AGGREGATE
CREATE AGGREGATE myaggn06b(BASETYPE = int, SFUNC = tf2p, STYPE = int[], INITCOND = '{}');
CREATE AGGREGATE
--    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:  AggregateCreate: function tfnp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn07b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = int[], INITCOND = '{}');
ERROR:  AggregateCreate: 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
CREATE AGGREGATE myaggn08b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = int[], INITCOND = '{}');
CREATE AGGREGATE
--    N    P        N    N
-- should CREATE
CREATE AGGREGATE myaggn09a(BASETYPE = int, SFUNC = tf1p, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}');
CREATE AGGREGATE
--    N    P        N    P
-- should CREATE
CREATE AGGREGATE myaggn10a(BASETYPE = int, SFUNC = tfp, STYPE = int[], FINALFUNC = ffnp, INITCOND = '{}');
CREATE AGGREGATE
--    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:  AggregateCreate: 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:  AggregateCreate: 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:  AggregateCreate: function tfnp(anyarray, integer) does not exist
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: function tfnp(anyarray, integer) does not exist
--    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:  AggregateCreate: function tf2p(anyarray, integer) does not exist
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: function tf2p(anyarray, integer) does not exist
--    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:  AggregateCreate: function tfnp(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn15b(BASETYPE = anyelement, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: 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:  AggregateCreate: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn16b(BASETYPE = anyelement, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}');
ERROR:  AggregateCreate: 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:  AggregateCreate: function ffnp(anyarray) does not exist
--    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:  AggregateCreate: function tfp(anyarray, integer) does not exist
--    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:  AggregateCreate: 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:  AggregateCreate: function ffnp(anyarray) does not exist
-- create test data for polymorphic aggregates
create table t(f1 int, f2 int[], f3 text);
CREATE TABLE
insert into t values(1,array[1],'a');
INSERT 1253618 1
insert into t values(1,array[11],'b');
INSERT 1253619 1
insert into t values(1,array[111],'c');
INSERT 1253620 1
insert into t values(2,array[2],'a');
INSERT 1253621 1
insert into t values(2,array[22],'b');
INSERT 1253622 1
insert into t values(2,array[222],'c');
INSERT 1253623 1
insert into t values(3,array[3],'a');
INSERT 1253624 1
insert into t values(3,array[3],'b');
INSERT 1253625 1
-- test the successfully created polymorphic aggregates
select f3, myaggp01a(*) from t group by f3;
 f3 | myaggp01a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp03a(*) from t group by f3;
 f3 | myaggp03a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp03b(*) from t group by f3;
 f3 | myaggp03b
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp05a(f1) from t group by f3;
 f3 | myaggp05a
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggp06a(f1) from t group by f3;
 f3 | myaggp06a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp08a(f1) from t group by f3;
 f3 | myaggp08a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp09a(f1) from t group by f3;
 f3 | myaggp09a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp09b(f1) from t group by f3;
 f3 | myaggp09b
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggp10a(f1) from t group by f3;
 f3 | myaggp10a
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggp10b(f1) from t group by f3;
 f3 | myaggp10b
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggp20a(f1) from t group by f3;
 f3 | myaggp20a
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggp20b(f1) from t group by f3;
 f3 | myaggp20b
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggn01a(*) from t group by f3;
 f3 | myaggn01a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn01b(*) from t group by f3;
 f3 | myaggn01b
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn03a(*) from t group by f3;
 f3 | myaggn03a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn05a(f1) from t group by f3;
 f3 | myaggn05a
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggn05b(f1) from t group by f3;
 f3 | myaggn05b
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)

select f3, myaggn06a(f1) from t group by f3;
 f3 | myaggn06a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn06b(f1) from t group by f3;
 f3 | myaggn06b
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn08a(f1) from t group by f3;
 f3 | myaggn08a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn08b(f1) from t group by f3;
 f3 | myaggn08b
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn09a(f1) from t group by f3;
 f3 | myaggn09a
----+-----------
 b  | {}
 a  | {}
 c  | {}
(3 rows)

select f3, myaggn10a(f1) from t group by f3;
 f3 | myaggn10a
----+-----------
 b  | {1,2,3}
 a  | {1,2,3}
 c  | {1,2}
(3 rows)


Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> Included in the patch, I changed SQL language functions so that they
> could be declared with and use polymorphic types.

I'm not convinced that will work ... in particular, does the parsetree
get fixed correctly when a SQL function is inlined?

            regards, tom lane

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>>Included in the patch, I changed SQL language functions so that they
>>could be declared with and use polymorphic types.
>
> I'm not convinced that will work ... in particular, does the parsetree
> get fixed correctly when a SQL function is inlined?
>

I'll try it out. What's the easiest way to be sure the function get's
inlined?

In any case, it's easy enough to rip that part out of the patch -- it
just would have been a lot more painful to test without it.

Joe


Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>
>>Included in the patch, I changed SQL language functions so that they
>>could be declared with and use polymorphic types.
>
> I'm not convinced that will work ... in particular, does the parsetree
> get fixed correctly when a SQL function is inlined?

As far as I can see (and verified through testing), evaluate_function()
is no problem; it passes on the args from the original FuncExpr at about
line 1690 of clauses.c

   newexpr->args = args;

and they get found just fine in init_sql_fcache.

regression=# CREATE OR REPLACE FUNCTION ffp(anyarray) returns anyarray
as 'select $1' language 'sql' strict immutable;
CREATE FUNCTION
regression=# select ffp(array[1]);
NOTICE:  init_sql_fcache: arg 0, oid 1007
NOTICE:  simplify_function: !newexpr = 0, allow_inline = 1
  ffp
-----
  {1}
(1 row)

When the function is defined as above, getting it to try to inline:

regression=# select ffp(array[f]) from (select 1 as f) as ss;
NOTICE:  simplify_function: !newexpr = 1, allow_inline = 1
NOTICE:  inline_function: I'm here
NOTICE:  init_sql_fcache: arg 0, oid 1007
  ffp
-----
  {1}
(1 row)

It doesn't get inlined (as defined above) because it fails this check in
inline_function():

   /* Forget it if declared return type is tuple or void */
   result_typtype = get_typtype(funcform->prorettype);
   if (result_typtype != 'b' &&
       result_typtype != 'd')
       return NULL;

So the only way a problem can arise given the patch I sent, is when the
function accepts polymorphic arguments, but does not return polymorphic:

regression=# drop FUNCTION ffp(anyarray);
DROP FUNCTION
regression=# CREATE OR REPLACE FUNCTION ffp(anyarray) returns int[] as
'select array[1]' language 'sql';
CREATE FUNCTION
regression=# select ffp(array[f]) from (select 1 as f) as ss;
NOTICE:  simplify_function: !newexpr = 1, allow_inline = 1
NOTICE:  inline_function: I'm here
NOTICE:  inline_function: simplified
  ffp
-----
  {1}
(1 row)

So I'd propose that we put another check in inline_function(), and
reject attempts to inline functions with polymorphic arguments. The
other bases are already covered and we already have the proc tuple
available in inline_function(). Sound OK?

Thanks,

Joe


Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Joe Conway wrote:
> Tom Lane wrote:
>> Joe Conway <mail@joeconway.com> writes:
>>> Included in the patch, I changed SQL language functions so that they
>>> could be declared with and use polymorphic types.
>>
>> I'm not convinced that will work ... in particular, does the parsetree
>> get fixed correctly when a SQL function is inlined?
>
> So I'd propose that we put another check in inline_function(), and
> reject attempts to inline functions with polymorphic arguments. The
> other bases are already covered and we already have the proc tuple
> available in inline_function(). Sound OK?
>

Here's another copy of the polymorphic (aggregates + SQL functions)
patch. This one includes the proposed chage above to ensure polymorphic
SQL functions do not get inlined. They can be successfully simplified by
evaluate_function() when appropriate, as I showed in the last post.

Otherwise, it should be the same. Still compiles clean and passes all
regression tests.

Please apply.

Thanks,

Joe
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.58
diff -c -r1.58 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    25 Jun 2003 21:30:25 -0000    1.58
--- src/backend/catalog/pg_aggregate.c    29 Jun 2003 19:17:47 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,137 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     /*
!      * func_get_detail will find functions requiring argument type coercion,
!      * but we aren't prepared to deal with that
!      */
!     if (true_oid_array_transfn[0] != ANYARRAYOID &&
!         true_oid_array_transfn[0] != ANYELEMENTOID &&
!         !IsBinaryCoercible(fnArgs[0], true_oid_array_transfn[0]))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     if (nargs_transfn == 2 &&
!         true_oid_array_transfn[1] != ANYARRAYOID &&
!         true_oid_array_transfn[1] != ANYELEMENTOID &&
!         !IsBinaryCoercible(fnArgs[1], true_oid_array_transfn[1]))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
          if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
-         proc = (Form_pg_proc) GETSTRUCT(tup);
-         finaltype = proc->prorettype;
-         ReleaseSysCache(tup);
      }
      else
      {
--- 151,185 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
!             func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
          if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
!
!         /*
!          * func_get_detail will find functions requiring argument type coercion,
!          * but we aren't prepared to deal with that
!          */
!         if (true_oid_array_finalfn[0] != ANYARRAYOID &&
!             true_oid_array_finalfn[0] != ANYELEMENTOID &&
!             !IsBinaryCoercible(fnArgs[0], true_oid_array_finalfn[0]))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
      }
      else
      {
***************
*** 125,130 ****
--- 189,222 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)  if finaltype (i.e. aggregate return type) is polymorphic,
+      *     basetype must be polymorphic also
+      * 2)  if finaltype (i.e. aggregate return type) is non-polymorphic,
+      *     and transition function's second argument is non-polymorphic, then
+      *     the transition function's first argument may not be polymorphic
+      *     unless the state type is non-polymorphic
+      */
+     if ((finaltype == ANYARRAYOID ||
+          finaltype == ANYELEMENTOID) &&
+         (aggBaseType != ANYARRAYOID &&
+          aggBaseType != ANYELEMENTOID))
+         elog(ERROR, "an aggregate returning ANYARRAY or ANYELEMENT " \
+                     "must also have either of the them as its base type");
+
+
+     if ((finaltype != ANYARRAYOID &&
+          finaltype != ANYELEMENTOID) &&                    /* rt non-poly */
+         (true_oid_array_transfn[0] == ANYARRAYOID ||
+          true_oid_array_transfn[0] == ANYELEMENTOID) && /* tf arg1 poly */
+         (true_oid_array_transfn[1] != ANYARRAYOID &&
+          true_oid_array_transfn[1] != ANYELEMENTOID) && /* tf arg2 non-poly */
+         (aggTransType == ANYARRAYOID ||
+          aggTransType == ANYELEMENTOID))                /* st arg1 poly */
+         elog(ERROR, "the state function's first argument is ambiguous in a " \
+                     "context that cannot support it");

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/catalog/pg_proc.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_proc.c,v
retrieving revision 1.97
diff -c -r1.97 pg_proc.c
*** src/backend/catalog/pg_proc.c    15 Jun 2003 17:59:10 -0000    1.97
--- src/backend/catalog/pg_proc.c    29 Jun 2003 16:26:36 -0000
***************
*** 377,383 ****

      typerelid = typeidTypeRelid(rettype);

!     if (fn_typtype == 'b' || fn_typtype == 'd')
      {
          /* Shouldn't have a typerelid */
          Assert(typerelid == InvalidOid);
--- 377,386 ----

      typerelid = typeidTypeRelid(rettype);

!     if (fn_typtype == 'b' ||
!         fn_typtype == 'd' ||
!         (fn_typtype == 'p' && rettype == ANYARRAYOID) ||
!         (fn_typtype == 'p' && rettype == ANYELEMENTOID))
      {
          /* Shouldn't have a typerelid */
          Assert(typerelid == InvalidOid);
***************
*** 595,610 ****
      functyptype = get_typtype(proc->prorettype);

      /* Disallow pseudotypes in arguments and result */
!     /* except that return type can be RECORD or VOID */
      if (functyptype == 'p' &&
          proc->prorettype != RECORDOID &&
!         proc->prorettype != VOIDOID)
          elog(ERROR, "SQL functions cannot return type %s",
               format_type_be(proc->prorettype));

      for (i = 0; i < proc->pronargs; i++)
      {
!         if (get_typtype(proc->proargtypes[i]) == 'p')
              elog(ERROR, "SQL functions cannot have arguments of type %s",
                   format_type_be(proc->proargtypes[i]));
      }
--- 598,617 ----
      functyptype = get_typtype(proc->prorettype);

      /* Disallow pseudotypes in arguments and result */
!     /* except that return type can be RECORD, VOID, ANYARRAY, or ANYELEMENT */
      if (functyptype == 'p' &&
          proc->prorettype != RECORDOID &&
!         proc->prorettype != VOIDOID &&
!         proc->prorettype != ANYARRAYOID &&
!         proc->prorettype != ANYELEMENTOID)
          elog(ERROR, "SQL functions cannot return type %s",
               format_type_be(proc->prorettype));

      for (i = 0; i < proc->pronargs; i++)
      {
!         if (get_typtype(proc->proargtypes[i]) == 'p' &&
!                         proc->proargtypes[i] != ANYARRAYOID &&
!                         proc->proargtypes[i] != ANYELEMENTOID)
              elog(ERROR, "SQL functions cannot have arguments of type %s",
                   format_type_be(proc->proargtypes[i]));
      }
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.8
diff -c -r1.8 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    27 Jun 2003 14:45:27 -0000    1.8
--- src/backend/commands/aggregatecmds.c    29 Jun 2003 16:26:36 -0000
***************
*** 120,126 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 120,128 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/functions.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/functions.c,v
retrieving revision 1.66
diff -c -r1.66 functions.c
*** src/backend/executor/functions.c    12 Jun 2003 17:29:26 -0000    1.66
--- src/backend/executor/functions.c    30 Jun 2003 01:19:42 -0000
***************
*** 20,25 ****
--- 20,26 ----
  #include "executor/execdefs.h"
  #include "executor/executor.h"
  #include "executor/functions.h"
+ #include "parser/parse_expr.h"
  #include "tcop/pquery.h"
  #include "tcop/tcopprot.h"
  #include "tcop/utility.h"
***************
*** 212,221 ****

      if (nargs > 0)
      {
          argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
!         memcpy(argOidVect,
!                procedureStruct->proargtypes,
!                nargs * sizeof(Oid));
      }
      else
          argOidVect = (Oid *) NULL;
--- 213,235 ----

      if (nargs > 0)
      {
+         List   *p;
+         int        argnum = 0;
+
          argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
!         if (finfo->fn_expr)
!         {
!             /*
!              * If we have a function expression node available to us
!              * use it, as any polymorphic types should have been
!              * disambiguated for us already
!              */
!             foreach(p, ((FuncExpr *) finfo->fn_expr)->args)
!                 argOidVect[argnum++] = exprType((Node *) lfirst(p));
!         }
!         else
!             memcpy(argOidVect, procedureStruct->proargtypes,
!                                             nargs * sizeof(Oid));
      }
      else
          argOidVect = (Oid *) NULL;
Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.109
diff -c -r1.109 nodeAgg.c
*** src/backend/executor/nodeAgg.c    25 Jun 2003 21:30:28 -0000    1.109
--- src/backend/executor/nodeAgg.c    29 Jun 2003 16:26:36 -0000
***************
*** 59,64 ****
--- 59,65 ----
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
  #include "optimizer/clauses.h"
+ #include "parser/parse_agg.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
  #include "parser/parse_oper.h"
***************
*** 1187,1193 ****
--- 1188,1199 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_basetype;
+         Oid            transfn_arg1_type;
          int            i;

          /* Planner should have assigned aggregate to correct level */
***************
*** 1238,1243 ****
--- 1244,1274 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_basetype = exprType((Node *) nth(0, fargs));
+
+         expand_aggregate(agg_rt_basetype,
+                          aggform->aggtranstype,
+                          aggref->aggfnoid,
+                          transfn_oid,
+                          finalfn_oid,
+                          &transfnexpr,
+                          &finalfnexpr,
+                          &transfn_arg1_type);
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1250,1263 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1281,1287 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg1_type);

          /*
           * If the transfn is strict and the initval is NULL, make sure
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.258
diff -c -r1.258 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    29 Jun 2003 00:33:43 -0000    1.258
--- src/backend/nodes/copyfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 728,733 ****
--- 728,734 ----
      COPY_SCALAR_FIELD(agglevelsup);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.201
diff -c -r1.201 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    29 Jun 2003 00:33:43 -0000    1.201
--- src/backend/nodes/equalfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 205,210 ****
--- 205,211 ----
      COMPARE_SCALAR_FIELD(agglevelsup);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.211
diff -c -r1.211 outfuncs.c
*** src/backend/nodes/outfuncs.c    29 Jun 2003 00:33:43 -0000    1.211
--- src/backend/nodes/outfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 616,621 ****
--- 616,622 ----
      WRITE_UINT_FIELD(agglevelsup);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.157
diff -c -r1.157 readfuncs.c
*** src/backend/nodes/readfuncs.c    29 Jun 2003 00:33:43 -0000    1.157
--- src/backend/nodes/readfuncs.c    29 Jun 2003 16:26:36 -0000
***************
*** 416,421 ****
--- 416,422 ----
      READ_UINT_FIELD(agglevelsup);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.142
diff -c -r1.142 clauses.c
*** src/backend/optimizer/util/clauses.c    29 Jun 2003 00:33:43 -0000    1.142
--- src/backend/optimizer/util/clauses.c    30 Jun 2003 01:26:30 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

***************
*** 1731,1736 ****
--- 1753,1759 ----
      int           *usecounts;
      List       *arg;
      int            i;
+     int            j;

      /*
       * Forget it if the function is not SQL-language or has other
***************
*** 1742,1752 ****
          funcform->pronargs != length(args))
          return NULL;

!     /* Forget it if declared return type is tuple or void */
      result_typtype = get_typtype(funcform->prorettype);
      if (result_typtype != 'b' &&
          result_typtype != 'd')
          return NULL;

      /* Check for recursive function, and give up trying to expand if so */
      if (oidMember(funcid, active_fns))
--- 1765,1783 ----
          funcform->pronargs != length(args))
          return NULL;

!     /* Forget it if declared return type is not base or domain */
      result_typtype = get_typtype(funcform->prorettype);
      if (result_typtype != 'b' &&
          result_typtype != 'd')
          return NULL;
+
+     /* Forget it if any declared argument type is polymorphic */
+     for (j = 0; j < funcform->pronargs; j++)
+     {
+         if (funcform->proargtypes[j] == ANYARRAYOID ||
+             funcform->proargtypes[j] == ANYELEMENTOID)
+             return NULL;
+     }

      /* Check for recursive function, and give up trying to expand if so */
      if (oidMember(funcid, active_fns))
Index: src/backend/parser/parse_agg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_agg.c,v
retrieving revision 1.53
diff -c -r1.53 parse_agg.c
*** src/backend/parser/parse_agg.c    6 Jun 2003 15:04:02 -0000    1.53
--- src/backend/parser/parse_agg.c    29 Jun 2003 16:26:36 -0000
***************
*** 14,25 ****
--- 14,29 ----
   */
  #include "postgres.h"

+ #include "catalog/pg_type.h"
+ #include "nodes/params.h"
  #include "optimizer/clauses.h"
  #include "optimizer/tlist.h"
  #include "optimizer/var.h"
  #include "parser/parse_agg.h"
+ #include "parser/parse_type.h"
  #include "parser/parsetree.h"
  #include "rewrite/rewriteManip.h"
+ #include "utils/lsyscache.h"


  typedef struct
***************
*** 312,314 ****
--- 316,539 ----
      return expression_tree_walker(node, check_ungrouped_columns_walker,
                                    (void *) context);
  }
+
+ /*
+  * Create function expressions for the transition and final functions
+  * of an aggregate so that they can be attached to the FmgrInfo nodes
+  * of AggStatePerAgg. If we didn't, the functions would not be able to
+  * know what argument and return data types to use for any that are
+  * polymorphic in its definition.
+  */
+ void
+ expand_aggregate(Oid agg_rt_basetype,
+                  Oid agg_statetype,
+                  Oid agg_fnoid,
+                  Oid transfn_oid,
+                  Oid finalfn_oid,
+                  FuncExpr **transfnexpr,
+                  FuncExpr **finalfnexpr,
+                  Oid *transfn_arg1_type)
+ {
+     Oid           *transfn_arg_types;
+     List       *transfn_args = NIL;
+     int            transfn_nargs;
+     Oid            transfn_ret_type;
+     Oid           *finalfn_arg_types = NULL;
+     List       *finalfn_args = NIL;
+     Oid            finalfn_ret_type = InvalidOid;
+     int            finalfn_nargs = 0;
+     Param       *arg0;
+     Param       *arg1;
+
+     /* get the transition function argument and return types */
+     transfn_ret_type = get_func_rettype(transfn_oid);
+     transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+     /* resolve any polymorphic types */
+     if (transfn_nargs == 2)
+     {
+         /* base type was not ANY */
+         if ((transfn_arg_types[0] == ANYARRAYOID ||
+              transfn_arg_types[0] == ANYELEMENTOID) &&
+             (transfn_arg_types[1] == ANYARRAYOID ||
+              transfn_arg_types[1] == ANYELEMENTOID))
+         {
+             /*
+              * If both transfn args are polymorphic, we can
+              * resolve transfn arg 1 using base type as context
+              */
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                     agg_rt_basetype);
+         }
+         else if ((transfn_arg_types[0] == ANYARRAYOID ||
+                   transfn_arg_types[0] == ANYELEMENTOID))
+         {
+             /*
+              * Otherwise, if transfn arg 1 is polymorphic, we can
+              * resolve it using state type as context. This is only
+              * safe because we prevented the situation where both
+              * state type and transfn arg 1 are polymorphic with a
+              * non-polymorphic transfn arg 2, during aggregate creation.
+              */
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_statetype);
+         }
+
+         /*
+          * Now, if transfn arg 2 is polymorphic, we can set it to the runtime
+          * base type without further adieu
+          */
+         if (transfn_arg_types[1] == ANYARRAYOID ||
+             transfn_arg_types[1] == ANYELEMENTOID)
+             transfn_arg_types[1] = agg_rt_basetype;
+
+         /*
+          * Build arg list to use on the transfn FuncExpr node. We really
+          * only care that transfn can discover the actual argument types
+          * at runtime using get_fn_expr_argtype()
+          */
+         arg0 = makeNode(Param);
+         arg0->paramkind = PARAM_EXEC;
+         arg0->paramid = -1;
+         arg0->paramtype = transfn_arg_types[0];
+
+         arg1 = makeNode(Param);
+         arg1->paramkind = PARAM_EXEC;
+         arg1->paramid = -1;
+         arg1->paramtype = transfn_arg_types[1];
+
+         transfn_args = makeList2(arg0, arg1);
+
+         /*
+          * the state transition function always returns the same type
+          * as its first argument
+          */
+         if (transfn_ret_type == ANYARRAYOID ||
+             transfn_ret_type == ANYELEMENTOID)
+             transfn_ret_type = transfn_arg_types[0];
+     }
+     else if (transfn_nargs == 1)
+     /*
+      * base type was ANY, therefore the aggregate return type should
+      * be non-polymorphic
+      */
+     {
+         Oid    finaltype = get_func_rettype(agg_fnoid);
+
+         /*
+          * this should have been prevented in AggregateCreate,
+          * but check anyway
+          */
+         if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+             elog(ERROR, "an aggregate returning ANYARRAY or ANYELEMENT " \
+                         "must also have either of the them as its base type");
+
+         /* see if we have a final function */
+         if (OidIsValid(finalfn_oid))
+         {
+             finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+             if (finalfn_nargs != 1)
+                 elog(ERROR, "final function takes unexpected number " \
+                             "of arguments: %d", finalfn_nargs);
+
+             /*
+              * final function argument is always the same as the state
+              * function return type
+              */
+             if (finalfn_arg_types[0] != ANYARRAYOID &&
+                 finalfn_arg_types[0] != ANYELEMENTOID)
+             {
+                 /* if it is not ambiguous, use it */
+                 transfn_ret_type = finalfn_arg_types[0];
+             }
+             else
+             {
+                 /* if it is ambiguous, try to derive it */
+                 finalfn_ret_type = finaltype;
+                 finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                         finalfn_ret_type);
+                 transfn_ret_type = finalfn_arg_types[0];
+             }
+         }
+         else
+             transfn_ret_type = finaltype;
+
+         transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                 transfn_ret_type);
+
+         /*
+          * Build arg list to use on the transfn FuncExpr node. We really
+          * only care that transfn can discover the actual argument types
+          * at runtime using get_fn_expr_argtype()
+          */
+         arg0 = makeNode(Param);
+         arg0->paramkind = PARAM_EXEC;
+         arg0->paramid = -1;
+         arg0->paramtype = transfn_arg_types[0];
+
+         transfn_args = makeList1(arg0);
+     }
+     else
+         elog(ERROR, "state transition function takes unexpected number " \
+                     "of arguments: %d", transfn_nargs);
+
+     if (OidIsValid(finalfn_oid))
+     {
+         /* get the final function argument and return types */
+         if (finalfn_ret_type == InvalidOid)
+             finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+         if (!finalfn_arg_types)
+         {
+             finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+             if (finalfn_nargs != 1)
+                 elog(ERROR, "final function takes unexpected number " \
+                             "of arguments: %d", finalfn_nargs);
+         }
+
+         /*
+          * final function argument is always the same as the state
+          * function return type, which by now should have been resolved
+          */
+         if (finalfn_arg_types[0] == ANYARRAYOID ||
+             finalfn_arg_types[0] == ANYELEMENTOID)
+             finalfn_arg_types[0] = transfn_ret_type;
+
+         /*
+          * Build arg list to use on the finalfn FuncExpr node. We really
+          * only care that finalfn can discover the actual argument types
+          * at runtime using get_fn_expr_argtype()
+          */
+         arg0 = makeNode(Param);
+         arg0->paramkind = PARAM_EXEC;
+         arg0->paramid = -1;
+         arg0->paramtype = finalfn_arg_types[0];
+
+         finalfn_args = makeList1(arg0);
+
+         finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                         finalfn_arg_types[0]);
+     }
+
+    *transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                                                transfn_ret_type,
+                                                false,
+                                                COERCE_DONTCARE,
+                                                transfn_args);
+
+     if (OidIsValid(finalfn_oid))
+     {
+        *finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                                                    finalfn_ret_type,
+                                                    false,
+                                                    COERCE_DONTCARE,
+                                                    finalfn_args);
+     }
+
+     /*
+      * we need to return the resolved transfn arg1 type to be used
+      * by GetAggInitVal
+      */
+    *transfn_arg1_type = transfn_arg_types[0];
+ }
+
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.101
diff -c -r2.101 parse_coerce.c
*** src/backend/parser/parse_coerce.c    27 Jun 2003 00:33:25 -0000    2.101
--- src/backend/parser/parse_coerce.c    29 Jun 2003 16:26:36 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.152
diff -c -r1.152 parse_func.c
*** src/backend/parser/parse_func.c    25 Jun 2003 21:30:31 -0000    1.152
--- src/backend/parser/parse_func.c    29 Jun 2003 16:26:36 -0000
***************
*** 336,341 ****
--- 336,342 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          /* parse_agg.c does additional aggregate-specific processing */
          transformAggregateCall(pstate, aggref);
Index: src/backend/parser/parse_type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_type.c,v
retrieving revision 1.57
diff -c -r1.57 parse_type.c
*** src/backend/parser/parse_type.c    29 Apr 2003 22:13:10 -0000    1.57
--- src/backend/parser/parse_type.c    29 Jun 2003 16:26:36 -0000
***************
*** 484,486 ****
--- 484,536 ----

      pfree(buf.data);
  }
+
+ /*
+  * Given a type_to_resolve oid, typically defined at function creation
+  * (e.g. a function argument or return type), and context_type oid,
+  * typically gleaned by the parser as one of the actual arguments
+  * at function call time, derive the runtime type of type_to_resolve.
+  * The intent is to use runtime context to determine what type we should
+  * assign to a polymorphic argument or return type.
+  *
+  * The rules for this resolution are as follows:
+  * 1) if the context type is polymorphic, punt and return type_to_resolve
+  *    unchanged
+  * 2) if type_to_resolve is ANYARRAY (polymorphic), then return context_type
+  *    if it is already an array type, or get its array type if not
+  * 3) if type_to_resolve is ANYELEMENT (polymorphic), then return context_type
+  *    if it is already an elemental type, or get its element type if not
+  * 4) if type_to_resolve is non-polymorphic, return it unchanged
+  */
+ Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
+ }
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.100
diff -c -r1.100 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    27 Jun 2003 00:33:25 -0000    1.100
--- src/backend/utils/cache/lsyscache.c    29 Jun 2003 16:26:36 -0000
***************
*** 719,724 ****
--- 719,758 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.86
diff -c -r1.86 primnodes.h
*** src/include/nodes/primnodes.h    29 Jun 2003 00:33:44 -0000    1.86
--- src/include/nodes/primnodes.h    29 Jun 2003 16:26:36 -0000
***************
*** 226,231 ****
--- 226,232 ----
      Index        agglevelsup;    /* > 0 if agg belongs to outer query */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.65
diff -c -r1.65 clauses.h
*** src/include/optimizer/clauses.h    25 Jun 2003 21:30:33 -0000    1.65
--- src/include/optimizer/clauses.h    29 Jun 2003 16:26:36 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_agg.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_agg.h,v
retrieving revision 1.26
diff -c -r1.26 parse_agg.h
*** src/include/parser/parse_agg.h    6 Jun 2003 15:04:03 -0000    1.26
--- src/include/parser/parse_agg.h    29 Jun 2003 16:26:36 -0000
***************
*** 18,22 ****
--- 18,30 ----
  extern void transformAggregateCall(ParseState *pstate, Aggref *agg);

  extern void parseCheckAggregates(ParseState *pstate, Query *qry);
+ extern void expand_aggregate(Oid agg_rt_basetype,
+                              Oid agg_statetype,
+                              Oid agg_fnoid,
+                              Oid transfn_oid,
+                              Oid finalfn_oid,
+                              FuncExpr **transfnexpr,
+                              FuncExpr **finalfnexpr,
+                              Oid *transfn_arg1_type);

  #endif   /* PARSE_AGG_H */
Index: src/include/parser/parse_type.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_type.h,v
retrieving revision 1.24
diff -c -r1.24 parse_type.h
*** src/include/parser/parse_type.h    31 Aug 2002 22:10:47 -0000    1.24
--- src/include/parser/parse_type.h    29 Jun 2003 16:26:36 -0000
***************
*** 40,45 ****
--- 40,46 ----
  extern Oid    typeidTypeRelid(Oid type_id);

  extern void parseTypeString(const char *str, Oid *type_id, int32 *typmod);
+ extern Oid resolve_type(Oid type_to_resolve, Oid context_type);

  #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)

Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.75
diff -c -r1.75 lsyscache.h
*** src/include/utils/lsyscache.h    27 Jun 2003 00:33:26 -0000    1.75
--- src/include/utils/lsyscache.h    29 Jun 2003 16:26:36 -0000
***************
*** 50,55 ****
--- 50,56 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);

Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> So I'd propose that we put another check in inline_function(), and
> reject attempts to inline functions with polymorphic arguments.

Seems reasonable.  Someday we might want to try to make that work,
but not the day before feature freeze...

            regards, tom lane

Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:

+  * The rules for this resolution are as follows:
+  * 1) if the context type is polymorphic, punt and return type_to_resolve
+  *    unchanged
+  * 2) if type_to_resolve is ANYARRAY (polymorphic), then return context_type
+  *    if it is already an array type, or get its array type if not
+  * 3) if type_to_resolve is ANYELEMENT (polymorphic), then return context_type
+  *    if it is already an elemental type, or get its element type if not
+  * 4) if type_to_resolve is non-polymorphic, return it unchanged
+  */
+ Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)

This seems wrong.  ANYELEMENT doesn't imply conversion from array to
element type.  I don't think you're giving resolve_type nearly enough
context to produce a correct answer.

[ thinks for a bit ]

Bearing in mind that the result type of the transfn must equal its first
input type, ISTM there are only four interesting cases for polymorphic
transfer functions:
    transfn(anyelement, anyelement) returns anyelement
    transfn(anyelement, anyarray) returns anyelement
    transfn(anyarray, anyelement) returns anyarray
    transfn(anyarray, anyarray) returns anyarray
Per our previous discussion, other cases (such as single-input transfn
or non-polymorphic second input type) can be rejected by CREATE AGGREGATE
since there'd be no way to resolve the actual transfer state type.

Knowing which of these four cases you have, you can correctly derive the
actual state type from the actual aggregate input type, namely
    1. same as input type (no restrictions on what it is)
    2. element type of input (which must be an array type)
    3. array type with input as element (there must be one)
    4. input type, but first check it's an array
You're not providing enough info to resolve_type to let it handle all
four cases correctly.  In any case, this procedure seems exceedingly
specific to the problem of resolving aggregate internal types.  I doubt
it should be in parse_type at all, and it certainly shouldn't have a
name as generic as resolve_type.

            regards, tom lane

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>
> +  * The rules for this resolution are as follows:
> +  * 1) if the context type is polymorphic, punt and return type_to_resolve
> +  *    unchanged
> +  * 2) if type_to_resolve is ANYARRAY (polymorphic), then return context_type
> +  *    if it is already an array type, or get its array type if not
> +  * 3) if type_to_resolve is ANYELEMENT (polymorphic), then return context_type
> +  *    if it is already an elemental type, or get its element type if not
> +  * 4) if type_to_resolve is non-polymorphic, return it unchanged
> +  */
> + Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
>
> This seems wrong.  ANYELEMENT doesn't imply conversion from array to
> element type.  I don't think you're giving resolve_type nearly enough
> context to produce a correct answer.

And the function isn't trying to do that. If I have an ANYELEMENT I'm
trying to resolve, and the context type is a scalar type, it uses that.
If the context type is array, then it uses the array's element type.


> You're not providing enough info to resolve_type to let it handle all
> four cases correctly.  In any case, this procedure seems exceedingly
> specific to the problem of resolving aggregate internal types.  I doubt
> it should be in parse_type at all, and it certainly shouldn't have a
> name as generic as resolve_type.
>

No, resolve_type() is not at all specific to polymorphic aggregates. It
implements the rules of polymorphism that we previously agreed to,
namely that an ANYARRAY can be resolved by knowing any of these data
types at the time of function call:

1) the actual call type at the same position (i.e. argument number or
    return type) as the ANYARRAY you're trying to resolve

2) the actual call type at a different position from the ANYARRAY you're
    trying to resolve, as long as the declared type at that position is
    either ANYARRAY or ANYELEMENT.

- If type_to_resolve is non-polymorphic, we have nothing in need of
resolution.

- If context_type is polymorphic, we have no context with which to do
resolution.

- If type_to_resolve is polymorphic (ANYARRAY or ANYELEMENT), and
context_type is not, resolve_type() picks the appropriate type based on
whether we need an array or not, and whether we've been given an array
or not as context.

Did you find a specific case where this falls down?

Joe



Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> You're not providing enough info to resolve_type to let it handle all
>> four cases correctly.

> No, resolve_type() is not at all specific to polymorphic aggregates. It
> implements the rules of polymorphism that we previously agreed to,
> namely that an ANYARRAY can be resolved by knowing any of these data
> types at the time of function call:

> 1) the actual call type at the same position (i.e. argument number or
>     return type) as the ANYARRAY you're trying to resolve

> 2) the actual call type at a different position from the ANYARRAY you're
>     trying to resolve, as long as the declared type at that position is
>     either ANYARRAY or ANYELEMENT.

But you still need three pieces of info, and it's only being given two.
In the second case (where you know actual argument type at a different
position) you must know whether the other position's declared type is
anyarray or anyelement, and you can't assume it's the same as the one at
the position you want to resolve.

> Did you find a specific case where this falls down?

It fails to raise errors in cases where errors should be raised,
but instead delivers the wrong datatype as result.  It can also
incorrectly replace an arraytype by its element type ("ANYELEMENT"
doesn't require the actual type to not be an array --- unless
"ANYARRAY" is also used in the same declaration, and even then
it's only going to fail because we don't support arrays of arrays).

            regards, tom lane

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> In the second case (where you know actual argument type at a different
> position) you must know whether the other position's declared type is
> anyarray or anyelement, and you can't assume it's the same as the one at
> the position you want to resolve.

I still don't understand why that's needed (but perhaps it's related to
your comment below).

> It can also incorrectly replace an arraytype by its element type
> ("ANYELEMENT" doesn't require the actual type to not be an array

Are you referring to ANYELEMENT actually being an array at runtime?
That's the first time I've heard that concept. 'Til now, I've been
working with the assumption that arrays and elements were distinct, and
one can imply the other.

> --- unless
> "ANYARRAY" is also used in the same declaration, and even then
> it's only going to fail because we don't support arrays of arrays).

But this is the least of our problems when/if we support arrays of
arrays. The notion of element types being distinct from array types goes
pretty deep currently.

In any case, can you suggest concrete changes I can work on between now
and tonight? Or can this go in before the freeze as-is and get adjusted
afterwards?

Joe


Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> Are you referring to ANYELEMENT actually being an array at runtime?
> That's the first time I've heard that concept.

That was the behavior we agreed to some time ago, to avoid needing
to entangle ANY into the polymorphism logic.  See the comments for
check_generic_type_consistency:

 * The argument consistency rules are:
 *
 * 1) All arguments declared ANYARRAY must have matching datatypes,
 *      and must in fact be varlena arrays.
 * 2) All arguments declared ANYELEMENT must have matching datatypes.
 * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
 *    the actual ANYELEMENT datatype is in fact the element type for
 *    the actual ANYARRAY datatype.

If only ANYELEMENT and not ANYARRAY appears in a function declaration,
then it can stand for any type, because only rule 2 applies.  (The
difference from ANY is that multiple occurences of ANYELEMENT are all
constrained to stand for the same type.)

            regards, tom lane

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> If only ANYELEMENT and not ANYARRAY appears in a function declaration,
> then it can stand for any type, because only rule 2 applies.  (The
> difference from ANY is that multiple occurences of ANYELEMENT are all
> constrained to stand for the same type.)

Hmmm, I don't remember that nuance, hence the code deficiency. I'm
should be able to finish up the plpgsql hash table stuff in the next
couple of hours, then I'll get back to thinking about this one. Do I
have until midnite PDT, or EDT?

Joe


Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> Do I have until midnite PDT, or EDT?

We hadn't actually set a formal deadline time AFAIR.  Midnight your
local time is fine with me.

Thinking about this further, it occurs to me that there's no hard reason
plpgsql (and other PLs that adopt the we-can-convert-anything-to-string
philosophy, such as pltcl) couldn't allow arguments (though not results)
of type ANY.  It's not different from accepting ANYELEMENT as far as the
runtime mechanisms are concerned.  The only difference is in
constraining multiple arguments and/or the result to be of the same or
related types, which is not really an issue that affects the PL directly.

As far as the other point goes, I plan to change resolve_type to be like

resolve_polymorphic_type(declared_type, context_actual_type,
                         context_declared_type)

where context_actual_type is the actual datatype passed to an argument
of declared type context_declared_type, and declared_type is the
declared type of some argument or result that you want to resolve (not
necessarily the same argument).  So the rules are

1. declared_type not polymorphic -> return it as-is.

2. declared_type polymorphic, but context_declared_type not polymorphic
   -> raise error ("can't resolve").

3. Otherwise there are four possible combinations:

declared_type    context_declared_type    action

ANYELEMENT    ANYELEMENT        return context_actual_type
ANYELEMENT    ANYARRAY        return get_element_type(context_actual_type)
                    (raise error if it fails)
ANYARRAY    ANYELEMENT        return get_array_type(context_actual_type)
                    (raise error if it fails)
ANYARRAY    ANYARRAY        check context_actual_type is an
                    array, then return it

This should work as long as the parser has previously done
enforce_generic_type_consistency on the call.  I'm still not convinced
that there is any application for it outside of deriving a polymorphic
aggregate's state type, though.

            regards, tom lane

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> Thinking about this further, it occurs to me that there's no hard reason
> plpgsql (and other PLs that adopt the we-can-convert-anything-to-string
> philosophy, such as pltcl) couldn't allow arguments (though not results)
> of type ANY.  It's not different from accepting ANYELEMENT as far as the
> runtime mechanisms are concerned.  The only difference is in
> constraining multiple arguments and/or the result to be of the same or
> related types, which is not really an issue that affects the PL directly.

True. As long as the function has access to the runtime data type, it
has the ability to deal with anything it's handed.

> As far as the other point goes, I plan to change resolve_type to be like

OK, that all makes good sense to me now.

> I'm still not convinced that there is any application for it outside of
> deriving a polymorphic aggregate's state type, though.

At least not yet ;-)

Between this discussion, and Peter pointing out that the spec allows
arrays-of-arrays, it's gotten me thinking about how to implement said
arrays-of-arrays. Obviously not gonna happen for 7.4, but I might try to
do that, fix the NULL array element issue, and otherwise try to continue
the progress on SQL99 array support for 7.5.

Joe


Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> Attached is a patch that implements polymorphic aggregates.

> Included in the patch, I changed SQL language functions so that they
> could be declared with and use polymorphic types.

I've committed the polymorphic-SQL-functions part of this separately.

I didn't like the way you did it --- in particular, hacking
enforce_generic_type_consistency to allow generic types to be returned
strikes me as a very dangerous thing; it's supposed to be replacing them
all, not passing them back.  In any case it didn't get the job done,
since any but the most trivial function bodies would fail type
resolution at some point.  For example, I tried

create function array_sub(anyarray, int) returns anyelement as
'select $1[$2]' language sql;

and it failed with something along the lines of
    ERROR: transformArraySubscripts: type anyarray is not an array

What I've done instead is not to weaken type checking, but simply to
postpone all checking of the body of a SQL function to runtime if it
has any polymorphic arguments.  At runtime, we know the actual types
for the arguments, and we know the actual assigned result type, and
then we can run the normal checking operations without any problem.

Applied patch attached, just FYI.  (It still needs documentation
updates, which I trust you will supply later.)

Now back to looking at polymorphic aggregates...

            regards, tom lane

*** src/backend/catalog/pg_proc.c.orig    Sun Jun 15 13:59:10 2003
--- src/backend/catalog/pg_proc.c    Mon Jun 30 19:45:01 2003
***************
*** 33,39 ****
  #include "utils/syscache.h"


- static void checkretval(Oid rettype, char fn_typtype, List *queryTreeList);
  Datum        fmgr_internal_validator(PG_FUNCTION_ARGS);
  Datum        fmgr_c_validator(PG_FUNCTION_ARGS);
  Datum        fmgr_sql_validator(PG_FUNCTION_ARGS);
--- 33,38 ----
***************
*** 317,331 ****
  }

  /*
!  * checkretval() -- check return value of a list of sql parse trees.
   *
   * The return value of a sql function is the value returned by
!  * the final query in the function.  We do some ad-hoc define-time
!  * type checking here to be sure that the user is returning the
!  * type he claims.
   */
! static void
! checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
  {
      Query       *parse;
      int            cmd;
--- 316,335 ----
  }

  /*
!  * check_sql_fn_retval() -- check return value of a list of sql parse trees.
   *
   * The return value of a sql function is the value returned by
!  * the final query in the function.  We do some ad-hoc type checking here
!  * to be sure that the user is returning the type he claims.
!  *
!  * This is normally applied during function definition, but in the case
!  * of a function with polymorphic arguments, we instead apply it during
!  * function execution startup.  The rettype is then the actual resolved
!  * output type of the function, rather than the declared type.  (Therefore,
!  * we should never see ANYARRAY or ANYELEMENT as rettype.)
   */
! void
! check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
  {
      Query       *parse;
      int            cmd;
***************
*** 472,478 ****

          relation_close(reln, AccessShareLock);
      }
!     else if (fn_typtype == 'p' && rettype == RECORDOID)
      {
          /* Shouldn't have a typerelid */
          Assert(typerelid == InvalidOid);
--- 476,482 ----

          relation_close(reln, AccessShareLock);
      }
!     else if (rettype == RECORDOID)
      {
          /* Shouldn't have a typerelid */
          Assert(typerelid == InvalidOid);
***************
*** 482,487 ****
--- 486,499 ----
           * tuple.
           */
      }
+     else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+     {
+         /*
+          * This should already have been caught ...
+          */
+         elog(ERROR, "functions returning ANYARRAY or ANYELEMENT must " \
+              "have at least one argument of either type");
+     }
      else
          elog(ERROR, "return type %s is not supported for SQL functions",
               format_type_be(rettype));
***************
*** 505,511 ****
      Datum        tmp;
      char       *prosrc;

!     tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
      if (!HeapTupleIsValid(tuple))
          elog(ERROR, "cache lookup of function %u failed", funcoid);
      proc = (Form_pg_proc) GETSTRUCT(tuple);
--- 517,525 ----
      Datum        tmp;
      char       *prosrc;

!     tuple = SearchSysCache(PROCOID,
!                            ObjectIdGetDatum(funcoid),
!                            0, 0, 0);
      if (!HeapTupleIsValid(tuple))
          elog(ERROR, "cache lookup of function %u failed", funcoid);
      proc = (Form_pg_proc) GETSTRUCT(tuple);
***************
*** 544,550 ****
      char       *prosrc;
      char       *probin;

!     tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
      if (!HeapTupleIsValid(tuple))
          elog(ERROR, "cache lookup of function %u failed", funcoid);
      proc = (Form_pg_proc) GETSTRUCT(tuple);
--- 558,566 ----
      char       *prosrc;
      char       *probin;

!     tuple = SearchSysCache(PROCOID,
!                            ObjectIdGetDatum(funcoid),
!                            0, 0, 0);
      if (!HeapTupleIsValid(tuple))
          elog(ERROR, "cache lookup of function %u failed", funcoid);
      proc = (Form_pg_proc) GETSTRUCT(tuple);
***************
*** 585,622 ****
      Datum        tmp;
      char       *prosrc;
      char        functyptype;
      int            i;

!     tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
      if (!HeapTupleIsValid(tuple))
          elog(ERROR, "cache lookup of function %u failed", funcoid);
      proc = (Form_pg_proc) GETSTRUCT(tuple);

      functyptype = get_typtype(proc->prorettype);

!     /* Disallow pseudotypes in arguments and result */
!     /* except that return type can be RECORD or VOID */
      if (functyptype == 'p' &&
          proc->prorettype != RECORDOID &&
!         proc->prorettype != VOIDOID)
          elog(ERROR, "SQL functions cannot return type %s",
               format_type_be(proc->prorettype));

      for (i = 0; i < proc->pronargs; i++)
      {
          if (get_typtype(proc->proargtypes[i]) == 'p')
!             elog(ERROR, "SQL functions cannot have arguments of type %s",
!                  format_type_be(proc->proargtypes[i]));
      }

!     tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
!     if (isnull)
!         elog(ERROR, "null prosrc");
!
!     prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
!
!     querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
!     checkretval(proc->prorettype, functyptype, querytree_list);

      ReleaseSysCache(tuple);

--- 601,662 ----
      Datum        tmp;
      char       *prosrc;
      char        functyptype;
+     bool        haspolyarg;
      int            i;

!     tuple = SearchSysCache(PROCOID,
!                            ObjectIdGetDatum(funcoid),
!                            0, 0, 0);
      if (!HeapTupleIsValid(tuple))
          elog(ERROR, "cache lookup of function %u failed", funcoid);
      proc = (Form_pg_proc) GETSTRUCT(tuple);

      functyptype = get_typtype(proc->prorettype);

!     /* Disallow pseudotype result */
!     /* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
      if (functyptype == 'p' &&
          proc->prorettype != RECORDOID &&
!         proc->prorettype != VOIDOID &&
!         proc->prorettype != ANYARRAYOID &&
!         proc->prorettype != ANYELEMENTOID)
          elog(ERROR, "SQL functions cannot return type %s",
               format_type_be(proc->prorettype));

+     /* Disallow pseudotypes in arguments */
+     /* except for ANYARRAY or ANYELEMENT */
+     haspolyarg = false;
      for (i = 0; i < proc->pronargs; i++)
      {
          if (get_typtype(proc->proargtypes[i]) == 'p')
!         {
!             if (proc->proargtypes[i] == ANYARRAYOID ||
!                 proc->proargtypes[i] == ANYELEMENTOID)
!                 haspolyarg = true;
!             else
!                 elog(ERROR, "SQL functions cannot have arguments of type %s",
!                      format_type_be(proc->proargtypes[i]));
!         }
      }

!     /*
!      * We can't precheck the function definition if there are any polymorphic
!      * input types, because actual datatypes of expression results will be
!      * unresolvable.  The check will be done at runtime instead.
!      */
!     if (!haspolyarg)
!     {
!         tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
!         if (isnull)
!             elog(ERROR, "null prosrc");
!
!         prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
!
!         querytree_list = pg_parse_and_rewrite(prosrc,
!                                               proc->proargtypes,
!                                               proc->pronargs);
!         check_sql_fn_retval(proc->prorettype, functyptype, querytree_list);
!     }

      ReleaseSysCache(tuple);

*** src/backend/executor/functions.c.orig    Thu Jun 12 13:29:26 2003
--- src/backend/executor/functions.c    Mon Jun 30 19:46:17 2003
***************
*** 24,29 ****
--- 24,30 ----
  #include "tcop/tcopprot.h"
  #include "tcop/utility.h"
  #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
  #include "utils/syscache.h"


***************
*** 76,82 ****

  /* non-export function prototypes */
  static execution_state *init_execution_state(char *src,
!                      Oid *argOidVect, int nargs);
  static void init_sql_fcache(FmgrInfo *finfo);
  static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
  static TupleTableSlot *postquel_getnext(execution_state *es);
--- 77,84 ----

  /* non-export function prototypes */
  static execution_state *init_execution_state(char *src,
!                      Oid *argOidVect, int nargs,
!                      Oid rettype, bool haspolyarg);
  static void init_sql_fcache(FmgrInfo *finfo);
  static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
  static TupleTableSlot *postquel_getnext(execution_state *es);
***************
*** 90,96 ****


  static execution_state *
! init_execution_state(char *src, Oid *argOidVect, int nargs)
  {
      execution_state *firstes;
      execution_state *preves;
--- 92,99 ----


  static execution_state *
! init_execution_state(char *src, Oid *argOidVect, int nargs,
!                      Oid rettype, bool haspolyarg)
  {
      execution_state *firstes;
      execution_state *preves;
***************
*** 99,104 ****
--- 102,114 ----

      queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);

+     /*
+      * If the function has any arguments declared as polymorphic types,
+      * then it wasn't type-checked at definition time; must do so now.
+      */
+     if (haspolyarg)
+         check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
+
      firstes = NULL;
      preves = NULL;

***************
*** 133,149 ****
--- 143,163 ----
  init_sql_fcache(FmgrInfo *finfo)
  {
      Oid            foid = finfo->fn_oid;
+     Oid            rettype;
      HeapTuple    procedureTuple;
      HeapTuple    typeTuple;
      Form_pg_proc procedureStruct;
      Form_pg_type typeStruct;
      SQLFunctionCachePtr fcache;
      Oid           *argOidVect;
+     bool        haspolyarg;
      char       *src;
      int            nargs;
      Datum        tmp;
      bool        isNull;

+     fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
+
      /*
       * get the procedure tuple corresponding to the given function Oid
       */
***************
*** 153,182 ****
      if (!HeapTupleIsValid(procedureTuple))
          elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
               foid);
-
      procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);

      /*
!      * get the return type from the procedure tuple
       */
      typeTuple = SearchSysCache(TYPEOID,
!                            ObjectIdGetDatum(procedureStruct->prorettype),
                                 0, 0, 0);
      if (!HeapTupleIsValid(typeTuple))
          elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
!              procedureStruct->prorettype);
!
      typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);

-     fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
-
      /*
       * get the type length and by-value flag from the type tuple
       */
      fcache->typlen = typeStruct->typlen;

!     if (typeStruct->typtype != 'c' &&
!         procedureStruct->prorettype != RECORDOID)
      {
          /* The return type is not a composite type, so just use byval */
          fcache->typbyval = typeStruct->typbyval;
--- 167,203 ----
      if (!HeapTupleIsValid(procedureTuple))
          elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
               foid);
      procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);

      /*
!      * get the result type from the procedure tuple, and check for
!      * polymorphic result type; if so, find out the actual result type.
       */
+     rettype = procedureStruct->prorettype;
+
+     if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+     {
+         rettype = get_fn_expr_rettype(finfo);
+         if (rettype == InvalidOid)
+             elog(ERROR, "could not determine actual result type for function declared %s",
+                  format_type_be(procedureStruct->prorettype));
+     }
+
+     /* Now look up the actual result type */
      typeTuple = SearchSysCache(TYPEOID,
!                                ObjectIdGetDatum(rettype),
                                 0, 0, 0);
      if (!HeapTupleIsValid(typeTuple))
          elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
!              rettype);
      typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);

      /*
       * get the type length and by-value flag from the type tuple
       */
      fcache->typlen = typeStruct->typlen;

!     if (typeStruct->typtype != 'c' && rettype != RECORDOID)
      {
          /* The return type is not a composite type, so just use byval */
          fcache->typbyval = typeStruct->typbyval;
***************
*** 205,221 ****
          fcache->funcSlot = NULL;

      /*
!      * Parse and plan the queries.  We need the argument info to pass
       * to the parser.
       */
      nargs = procedureStruct->pronargs;

      if (nargs > 0)
      {
          argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
          memcpy(argOidVect,
                 procedureStruct->proargtypes,
                 nargs * sizeof(Oid));
      }
      else
          argOidVect = (Oid *) NULL;
--- 226,260 ----
          fcache->funcSlot = NULL;

      /*
!      * Parse and plan the queries.  We need the argument type info to pass
       * to the parser.
       */
      nargs = procedureStruct->pronargs;
+     haspolyarg = false;

      if (nargs > 0)
      {
+         int        argnum;
+
          argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
          memcpy(argOidVect,
                 procedureStruct->proargtypes,
                 nargs * sizeof(Oid));
+         /* Resolve any polymorphic argument types */
+         for (argnum = 0; argnum < nargs; argnum++)
+         {
+             Oid        argtype = argOidVect[argnum];
+
+             if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
+             {
+                 argtype = get_fn_expr_argtype(finfo, argnum);
+                 if (argtype == InvalidOid)
+                     elog(ERROR, "could not determine actual type of argument declared %s",
+                          format_type_be(argOidVect[argnum]));
+                 argOidVect[argnum] = argtype;
+                 haspolyarg = true;
+             }
+         }
      }
      else
          argOidVect = (Oid *) NULL;
***************
*** 229,235 ****
               foid);
      src = DatumGetCString(DirectFunctionCall1(textout, tmp));

!     fcache->func_state = init_execution_state(src, argOidVect, nargs);

      pfree(src);

--- 268,275 ----
               foid);
      src = DatumGetCString(DirectFunctionCall1(textout, tmp));

!     fcache->func_state = init_execution_state(src, argOidVect, nargs,
!                                               rettype, haspolyarg);

      pfree(src);

*** src/backend/optimizer/util/clauses.c.orig    Sat Jun 28 20:33:43 2003
--- src/backend/optimizer/util/clauses.c    Mon Jun 30 18:47:38 2003
***************
*** 1731,1736 ****
--- 1731,1737 ----
      int           *usecounts;
      List       *arg;
      int            i;
+     int            j;

      /*
       * Forget it if the function is not SQL-language or has other
***************
*** 1742,1752 ****
          funcform->pronargs != length(args))
          return NULL;

!     /* Forget it if declared return type is tuple or void */
      result_typtype = get_typtype(funcform->prorettype);
      if (result_typtype != 'b' &&
          result_typtype != 'd')
          return NULL;

      /* Check for recursive function, and give up trying to expand if so */
      if (oidMember(funcid, active_fns))
--- 1743,1761 ----
          funcform->pronargs != length(args))
          return NULL;

!     /* Forget it if declared return type is not base or domain */
      result_typtype = get_typtype(funcform->prorettype);
      if (result_typtype != 'b' &&
          result_typtype != 'd')
          return NULL;
+
+     /* Forget it if any declared argument type is polymorphic */
+     for (j = 0; j < funcform->pronargs; j++)
+     {
+         if (funcform->proargtypes[j] == ANYARRAYOID ||
+             funcform->proargtypes[j] == ANYELEMENTOID)
+             return NULL;
+     }

      /* Check for recursive function, and give up trying to expand if so */
      if (oidMember(funcid, active_fns))
*** src/backend/utils/adt/array_userfuncs.c.orig    Thu Jun 26 20:33:25 2003
--- src/backend/utils/adt/array_userfuncs.c    Mon Jun 30 18:40:03 2003
***************
*** 37,44 ****
      int16        typlen;
      bool        typbyval;
      char        typalign;
!     Oid            arg0_typeid = get_fn_expr_argtype(fcinfo, 0);
!     Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
      ArrayMetaState *my_extra;
--- 37,44 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
!     Oid            arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
!     Oid            arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
      ArrayMetaState *my_extra;
*** src/backend/utils/adt/arrayfuncs.c.orig    Thu Jun 26 20:33:25 2003
--- src/backend/utils/adt/arrayfuncs.c    Mon Jun 30 18:40:03 2003
***************
*** 2792,2798 ****

      if (my_extra->srctype != src_elem_type)
      {
!         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
          Oid            tgt_elem_type;
          Oid            funcId;

--- 2792,2798 ----

      if (my_extra->srctype != src_elem_type)
      {
!         Oid            tgt_type = get_fn_expr_rettype(fmgr_info);
          Oid            tgt_elem_type;
          Oid            funcId;

*** src/backend/utils/fmgr/fmgr.c.orig    Sat Jun 28 20:33:44 2003
--- src/backend/utils/fmgr/fmgr.c    Mon Jun 30 18:39:50 2003
***************
*** 1616,1631 ****

  /*-------------------------------------------------------------------------
   *        Support routines for extracting info from fn_expr parse tree
   *-------------------------------------------------------------------------
   */

  /*
!  * Get the OID of the function return type
   *
   * Returns InvalidOid if information is not available
   */
  Oid
! get_fn_expr_rettype(FunctionCallInfo fcinfo)
  {
      Node   *expr;

--- 1616,1634 ----

  /*-------------------------------------------------------------------------
   *        Support routines for extracting info from fn_expr parse tree
+  *
+  * These are needed by polymorphic functions, which accept multiple possible
+  * input types and need help from the parser to know what they've got.
   *-------------------------------------------------------------------------
   */

  /*
!  * Get the actual type OID of the function return type
   *
   * Returns InvalidOid if information is not available
   */
  Oid
! get_fn_expr_rettype(FmgrInfo *flinfo)
  {
      Node   *expr;

***************
*** 1633,1653 ****
       * can't return anything useful if we have no FmgrInfo or if
       * its fn_expr node has not been initialized
       */
!     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
          return InvalidOid;

!     expr = fcinfo->flinfo->fn_expr;

      return exprType(expr);
  }

  /*
!  * Get the type OID of a specific function argument (counting from 0)
   *
   * Returns InvalidOid if information is not available
   */
  Oid
! get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
  {
      Node   *expr;
      List   *args;
--- 1636,1656 ----
       * can't return anything useful if we have no FmgrInfo or if
       * its fn_expr node has not been initialized
       */
!     if (!flinfo || !flinfo->fn_expr)
          return InvalidOid;

!     expr = flinfo->fn_expr;

      return exprType(expr);
  }

  /*
!  * Get the actual type OID of a specific function argument (counting from 0)
   *
   * Returns InvalidOid if information is not available
   */
  Oid
! get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
  {
      Node   *expr;
      List   *args;
***************
*** 1657,1666 ****
       * can't return anything useful if we have no FmgrInfo or if
       * its fn_expr node has not been initialized
       */
!     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
          return InvalidOid;

!     expr = fcinfo->flinfo->fn_expr;

      if (IsA(expr, FuncExpr))
          args = ((FuncExpr *) expr)->args;
--- 1660,1669 ----
       * can't return anything useful if we have no FmgrInfo or if
       * its fn_expr node has not been initialized
       */
!     if (!flinfo || !flinfo->fn_expr)
          return InvalidOid;

!     expr = flinfo->fn_expr;

      if (IsA(expr, FuncExpr))
          args = ((FuncExpr *) expr)->args;
*** src/include/catalog/pg_proc.h.orig    Thu Jun 26 20:33:25 2003
--- src/include/catalog/pg_proc.h    Mon Jun 30 19:42:59 2003
***************
*** 3438,3441 ****
--- 3438,3444 ----
                  int parameterCount,
                  const Oid *parameterTypes);

+ extern void check_sql_fn_retval(Oid rettype, char fn_typtype,
+                                 List *queryTreeList);
+
  #endif   /* PG_PROC_H */
*** src/include/fmgr.h.orig    Thu Jun 26 10:18:56 2003
--- src/include/fmgr.h    Mon Jun 30 18:39:37 2003
***************
*** 378,385 ****
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
  extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 378,385 ----
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
  extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FmgrInfo *flinfo);
! extern Oid    get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);

  /*
   * Routines in dfmgr.c

Re: [HACKERS] Missing array support

От
Joe Conway
Дата:
Tom Lane wrote:
> What I've done instead is not to weaken type checking, but simply to
> postpone all checking of the body of a SQL function to runtime if it
> has any polymorphic arguments.  At runtime, we know the actual types
> for the arguments, and we know the actual assigned result type, and
> then we can run the normal checking operations without any problem.

As usual, big improvement in what I submitted. Thanks.

> Applied patch attached, just FYI.  (It still needs documentation
> updates, which I trust you will supply later.)

Yup, you have my gold plated IOU on the doc cleanup for all this stuff.

One note; this change

>   Oid
> ! get_fn_expr_rettype(FunctionCallInfo fcinfo)
>   {
[snip]
>   Oid
> ! get_fn_expr_rettype(FmgrInfo *flinfo)
>   {

is a good example why some things, particularly PLs, are better off
being in the main source tree rather than on gborg (or someplace else).
PL/R uses get_fn_expr_rettype() and get_fn_expr_argtype(), so it's now
broken as of CVS tip :(

I know the license issue is the primary reason why PL/R is not in the
main source tree, but I bring it up because I think the tendency to push
things out and over to gborg has been too strong lately.

Joe


Re: [HACKERS] Missing array support

От
Tom Lane
Дата:
Joe Conway <mail@joeconway.com> writes:
> One note; this change
>> ! get_fn_expr_rettype(FunctionCallInfo fcinfo)
>> to
>> ! get_fn_expr_rettype(FmgrInfo *flinfo)

> is a good example why some things, particularly PLs, are better off
> being in the main source tree rather than on gborg (or someplace else).
> PL/R uses get_fn_expr_rettype() and get_fn_expr_argtype(), so it's now
> broken as of CVS tip :(

Sorry about that.  I suspected you had some calls I didn't know about,
but there wasn't much I could do about 'em; and I wanted to correct the
function signatures before anything else started to depend on them.

            regards, tom lane