Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e3b1b6c

Browse files
committed
Aggregates can be polymorphic, using polymorphic implementation functions.
It also works to create a non-polymorphic aggregate from polymorphic functions, should you want to do that. Regression test added, docs still lacking. By Joe Conway, with some kibitzing from Tom Lane.
1 parent 02b5d8e commit e3b1b6c

File tree

15 files changed

+1300
-83
lines changed

15 files changed

+1300
-83
lines changed

src/backend/catalog/pg_aggregate.c

Lines changed: 120 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.58 2003/06/25 21:30:25 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.59 2003/07/01 19:10:52 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -29,6 +29,10 @@
2929
#include "utils/syscache.h"
3030

3131

32+
static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
33+
Oid *rettype);
34+
35+
3236
/*
3337
* AggregateCreate
3438
*/
@@ -48,9 +52,10 @@ AggregateCreate(const char *aggName,
4852
Form_pg_proc proc;
4953
Oid transfn;
5054
Oid finalfn = InvalidOid; /* can be omitted */
55+
Oid rettype;
5156
Oid finaltype;
5257
Oid fnArgs[FUNC_MAX_ARGS];
53-
int nargs;
58+
int nargs_transfn;
5459
Oid procOid;
5560
TupleDesc tupDesc;
5661
int i;
@@ -64,28 +69,49 @@ AggregateCreate(const char *aggName,
6469
if (!aggtransfnName)
6570
elog(ERROR, "aggregate must have a transition function");
6671

72+
/*
73+
* If transtype is polymorphic, basetype must be polymorphic also;
74+
* else we will have no way to deduce the actual transtype.
75+
*/
76+
if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) &&
77+
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
78+
elog(ERROR, "an aggregate using ANYARRAY or ANYELEMENT as trans type "
79+
"must also have one of them as its base type");
80+
6781
/* handle transfn */
6882
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
6983
fnArgs[0] = aggTransType;
7084
if (aggBaseType == ANYOID)
71-
nargs = 1;
85+
nargs_transfn = 1;
7286
else
7387
{
7488
fnArgs[1] = aggBaseType;
75-
nargs = 2;
89+
nargs_transfn = 2;
7690
}
77-
transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
78-
if (!OidIsValid(transfn))
79-
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
91+
transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
92+
&rettype);
93+
94+
/*
95+
* Return type of transfn (possibly after refinement by
96+
* enforce_generic_type_consistency, if transtype isn't polymorphic)
97+
* must exactly match declared transtype.
98+
*
99+
* In the non-polymorphic-transtype case, it might be okay to allow
100+
* a rettype that's binary-coercible to transtype, but I'm not quite
101+
* convinced that it's either safe or useful. When transtype is
102+
* polymorphic we *must* demand exact equality.
103+
*/
104+
if (rettype != aggTransType)
105+
elog(ERROR, "return type of transition function %s is not %s",
106+
NameListToString(aggtransfnName), format_type_be(aggTransType));
107+
80108
tup = SearchSysCache(PROCOID,
81109
ObjectIdGetDatum(transfn),
82110
0, 0, 0);
83111
if (!HeapTupleIsValid(tup))
84-
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
112+
func_error("AggregateCreate", aggtransfnName,
113+
nargs_transfn, fnArgs, NULL);
85114
proc = (Form_pg_proc) GETSTRUCT(tup);
86-
if (proc->prorettype != aggTransType)
87-
elog(ERROR, "return type of transition function %s is not %s",
88-
NameListToString(aggtransfnName), format_type_be(aggTransType));
89115

90116
/*
91117
* If the transfn is strict and the initval is NULL, make sure input
@@ -105,17 +131,8 @@ AggregateCreate(const char *aggName,
105131
{
106132
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
107133
fnArgs[0] = aggTransType;
108-
finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
109-
if (!OidIsValid(finalfn))
110-
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
111-
tup = SearchSysCache(PROCOID,
112-
ObjectIdGetDatum(finalfn),
113-
0, 0, 0);
114-
if (!HeapTupleIsValid(tup))
115-
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
116-
proc = (Form_pg_proc) GETSTRUCT(tup);
117-
finaltype = proc->prorettype;
118-
ReleaseSysCache(tup);
134+
finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
135+
&finaltype);
119136
}
120137
else
121138
{
@@ -126,6 +143,19 @@ AggregateCreate(const char *aggName,
126143
}
127144
Assert(OidIsValid(finaltype));
128145

146+
/*
147+
* If finaltype (i.e. aggregate return type) is polymorphic,
148+
* basetype must be polymorphic also, else parser will fail to deduce
149+
* result type. (Note: given the previous test on transtype and basetype,
150+
* this cannot happen, unless someone has snuck a finalfn definition
151+
* into the catalogs that itself violates the rule against polymorphic
152+
* result with no polymorphic input.)
153+
*/
154+
if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) &&
155+
!(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID))
156+
elog(ERROR, "an aggregate returning ANYARRAY or ANYELEMENT "
157+
"must also have one of them as its base type");
158+
129159
/*
130160
* Everything looks okay. Try to create the pg_proc entry for the
131161
* aggregate. (This could fail if there's already a conflicting
@@ -207,3 +237,71 @@ AggregateCreate(const char *aggName,
207237
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
208238
}
209239
}
240+
241+
/*
242+
* lookup_agg_function -- common code for finding both transfn and finalfn
243+
*/
244+
static Oid
245+
lookup_agg_function(List *fnName,
246+
int nargs,
247+
Oid *input_types,
248+
Oid *rettype)
249+
{
250+
Oid fnOid;
251+
bool retset;
252+
Oid *true_oid_array;
253+
FuncDetailCode fdresult;
254+
255+
/*
256+
* func_get_detail looks up the function in the catalogs, does
257+
* disambiguation for polymorphic functions, handles inheritance, and
258+
* returns the funcid and type and set or singleton status of the
259+
* function's return value. it also returns the true argument types
260+
* to the function.
261+
*/
262+
fdresult = func_get_detail(fnName, NIL, nargs, input_types,
263+
&fnOid, rettype, &retset,
264+
&true_oid_array);
265+
266+
/* only valid case is a normal function not returning a set */
267+
if (fdresult != FUNCDETAIL_NORMAL ||
268+
!OidIsValid(fnOid) ||
269+
retset)
270+
func_error("AggregateCreate", fnName, nargs, input_types, NULL);
271+
272+
/*
273+
* If the given type(s) are all polymorphic, there's nothing we
274+
* can check. Otherwise, enforce consistency, and possibly refine
275+
* the result type.
276+
*/
277+
if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) &&
278+
(nargs == 1 ||
279+
(input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID)))
280+
{
281+
/* nothing to check here */
282+
}
283+
else
284+
{
285+
*rettype = enforce_generic_type_consistency(input_types,
286+
true_oid_array,
287+
nargs,
288+
*rettype);
289+
}
290+
291+
/*
292+
* func_get_detail will find functions requiring run-time argument type
293+
* coercion, but nodeAgg.c isn't prepared to deal with that
294+
*/
295+
if (true_oid_array[0] != ANYARRAYOID &&
296+
true_oid_array[0] != ANYELEMENTOID &&
297+
!IsBinaryCoercible(input_types[0], true_oid_array[0]))
298+
func_error("AggregateCreate", fnName, nargs, input_types, NULL);
299+
300+
if (nargs == 2 &&
301+
true_oid_array[1] != ANYARRAYOID &&
302+
true_oid_array[1] != ANYELEMENTOID &&
303+
!IsBinaryCoercible(input_types[1], true_oid_array[1]))
304+
func_error("AggregateCreate", fnName, nargs, input_types, NULL);
305+
306+
return fnOid;
307+
}

src/backend/commands/aggregatecmds.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.8 2003/06/27 14:45:27 petere Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.9 2003/07/01 19:10:52 tgl Exp $
1313
*
1414
* DESCRIPTION
1515
* The "DefineFoo" routines take the parse tree and pick out the
@@ -120,7 +120,9 @@ DefineAggregate(List *names, List *parameters)
120120
baseTypeId = typenameTypeId(baseType);
121121

122122
transTypeId = typenameTypeId(transType);
123-
if (get_typtype(transTypeId) == 'p')
123+
if (get_typtype(transTypeId) == 'p' &&
124+
transTypeId != ANYARRAYOID &&
125+
transTypeId != ANYELEMENTOID)
124126
elog(ERROR, "Aggregate transition datatype cannot be %s",
125127
format_type_be(transTypeId));
126128

src/backend/executor/nodeAgg.c

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* Portions Copyright (c) 1994, Regents of the University of California
4646
*
4747
* IDENTIFICATION
48-
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.109 2003/06/25 21:30:28 momjian Exp $
48+
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.110 2003/07/01 19:10:52 tgl Exp $
4949
*
5050
*-------------------------------------------------------------------------
5151
*/
@@ -59,6 +59,7 @@
5959
#include "executor/nodeAgg.h"
6060
#include "miscadmin.h"
6161
#include "optimizer/clauses.h"
62+
#include "parser/parse_agg.h"
6263
#include "parser/parse_coerce.h"
6364
#include "parser/parse_expr.h"
6465
#include "parser/parse_oper.h"
@@ -1182,11 +1183,15 @@ ExecInitAgg(Agg *node, EState *estate)
11821183
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(alist);
11831184
Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
11841185
AggStatePerAgg peraggstate;
1186+
Oid inputType;
11851187
HeapTuple aggTuple;
11861188
Form_pg_aggregate aggform;
1189+
Oid aggtranstype;
11871190
AclResult aclresult;
11881191
Oid transfn_oid,
11891192
finalfn_oid;
1193+
Expr *transfnexpr,
1194+
*finalfnexpr;
11901195
Datum textInitVal;
11911196
int i;
11921197

@@ -1217,6 +1222,13 @@ ExecInitAgg(Agg *node, EState *estate)
12171222
peraggstate->aggrefstate = aggrefstate;
12181223
peraggstate->aggref = aggref;
12191224

1225+
/*
1226+
* Get actual datatype of the input. We need this because it may
1227+
* be different from the agg's declared input type, when the agg
1228+
* accepts ANY (eg, COUNT(*)) or ANYARRAY or ANYELEMENT.
1229+
*/
1230+
inputType = exprType((Node *) aggref->target);
1231+
12201232
aggTuple = SearchSysCache(AGGFNOID,
12211233
ObjectIdGetDatum(aggref->aggfnoid),
12221234
0, 0, 0);
@@ -1231,10 +1243,47 @@ ExecInitAgg(Agg *node, EState *estate)
12311243
if (aclresult != ACLCHECK_OK)
12321244
aclcheck_error(aclresult, get_func_name(aggref->aggfnoid));
12331245

1246+
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
1247+
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
1248+
1249+
/* resolve actual type of transition state, if polymorphic */
1250+
aggtranstype = aggform->aggtranstype;
1251+
if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
1252+
{
1253+
/* have to fetch the agg's declared input type... */
1254+
Oid agg_arg_types[FUNC_MAX_ARGS];
1255+
int agg_nargs;
1256+
1257+
(void) get_func_signature(aggref->aggfnoid,
1258+
agg_arg_types, &agg_nargs);
1259+
Assert(agg_nargs == 1);
1260+
aggtranstype = resolve_generic_type(aggtranstype,
1261+
inputType,
1262+
agg_arg_types[0]);
1263+
}
1264+
1265+
/* build expression trees using actual argument & result types */
1266+
build_aggregate_fnexprs(inputType,
1267+
aggtranstype,
1268+
aggref->aggtype,
1269+
transfn_oid,
1270+
finalfn_oid,
1271+
&transfnexpr,
1272+
&finalfnexpr);
1273+
1274+
fmgr_info(transfn_oid, &peraggstate->transfn);
1275+
peraggstate->transfn.fn_expr = (Node *) transfnexpr;
1276+
1277+
if (OidIsValid(finalfn_oid))
1278+
{
1279+
fmgr_info(finalfn_oid, &peraggstate->finalfn);
1280+
peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
1281+
}
1282+
12341283
get_typlenbyval(aggref->aggtype,
12351284
&peraggstate->resulttypeLen,
12361285
&peraggstate->resulttypeByVal);
1237-
get_typlenbyval(aggform->aggtranstype,
1286+
get_typlenbyval(aggtranstype,
12381287
&peraggstate->transtypeLen,
12391288
&peraggstate->transtypeByVal);
12401289

@@ -1250,14 +1299,7 @@ ExecInitAgg(Agg *node, EState *estate)
12501299
peraggstate->initValue = (Datum) 0;
12511300
else
12521301
peraggstate->initValue = GetAggInitVal(textInitVal,
1253-
aggform->aggtranstype);
1254-
1255-
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
1256-
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
1257-
1258-
fmgr_info(transfn_oid, &peraggstate->transfn);
1259-
if (OidIsValid(finalfn_oid))
1260-
fmgr_info(finalfn_oid, &peraggstate->finalfn);
1302+
aggtranstype);
12611303

12621304
/*
12631305
* If the transfn is strict and the initval is NULL, make sure
@@ -1268,26 +1310,13 @@ ExecInitAgg(Agg *node, EState *estate)
12681310
*/
12691311
if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
12701312
{
1271-
/*
1272-
* Note: use the type from the input expression here, not from
1273-
* pg_proc.proargtypes, because the latter might be 0.
1274-
* (Consider COUNT(*).)
1275-
*/
1276-
Oid inputType = exprType((Node *) aggref->target);
1277-
1278-
if (!IsBinaryCoercible(inputType, aggform->aggtranstype))
1313+
if (!IsBinaryCoercible(inputType, aggtranstype))
12791314
elog(ERROR, "Aggregate %u needs to have compatible input type and transition type",
12801315
aggref->aggfnoid);
12811316
}
12821317

12831318
if (aggref->aggdistinct)
12841319
{
1285-
/*
1286-
* Note: use the type from the input expression here, not from
1287-
* pg_proc.proargtypes, because the latter might be a pseudotype.
1288-
* (Consider COUNT(*).)
1289-
*/
1290-
Oid inputType = exprType((Node *) aggref->target);
12911320
Oid eq_function;
12921321

12931322
/* We don't implement DISTINCT aggs in the HASHED case */

src/backend/nodes/makefuncs.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.39 2003/05/06 00:20:32 tgl Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.40 2003/07/01 19:10:52 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -251,3 +251,24 @@ makeTypeName(char *typnam)
251251
n->typmod = -1;
252252
return n;
253253
}
254+
255+
/*
256+
* makeFuncExpr -
257+
* build an expression tree representing a function call.
258+
*
259+
* The argument expressions must have been transformed already.
260+
*/
261+
FuncExpr *
262+
makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat)
263+
{
264+
FuncExpr *funcexpr;
265+
266+
funcexpr = makeNode(FuncExpr);
267+
funcexpr->funcid = funcid;
268+
funcexpr->funcresulttype = rettype;
269+
funcexpr->funcretset = false; /* only allowed case here */
270+
funcexpr->funcformat = fformat;
271+
funcexpr->args = args;
272+
273+
return funcexpr;
274+
}

0 commit comments

Comments
 (0)