Re: [HACKERS] Missing array support
| От | Joe Conway |
|---|---|
| Тема | Re: [HACKERS] Missing array support |
| Дата | |
| Msg-id | 3EFFA6D0.1000505@joeconway.com обсуждение исходный текст |
| Ответ на | Re: [HACKERS] Missing array support (Joe Conway <mail@joeconway.com>) |
| Ответы |
Re: [HACKERS] Missing array support
|
| Список | pgsql-patches |
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);
В списке pgsql-patches по дате отправления: