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);