Joe Conway wrote:
Tom Lane wrote:
Joe Conway <[EMAIL PROTECTED]> 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);
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to