diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 93 | ||||
-rw-r--r-- | src/backend/commands/aggregatecmds.c | 8 | ||||
-rw-r--r-- | src/backend/executor/nodeAgg.c | 48 | ||||
-rw-r--r-- | src/backend/executor/nodeWindowAgg.c | 33 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 24 |
5 files changed, 129 insertions, 77 deletions
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 633b8f1d6ac..d99c2e5edae 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -60,6 +60,8 @@ AggregateCreate(const char *aggName, List *aggmtransfnName, List *aggminvtransfnName, List *aggmfinalfnName, + bool finalfnExtraArgs, + bool mfinalfnExtraArgs, List *aggsortopName, Oid aggTransType, int32 aggTransSpace, @@ -344,48 +346,46 @@ AggregateCreate(const char *aggName, ReleaseSysCache(tup); } - /* - * Set up fnArgs for looking up finalfn(s) - * - * For ordinary aggs, the finalfn just takes the transtype. For - * ordered-set aggs, it takes the transtype plus all args. (The - * aggregated args are useless at runtime, and are actually passed as - * NULLs, but we may need them in the function signature to allow - * resolution of a polymorphic agg's result type.) - */ - fnArgs[0] = aggTransType; - if (AGGKIND_IS_ORDERED_SET(aggKind)) - { - nargs_finalfn = numArgs + 1; - memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); - } - else - { - nargs_finalfn = 1; - /* variadic-ness of the aggregate doesn't affect finalfn */ - variadicArgType = InvalidOid; - } - /* handle finalfn, if supplied */ if (aggfinalfnName) { + /* + * If finalfnExtraArgs is specified, the transfn takes the transtype + * plus all args; otherwise, it just takes the transtype plus any + * direct args. (Non-direct args are useless at runtime, and are + * actually passed as NULLs, but we may need them in the function + * signature to allow resolution of a polymorphic agg's result type.) + */ + Oid ffnVariadicArgType = variadicArgType; + + fnArgs[0] = aggTransType; + memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); + if (finalfnExtraArgs) + nargs_finalfn = numArgs + 1; + else + { + nargs_finalfn = numDirectArgs + 1; + if (numDirectArgs < numArgs) + { + /* variadic argument doesn't affect finalfn */ + ffnVariadicArgType = InvalidOid; + } + } + finalfn = lookup_agg_function(aggfinalfnName, nargs_finalfn, - fnArgs, variadicArgType, + fnArgs, ffnVariadicArgType, &finaltype); /* - * The finalfn of an ordered-set agg will certainly be passed at least - * one null argument, so complain if it's strict. Nothing bad would - * happen at runtime (you'd just get a null result), but it's surely - * not what the user wants, so let's complain now. - * - * Note: it's likely that a strict transfn would also be a mistake, - * but the case isn't quite so airtight, so we let that pass. + * When finalfnExtraArgs is specified, the finalfn will certainly be + * passed at least one null argument, so complain if it's strict. + * Nothing bad would happen at runtime (you'd just get a null result), + * but it's surely not what the user wants, so let's complain now. */ - if (AGGKIND_IS_ORDERED_SET(aggKind) && func_strict(finalfn)) + if (finalfnExtraArgs && func_strict(finalfn)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("final function of an ordered-set aggregate must not be declared STRICT"))); + errmsg("final function with extra arguments must not be declared STRICT"))); } else { @@ -434,21 +434,34 @@ AggregateCreate(const char *aggName, if (aggmfinalfnName) { /* - * The arguments are the same as for the regular finalfn, except - * that the transition data type might be different. So re-use - * the fnArgs values set up above, except for that one. + * The arguments are figured the same way as for the regular + * finalfn, but using aggmTransType and mfinalfnExtraArgs. */ + Oid ffnVariadicArgType = variadicArgType; + fnArgs[0] = aggmTransType; + memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); + if (mfinalfnExtraArgs) + nargs_finalfn = numArgs + 1; + else + { + nargs_finalfn = numDirectArgs + 1; + if (numDirectArgs < numArgs) + { + /* variadic argument doesn't affect finalfn */ + ffnVariadicArgType = InvalidOid; + } + } mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn, - fnArgs, variadicArgType, + fnArgs, ffnVariadicArgType, &rettype); - /* As above, check strictness if it's an ordered-set agg */ - if (AGGKIND_IS_ORDERED_SET(aggKind) && func_strict(mfinalfn)) + /* As above, check strictness if mfinalfnExtraArgs is given */ + if (mfinalfnExtraArgs && func_strict(mfinalfn)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("final function of an ordered-set aggregate must not be declared STRICT"))); + errmsg("final function with extra arguments must not be declared STRICT"))); } else { @@ -554,6 +567,8 @@ AggregateCreate(const char *aggName, values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn); values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn); values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn); + values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs); + values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace); diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 9714112f6d4..a73d7094376 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -64,6 +64,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, List *mtransfuncName = NIL; List *minvtransfuncName = NIL; List *mfinalfuncName = NIL; + bool finalfuncExtraArgs = false; + bool mfinalfuncExtraArgs = false; List *sortoperatorName = NIL; TypeName *baseType = NULL; TypeName *transType = NULL; @@ -128,6 +130,10 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, minvtransfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "mfinalfunc") == 0) mfinalfuncName = defGetQualifiedName(defel); + else if (pg_strcasecmp(defel->defname, "finalfunc_extra") == 0) + finalfuncExtraArgs = defGetBoolean(defel); + else if (pg_strcasecmp(defel->defname, "mfinalfunc_extra") == 0) + mfinalfuncExtraArgs = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "sortop") == 0) sortoperatorName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "basetype") == 0) @@ -380,6 +386,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, mtransfuncName, /* fwd trans function name */ minvtransfuncName, /* inv trans function name */ mfinalfuncName, /* final function name */ + finalfuncExtraArgs, + mfinalfuncExtraArgs, sortoperatorName, /* sort operator name */ transTypeId, /* transition data type */ transSpace, /* transition space */ diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index d60845bcd34..186c319a3a2 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -37,11 +37,12 @@ * * Ordered-set aggregates are treated specially in one other way: we * evaluate any "direct" arguments and pass them to the finalfunc along - * with the transition value. In addition, NULL placeholders are - * provided to match the remaining finalfunc arguments, which correspond - * to the aggregated expressions. (These arguments have no use at - * runtime, but may be needed to allow resolution of a polymorphic - * aggregate's result type.) + * with the transition value. + * + * A finalfunc can have additional arguments beyond the transvalue and + * any "direct" arguments, corresponding to the input arguments of the + * aggregate. These are always just passed as NULL. Such arguments may be + * needed to allow resolution of a polymorphic aggregate's result type. * * We compute aggregate input expressions and run the transition functions * in a temporary econtext (aggstate->tmpcontext). This is reset at @@ -151,6 +152,14 @@ typedef struct AggStatePerAggData */ int numTransInputs; + /* + * Number of arguments to pass to the finalfn. This is always at least 1 + * (the transition state value) plus any ordered-set direct args. If the + * finalfn wants extra args then we pass nulls corresponding to the + * aggregated input columns. + */ + int numFinalArgs; + /* Oids of transfer functions */ Oid transfn_oid; Oid finalfn_oid; /* may be InvalidOid */ @@ -797,6 +806,8 @@ finalize_aggregate(AggState *aggstate, /* * Evaluate any direct arguments. We do this even if there's no finalfn * (which is unlikely anyway), so that side-effects happen as expected. + * The direct arguments go into arg positions 1 and up, leaving position 0 + * for the transition state value. */ i = 1; foreach(lc, peraggstate->aggrefstate->aggdirectargs) @@ -816,19 +827,7 @@ finalize_aggregate(AggState *aggstate, */ if (OidIsValid(peraggstate->finalfn_oid)) { - int numFinalArgs; - - /* - * Identify number of arguments being passed to the finalfn. For a - * plain agg it's just one (the transition state value). For - * ordered-set aggs we also pass the direct argument(s), plus nulls - * corresponding to the aggregate-input columns. - */ - if (AGGKIND_IS_ORDERED_SET(peraggstate->aggref->aggkind)) - numFinalArgs = peraggstate->numArguments + 1; - else - numFinalArgs = 1; - Assert(i <= numFinalArgs); + int numFinalArgs = peraggstate->numFinalArgs; /* set up aggstate->curperagg for AggGetAggref() */ aggstate->curperagg = peraggstate; @@ -844,12 +843,11 @@ finalize_aggregate(AggState *aggstate, anynull |= pergroupstate->transValueIsNull; /* Fill any remaining argument positions with nulls */ - while (i < numFinalArgs) + for (; i < numFinalArgs; i++) { fcinfo.arg[i] = (Datum) 0; fcinfo.argnull[i] = true; anynull = true; - i++; } if (fcinfo.flinfo->fn_strict && anynull) @@ -1776,12 +1774,18 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) numInputs = list_length(aggref->args); peraggstate->numInputs = numInputs; - /* Detect how many columns to pass to the transfn */ + /* Detect how many arguments to pass to the transfn */ if (AGGKIND_IS_ORDERED_SET(aggref->aggkind)) peraggstate->numTransInputs = numInputs; else peraggstate->numTransInputs = numArguments; + /* Detect how many arguments to pass to the finalfn */ + if (aggform->aggfinalextra) + peraggstate->numFinalArgs = numArguments + 1; + else + peraggstate->numFinalArgs = numDirectArgs + 1; + /* resolve actual type of transition state, if polymorphic */ aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid, aggform->aggtranstype, @@ -1792,7 +1796,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) build_aggregate_fnexprs(inputTypes, numArguments, numDirectArgs, - AGGKIND_IS_ORDERED_SET(aggref->aggkind), + peraggstate->numFinalArgs, aggref->aggvariadic, aggtranstype, aggref->aggtype, diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index 2fcc630a925..40a925331c9 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -116,6 +116,8 @@ typedef struct WindowStatePerAggData FmgrInfo invtransfn; FmgrInfo finalfn; + int numFinalArgs; /* number of arguments to pass to finalfn */ + /* * initial value from pg_aggregate entry */ @@ -557,14 +559,28 @@ finalize_windowaggregate(WindowAggState *winstate, */ if (OidIsValid(peraggstate->finalfn_oid)) { + int numFinalArgs = peraggstate->numFinalArgs; FunctionCallInfoData fcinfo; + bool anynull; + int i; - InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), 1, + InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), + numFinalArgs, perfuncstate->winCollation, (void *) winstate, NULL); fcinfo.arg[0] = peraggstate->transValue; fcinfo.argnull[0] = peraggstate->transValueIsNull; - if (fcinfo.flinfo->fn_strict && peraggstate->transValueIsNull) + anynull = peraggstate->transValueIsNull; + + /* Fill any remaining argument positions with nulls */ + for (i = 1; i < numFinalArgs; i++) + { + fcinfo.arg[i] = (Datum) 0; + fcinfo.argnull[i] = true; + anynull = true; + } + + if (fcinfo.flinfo->fn_strict && anynull) { /* don't call a strict function with NULL inputs */ *result = (Datum) 0; @@ -2089,6 +2105,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, Oid transfn_oid, invtransfn_oid, finalfn_oid; + bool finalextra; Expr *transfnexpr, *invtransfnexpr, *finalfnexpr; @@ -2127,6 +2144,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn; peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn; peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn; + finalextra = aggform->aggmfinalextra; aggtranstype = aggform->aggmtranstype; initvalAttNo = Anum_pg_aggregate_aggminitval; } @@ -2135,6 +2153,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid; peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; + finalextra = aggform->aggfinalextra; aggtranstype = aggform->aggtranstype; initvalAttNo = Anum_pg_aggregate_agginitval; } @@ -2185,6 +2204,12 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, } } + /* Detect how many arguments to pass to the finalfn */ + if (finalextra) + peraggstate->numFinalArgs = numArguments + 1; + else + peraggstate->numFinalArgs = 1; + /* resolve actual type of transition state, if polymorphic */ aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid, aggtranstype, @@ -2195,7 +2220,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, build_aggregate_fnexprs(inputTypes, numArguments, 0, /* no ordered-set window functions yet */ - false, + peraggstate->numFinalArgs, false, /* no variadic window functions yet */ aggtranstype, wfunc->wintype, @@ -2207,6 +2232,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, &invtransfnexpr, &finalfnexpr); + /* set up infrastructure for calling the transfn(s) and finalfn */ fmgr_info(transfn_oid, &peraggstate->transfn); fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn); @@ -2222,6 +2248,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn); } + /* get info about relevant datatypes */ get_typlenbyval(wfunc->wintype, &peraggstate->resulttypeLen, &peraggstate->resulttypeByVal); diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 272d27f919e..9af43d2a328 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -1199,7 +1199,7 @@ void build_aggregate_fnexprs(Oid *agg_input_types, int agg_num_inputs, int agg_num_direct_inputs, - bool agg_ordered_set, + int num_finalfn_inputs, bool agg_variadic, Oid agg_state_type, Oid agg_result_type, @@ -1292,19 +1292,17 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->location = -1; args = list_make1(argp); - if (agg_ordered_set) + /* finalfn may take additional args, which match agg's input types */ + for (i = 0; i < num_finalfn_inputs - 1; i++) { - for (i = 0; i < agg_num_inputs; i++) - { - argp = makeNode(Param); - argp->paramkind = PARAM_EXEC; - argp->paramid = -1; - argp->paramtype = agg_input_types[i]; - argp->paramtypmod = -1; - argp->paramcollid = agg_input_collation; - argp->location = -1; - args = lappend(args, argp); - } + argp = makeNode(Param); + argp->paramkind = PARAM_EXEC; + argp->paramid = -1; + argp->paramtype = agg_input_types[i]; + argp->paramtypmod = -1; + argp->paramcollid = agg_input_collation; + argp->location = -1; + args = lappend(args, argp); } *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid, |