diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml index eaa410b..f0e4407 100644 --- a/doc/src/sgml/ref/create_aggregate.sgml +++ b/doc/src/sgml/ref/create_aggregate.sgml @@ -27,6 +27,7 @@ CREATE AGGREGATE name ( [ state_data_size ] [ , FINALFUNC = ffunc ] [ , FINALFUNC_EXTRA ] + [ , CFUNC = cfunc ] [ , INITCOND = initial_condition ] [ , MSFUNC = msfunc ] [ , MINVFUNC = minvfunc ] @@ -45,6 +46,7 @@ CREATE AGGREGATE name ( [ [ state_data_size ] [ , FINALFUNC = ffunc ] [ , FINALFUNC_EXTRA ] + [ , CFUNC = cfunc ] [ , INITCOND = initial_condition ] [ , HYPOTHETICAL ] ) @@ -58,6 +60,7 @@ CREATE AGGREGATE name ( [ , SSPACE = state_data_size ] [ , FINALFUNC = ffunc ] [ , FINALFUNC_EXTRA ] + [ , CFUNC = cfunc ] [ , INITCOND = initial_condition ] [ , MSFUNC = msfunc ] [ , MINVFUNC = minvfunc ] @@ -105,12 +108,15 @@ CREATE AGGREGATE name ( functions: a state transition function sfunc, - and an optional final calculation function - ffunc. + an optional final calculation function + ffunc, + and an optional combine function + cfunc. These are used as follows: sfunc( internal-state, next-data-values ) ---> next-internal-state ffunc( internal-state ) ---> aggregate-value +cfunc( internal-state, internal-state ) ---> next-internal-state @@ -128,6 +134,13 @@ CREATE AGGREGATE name ( + An aggregate function may also supply a combining function, which allows + the aggregation process to be broken down into multiple steps. This + facilitates query optimization techniques such as parallel query, + pre-join aggregation and aggregation while sorting. + + + An aggregate function can provide an initial condition, that is, an initial value for the internal state value. This is specified and stored in the database as a value of type diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 121c27f..820d982 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -57,6 +57,7 @@ AggregateCreate(const char *aggName, Oid variadicArgType, List *aggtransfnName, List *aggfinalfnName, + List *aggcombinefnName, List *aggmtransfnName, List *aggminvtransfnName, List *aggmfinalfnName, @@ -77,6 +78,7 @@ AggregateCreate(const char *aggName, Form_pg_proc proc; Oid transfn; Oid finalfn = InvalidOid; /* can be omitted */ + Oid combinefn = InvalidOid; /* can be omitted */ Oid mtransfn = InvalidOid; /* can be omitted */ Oid minvtransfn = InvalidOid; /* can be omitted */ Oid mfinalfn = InvalidOid; /* can be omitted */ @@ -396,6 +398,20 @@ AggregateCreate(const char *aggName, } Assert(OidIsValid(finaltype)); + /* handle the combinefn, if supplied */ + if (aggcombinefnName) + { + /* + * Combine function must have 2 argument, each of which is the + * trans type + */ + fnArgs[0] = aggTransType; + fnArgs[1] = aggTransType; + + combinefn = lookup_agg_function(aggcombinefnName, 2, fnArgs, + variadicArgType, &finaltype); + } + /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 894c89d..035882e 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -61,6 +61,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, char aggKind = AGGKIND_NORMAL; List *transfuncName = NIL; List *finalfuncName = NIL; + List *combinefuncName = NIL; List *mtransfuncName = NIL; List *minvtransfuncName = NIL; List *mfinalfuncName = NIL; @@ -124,6 +125,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, transfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "finalfunc") == 0) finalfuncName = defGetQualifiedName(defel); + else if (pg_strcasecmp(defel->defname, "cfunc") == 0) + combinefuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "msfunc") == 0) mtransfuncName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "minvfunc") == 0) @@ -383,6 +386,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, variadicArgType, transfuncName, /* step function name */ finalfuncName, /* final function name */ + combinefuncName, /* combine function name */ mtransfuncName, /* fwd trans function name */ minvtransfuncName, /* inv trans function name */ mfinalfuncName, /* final function name */ diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index f0d9e94..df9d2e8 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -904,25 +904,38 @@ ExplainNode(PlanState *planstate, List *ancestors, pname = sname = "Group"; break; case T_Agg: - sname = "Aggregate"; - switch (((Agg *) plan)->aggstrategy) { - case AGG_PLAIN: - pname = "Aggregate"; - strategy = "Plain"; - break; - case AGG_SORTED: - pname = "GroupAggregate"; - strategy = "Sorted"; - break; - case AGG_HASHED: - pname = "HashAggregate"; - strategy = "Hashed"; - break; - default: - pname = "Aggregate ???"; - strategy = "???"; - break; + char *modifier; + Agg *agg = (Agg *) plan; + + sname = "Aggregate"; + + if (agg->finalizeAggs == false) + modifier = "Partial "; + else if (agg->combineStates == true) + modifier = "Finalize "; + else + modifier = ""; + + switch (agg->aggstrategy) + { + case AGG_PLAIN: + pname = psprintf("%sAggregate", modifier); + strategy = "Plain"; + break; + case AGG_SORTED: + pname = psprintf("%sGroupAggregate", modifier); + strategy = "Sorted"; + break; + case AGG_HASHED: + pname = psprintf("%sHashAggregate", modifier); + strategy = "Hashed"; + break; + default: + pname = "Aggregate ???"; + strategy = "???"; + break; + } } break; case T_WindowAgg: diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 2e36855..0336220 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -3,15 +3,24 @@ * nodeAgg.c * Routines to handle aggregate nodes. * - * ExecAgg evaluates each aggregate in the following steps: + * ExecAgg normally evaluates each aggregate in the following steps: * * transvalue = initcond * foreach input_tuple do * transvalue = transfunc(transvalue, input_value(s)) * result = finalfunc(transvalue, direct_argument(s)) * - * If a finalfunc is not supplied then the result is just the ending - * value of transvalue. + * If a finalfunc is not supplied or finalizeAggs is false, then the result + * is just the ending value of transvalue. + * + * If combineStates is true then we assume that input values are other + * transvalues. In this case we use the aggregate's combinefunc to 'add' + * the passed in transvalue to the transvalue being operated on. This allows + * aggregation to happen in multiple stages. 'combineStates' will only be + * true if another nodeAgg is below this one in the plan tree. + * + * 'finalizeAggs' should be false for all nodeAggs apart from the upper most + * one in the plan tree. * * If a normal aggregate call specifies DISTINCT or ORDER BY, we sort the * input tuples and eliminate duplicates (if required) before performing @@ -197,7 +206,7 @@ typedef struct AggStatePerTransData */ int numTransInputs; - /* Oid of the state transition function */ + /* Oid of the state transition or combine function */ Oid transfn_oid; /* Oid of state value's datatype */ @@ -209,8 +218,8 @@ typedef struct AggStatePerTransData List *aggdirectargs; /* states of direct-argument expressions */ /* - * fmgr lookup data for transition function. Note in particular that the - * fn_strict flag is kept here. + * fmgr lookup data for transition function or combination function. Note + * in particular that the fn_strict flag is kept here. */ FmgrInfo transfn; @@ -421,6 +430,10 @@ static void advance_transition_function(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroupstate); static void advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup); +static void advance_combination_function(AggState *aggstate, + AggStatePerTrans pertrans, + AggStatePerGroup pergroupstate); +static void combine_aggregates(AggState *aggstate, AggStatePerGroup pergroup); static void process_ordered_aggregate_single(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroupstate); @@ -796,6 +809,8 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup) int numGroupingSets = Max(aggstate->phase->numsets, 1); int numTrans = aggstate->numtrans; + Assert(!aggstate->combineStates); + for (transno = 0; transno < numTrans; transno++) { AggStatePerTrans pertrans = &aggstate->pertrans[transno]; @@ -879,6 +894,109 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup) } } +static void +combine_aggregates(AggState *aggstate, AggStatePerGroup pergroup) +{ + int transno; + int numTrans = aggstate->numtrans; + + /* combine not supported with grouping sets */ + Assert(aggstate->phase->numsets == 0); + Assert(aggstate->combineStates); + + for (transno = 0; transno < numTrans; transno++) + { + AggStatePerTrans pertrans = &aggstate->pertrans[transno]; + TupleTableSlot *slot; + FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo; + AggStatePerGroup pergroupstate = &pergroup[transno]; + + /* Evaluate the current input expressions for this aggregate */ + slot = ExecProject(pertrans->evalproj, NULL); + Assert(slot->tts_nvalid >= 1); + + fcinfo->arg[1] = slot->tts_values[0]; + fcinfo->argnull[1] = slot->tts_isnull[0]; + + advance_combination_function(aggstate, pertrans, pergroupstate); + } +} + +/* + * Perform combination of states between 2 aggregate states. Effectively this + * 'adds' two states together by whichever logic is defined in the aggregate + * function's combine function. + * + * Note that in this case transfn is set to the combination function. This + * perhaps should be changed to avoid confusion, but one field is ok for now + * as they'll never be needed at the same time. + */ +static void +advance_combination_function(AggState *aggstate, + AggStatePerTrans pertrans, + AggStatePerGroup pergroupstate) +{ + FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo; + MemoryContext oldContext; + Datum newVal; + + if (pertrans->transfn.fn_strict) + { + /* if we're asked to merge to a NULL state, then do nothing */ + if (fcinfo->argnull[1]) + return; + + if (pergroupstate->noTransValue) + { + pergroupstate->transValue = fcinfo->arg[1]; + pergroupstate->transValueIsNull = false; + return; + } + } + + /* We run the combine functions in per-input-tuple memory context */ + oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); + + /* set up aggstate->curpertrans for AggGetAggref() */ + aggstate->curpertrans = pertrans; + + /* + * OK to call the combine function + */ + fcinfo->arg[0] = pergroupstate->transValue; + fcinfo->argnull[0] = pergroupstate->transValueIsNull; + fcinfo->isnull = false; /* just in case combine func doesn't set it */ + + newVal = FunctionCallInvoke(fcinfo); + + aggstate->curpertrans = NULL; + + /* + * If pass-by-ref datatype, must copy the new value into aggcontext and + * pfree the prior transValue. But if the combine function returned a + * pointer to its first input, we don't need to do anything. + */ + if (!pertrans->transtypeByVal && + DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue)) + { + if (!fcinfo->isnull) + { + MemoryContextSwitchTo(aggstate->aggcontexts[aggstate->current_set]->ecxt_per_tuple_memory); + newVal = datumCopy(newVal, + pertrans->transtypeByVal, + pertrans->transtypeLen); + } + if (!pergroupstate->transValueIsNull) + pfree(DatumGetPointer(pergroupstate->transValue)); + } + + pergroupstate->transValue = newVal; + pergroupstate->transValueIsNull = fcinfo->isnull; + + MemoryContextSwitchTo(oldContext); + +} + /* * Run the transition function for a DISTINCT or ORDER BY aggregate @@ -1278,8 +1396,14 @@ finalize_aggregates(AggState *aggstate, pergroupstate); } - finalize_aggregate(aggstate, peragg, pergroupstate, - &aggvalues[aggno], &aggnulls[aggno]); + if (aggstate->finalizeAggs) + finalize_aggregate(aggstate, peragg, pergroupstate, + &aggvalues[aggno], &aggnulls[aggno]); + else + { + aggvalues[aggno] = pergroupstate->transValue; + aggnulls[aggno] = pergroupstate->transValueIsNull; + } } } @@ -1294,9 +1418,11 @@ project_aggregates(AggState *aggstate) ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; /* - * Check the qual (HAVING clause); if the group does not match, ignore it. + * iif performing the final aggregate stage we'll check the qual (HAVING + * clause); if the group does not match, ignore it. */ - if (ExecQual(aggstate->ss.ps.qual, econtext, false)) + if (aggstate->finalizeAggs == false || + ExecQual(aggstate->ss.ps.qual, econtext, false)) { /* * Form and return or store a projection tuple using the aggregate @@ -1811,7 +1937,10 @@ agg_retrieve_direct(AggState *aggstate) */ for (;;) { - advance_aggregates(aggstate, pergroup); + if (!aggstate->combineStates) + advance_aggregates(aggstate, pergroup); + else + combine_aggregates(aggstate, pergroup); /* Reset per-input-tuple context after each tuple */ ResetExprContext(tmpcontext); @@ -1919,7 +2048,10 @@ agg_fill_hash_table(AggState *aggstate) entry = lookup_hash_entry(aggstate, outerslot); /* Advance the aggregates */ - advance_aggregates(aggstate, entry->pergroup); + if (!aggstate->combineStates) + advance_aggregates(aggstate, entry->pergroup); + else + combine_aggregates(aggstate, entry->pergroup); /* Reset per-input-tuple context after each tuple */ ResetExprContext(tmpcontext); @@ -2051,6 +2183,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) aggstate->pertrans = NULL; aggstate->curpertrans = NULL; aggstate->agg_done = false; + aggstate->combineStates = node->combineStates; + aggstate->finalizeAggs = node->finalizeAggs; aggstate->input_done = false; aggstate->pergroup = NULL; aggstate->grp_firstTuple = NULL; @@ -2402,7 +2536,21 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) get_func_name(aggref->aggfnoid)); InvokeFunctionExecuteHook(aggref->aggfnoid); - transfn_oid = aggform->aggtransfn; + /* + * if this aggregation is performing state combines, then instead of + * using the transition function, we'll use the combine function + */ + if (aggstate->combineStates) + { + transfn_oid = aggform->aggcombinefn; + + /* If not set then the planner messed up */ + if (!OidIsValid(transfn_oid)) + elog(ERROR, "combinefn not set during aggregate state combine phase"); + } + else + transfn_oid = aggform->aggtransfn; + peragg->finalfn_oid = finalfn_oid = aggform->aggfinalfn; /* Check that aggregate owner has permission to call component fns */ @@ -2583,44 +2731,69 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, pertrans->numTransInputs = numArguments; /* - * Set up infrastructure for calling the transfn + * When combining states, we have no use at all for the aggregate + * function's transfn. Instead we use the combinefn. However we do + * reuse the transfnexpr for the combinefn, perhaps this should change */ - build_aggregate_transfn_expr(inputTypes, - numArguments, - numDirectArgs, - aggref->aggvariadic, - aggtranstype, - aggref->inputcollid, - aggtransfn, - InvalidOid, /* invtrans is not needed here */ - &transfnexpr, - NULL); - fmgr_info(aggtransfn, &pertrans->transfn); - fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn); - - InitFunctionCallInfoData(pertrans->transfn_fcinfo, - &pertrans->transfn, - pertrans->numTransInputs + 1, - pertrans->aggCollation, - (void *) aggstate, NULL); + if (aggstate->combineStates) + { + build_aggregate_combinefn_expr(aggref->aggvariadic, + aggtranstype, + aggref->inputcollid, + aggtransfn, + &transfnexpr); + fmgr_info(aggtransfn, &pertrans->transfn); + fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn); + + InitFunctionCallInfoData(pertrans->transfn_fcinfo, + &pertrans->transfn, + 2, + pertrans->aggCollation, + (void *) aggstate, NULL); - /* - * If the transfn is strict and the initval is NULL, make sure input type - * and transtype are the same (or at least binary-compatible), so that - * it's OK to use the first aggregated input value as the initial - * transValue. This should have been checked at agg definition time, but - * we must check again in case the transfn's strictness property has been - * changed. - */ - if (pertrans->transfn.fn_strict && pertrans->initValueIsNull) + } + else { - if (numArguments <= numDirectArgs || - !IsBinaryCoercible(inputTypes[numDirectArgs], - aggtranstype)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("aggregate %u needs to have compatible input type and transition type", - aggref->aggfnoid))); + /* + * Set up infrastructure for calling the transfn + */ + build_aggregate_transfn_expr(inputTypes, + numArguments, + numDirectArgs, + aggref->aggvariadic, + aggtranstype, + aggref->inputcollid, + aggtransfn, + InvalidOid, /* invtrans is not needed here */ + &transfnexpr, + NULL); + fmgr_info(aggtransfn, &pertrans->transfn); + fmgr_info_set_expr((Node *) transfnexpr, &pertrans->transfn); + + InitFunctionCallInfoData(pertrans->transfn_fcinfo, + &pertrans->transfn, + pertrans->numTransInputs + 1, + pertrans->aggCollation, + (void *) aggstate, NULL); + + /* + * If the transfn is strict and the initval is NULL, make sure input type + * and transtype are the same (or at least binary-compatible), so that + * it's OK to use the first aggregated input value as the initial + * transValue. This should have been checked at agg definition time, but + * we must check again in case the transfn's strictness property has been + * changed. + */ + if (pertrans->transfn.fn_strict && pertrans->initValueIsNull) + { + if (numArguments <= numDirectArgs || + !IsBinaryCoercible(inputTypes[numDirectArgs], + aggtranstype)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("aggregate %u needs to have compatible input type and transition type", + aggref->aggfnoid))); + } } /* get info about the state value's datatype */ diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 8fc1cfd..3040ca7 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1426,8 +1426,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Generate the plan for the subquery */ rel->subplan = subquery_planner(root->glob, subquery, root, - false, tuple_fraction, - &subroot); + false, !rel->partial_aggregate, + tuple_fraction, &subroot); rel->subroot = subroot; /* Isolate the params needed by this specific subplan */ diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index d107d76..6f0a6f6 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -118,6 +118,7 @@ bool enable_nestloop = true; bool enable_material = true; bool enable_mergejoin = true; bool enable_hashjoin = true; +bool enable_earlygrouping = true; typedef struct { diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index a35c881..c177ca5 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -16,11 +16,25 @@ #include +#include "access/htup_details.h" +#include "catalog/pg_aggregate.h" #include "executor/executor.h" #include "foreign/fdwapi.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/planmain.h" +#include "optimizer/planner.h" +#include "optimizer/tlist.h" +#include "optimizer/var.h" +#include "parser/parse_clause.h" +#include "parser/parse_oper.h" +#include "parser/parsetree.h" +#include "rewrite/rewriteManip.h" +#include "utils/syscache.h" /* Hook for plugins to get control in add_paths_to_joinrel() */ set_join_pathlist_hook_type set_join_pathlist_hook = NULL; @@ -1474,3 +1488,594 @@ select_mergejoin_clauses(PlannerInfo *root, return result_list; } + +static List * +remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved) +{ + List *result = NIL; + ListCell *jl; + + foreach(jl, joinlist) + { + Node *jlnode = (Node *) lfirst(jl); + + if (IsA(jlnode, RangeTblRef)) + { + int varno = ((RangeTblRef *) jlnode)->rtindex; + + if (varno == relid) + (*nremoved)++; + else + result = lappend(result, jlnode); + } + else if (IsA(jlnode, List)) + { + /* Recurse to handle subproblem */ + List *sublist; + + sublist = remove_rel_from_joinlist((List *) jlnode, + relid, nremoved); + /* Avoid including empty sub-lists in the result */ + if (sublist) + result = lappend(result, sublist); + } + else + { + elog(ERROR, "unrecognized joinlist node type: %d", + (int) nodeTag(jlnode)); + } + } + + return result; +} + +static Expr * +substitute_expr_vars(Expr *expr, int srcrelid, int tarrelid) +{ + Expr *newexpr = (Expr *) copyObject(expr); + List *vars = pull_vars_of_level((Node *) newexpr, 0); + ListCell *l; + + foreach(l, vars) + { + Var *var = lfirst(l); + if (var->varno == srcrelid) + var->varno = tarrelid; + } + return newexpr; +} + +static void +duplicate_eclass_exprs(PlannerInfo *root, int srcrelid, int tarrelid, List *tlist) +{ + ListCell *lc; + + foreach(lc, root->eq_classes) + { + EquivalenceClass *ec = lfirst(lc); + ListCell *l2; + List *newmembers = NIL; + + /* Skip classes which don't contain any members for srcrelid */ + if (!bms_is_member(srcrelid, ec->ec_relids)) + continue; + + /* + * Now look at each member to find out if there's Vars for the + * srcrelid. All expression which have Vars belonging to the source + * rel must be copied, and the Vars replaced with Vars from the target + * relation. + */ + foreach(l2, ec->ec_members) + { + EquivalenceMember *em = lfirst(l2); + Expr *newexpr; + EquivalenceMember *newem; + ListCell *l3; + List *vars; + + /* + * No point in looking at Consts, and skip if no Vars belong to + * srcrelid. + */ + if (em->em_is_const || !bms_is_member(srcrelid, em->em_relids)) + continue; + + newexpr = (Expr *) copyObject(em->em_expr); + vars = pull_vars_of_level((Node *) newexpr, 0); + + foreach(l3, vars) + { + Var *var = lfirst(l3); + if (var->varno == srcrelid) + { + AttrNumber resno = 1; + ListCell *l4; + bool found = false; + foreach(l4, tlist) + { + Expr *expr = lfirst(l4); + + if (equal(expr, em->em_expr)) + { + var->varattno = resno; + found = true; + break; + } + + resno++; + } + + if (!found) + elog(ERROR, "missing item from tlist"); + + var->varno = tarrelid; + } + } + + /* + * Ok, now we just need to generate a new member for the new + * expression. We can't append it to the members list of the eclass + * just yet, as we're looping over it. Instead we'll just generate + * a new list of members to append, and we'll do that after we're + * done looping. + */ + newem = makeNode(EquivalenceMember); + newem->em_datatype = em->em_datatype; + newem->em_expr = newexpr; + newem->em_is_child = em->em_is_child; + newem->em_is_const = false; + newem->em_nullable_relids = bms_copy(em->em_nullable_relids); + newem->em_relids = pull_varnos((Node *) newexpr); + + newmembers = lappend(newmembers, newem); + } + + /* There should be new members, let's check anyway... */ + if (newmembers != NIL) + { + ec->ec_members = list_concat(ec->ec_members, newmembers); + ec->ec_relids = bms_add_member(ec->ec_relids, tarrelid); + } + } +} + +/* + * setup_grouping_rel + * Build a new subquery RTE which performs the aggregation required of the + * outer query, but without joining to any of the rels which are not + * required of the aggregate functions. + * + * Note: For now this removes the original RTE from the joinlist. This will + * be changed in a more complete version. + */ +List * +setup_grouping_rel(PlannerInfo *root, List *joinlist) +{ + ListCell *lc; + int aggregaterel = root->aggregaterel; + int groupbyrel = root->groupbyrel; + int groupbeforejoinrel = root->groupbeforejoinrel; + List *joinconditions = NIL; + List *groupexprs = NIL; + Query *parse; + RangeTblEntry *rte; + RangeTblEntry *subrte; + RangeTblRef *rtr; + RelOptInfo *rel; + List *sub_tlist; + List *tlist; + List *oldtlist; + AttrNumber resno; + int nremoved; + + /* + * If the query's GROUP BY clause does not contain columns from the + * aggregate relation, then we must be sure that the GROUP BY clause is + * functionally dependent on the join clause to that relation. The reason + * for this is that we must ensure that we do not perform a partial + * grouping which creates a more tightly grouped set than what is required + * of the query's main GROUP BY clause. We can test this simply by looking + * for a unique index in the join expressions to the grouprel. If we find + * a supporting unique index then we can be certain that early grouping is + * legal. + */ + if (groupbyrel != 0 && groupbyrel != aggregaterel) + { + RelOptInfo *grouprel = find_base_rel(root, groupbyrel); + + foreach(lc, joinlist) + { + RangeTblRef *rtr = (RangeTblRef *) lfirst(lc); + RelOptInfo *joinrel; + List *joininfo; + Relids joinrelids; + + if (!IsA(rtr, RangeTblRef) || rtr->rtindex == groupbyrel) + continue; + + joinrel = find_base_rel(root, rtr->rtindex); + joinrelids = bms_union(joinrel->relids, grouprel->relids); + + joininfo = generate_join_implied_equalities(root, joinrelids, + joinrel->relids, grouprel); + + if (joininfo != NIL) + { + ListCell *lc2; + + foreach(lc2, joininfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc2); + Node *expr; + clause_sides_match_join(rinfo, joinrel, grouprel); + + if (rinfo->outer_is_left) + expr = get_rightop(rinfo->clause); + else + expr = get_leftop(rinfo->clause); + + groupexprs = list_append_unique(groupexprs, expr); + + joinconditions = lappend(joinconditions, rinfo); + } + } + } + + /* If no unique index is found then we cannot perform the group before join */ + if (!relation_has_unique_index_for(root, grouprel, joinconditions, NIL, NIL)) + return joinlist; + + foreach(lc, joinconditions) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *rexpr; + + if (rinfo->outer_is_left) + rexpr = get_rightop(rinfo->clause); + else + rexpr = get_leftop(rinfo->clause); + + groupexprs = list_append_unique(groupexprs, rexpr); + } + } + else if (root->parse->groupClause) + { + List *tempexprs = get_sortgrouplist_exprs(root->parse->groupClause, + root->parse->targetList); + + /* + * All grouping expressions come from the aggregate relation. We can + * simply just copy these expressions and replace the varno of each + * var with the first RTE in the new subquery that we're creating + */ + foreach(lc, tempexprs) + { + Expr *expr = (Expr *) lfirst(lc); + Expr *newexpr = substitute_expr_vars(expr, aggregaterel, 1); + groupexprs = lappend(groupexprs, newexpr); + } + } + + parse = makeNode(Query); + /************ The following code is still under construction! ************/ + rte = copyObject(root->simple_rte_array[aggregaterel]); + rte->rtekind = RTE_SUBQUERY; + rte->subquery = parse; + root->simple_rte_array[groupbeforejoinrel] = rte; + list_nth_cell(root->parse->rtable, groupbeforejoinrel - 1)->data.ptr_value = rte; + + rel = build_simple_rel(root, groupbeforejoinrel, RELOPT_BASEREL); + rel->rtekind = RTE_SUBQUERY; + rel->partial_aggregate = true; + root->simple_rel_array[groupbeforejoinrel] = rel; + + subrte = copyObject(root->simple_rte_array[aggregaterel]); + + parse->rtable = list_make1(subrte); + parse->jointree = makeNode(FromExpr); + parse->canSetTag = true; + parse->hasAggs = root->parse->hasAggs; + + joinlist = remove_rel_from_joinlist(joinlist, aggregaterel, &nremoved); + + rtr = makeNode(RangeTblRef); + rtr->rtindex = groupbeforejoinrel; + joinlist = lappend(joinlist, rtr); + /* for now don't bother considering the non group before join version */ + root->simple_rel_array[aggregaterel]->reloptkind = RELOPT_DEADREL; + + oldtlist = tlist = sub_tlist = NIL; + resno = 1; + + foreach(lc, root->parse->targetList) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + Relids relids = pull_varnos((Node *) tle->expr); + + if (bms_is_member(aggregaterel, relids)) + { + TargetEntry *newtle; + Var *var; + Expr *newexpr; + + /* We should have ensured this earlier */ + Assert(bms_membership(relids) == BMS_SINGLETON); + + newexpr = substitute_expr_vars(tle->expr, aggregaterel, 1); + newtle = makeTargetEntry(newexpr, resno, tle->resname ? pstrdup(tle->resname) : NULL, false); + + sub_tlist = lappend(sub_tlist, newtle); + /* we need to copy this as the tle will be modified later */ + oldtlist = lappend(oldtlist, copyObject(tle->expr)); + var = makeVarFromTargetEntry(groupbeforejoinrel, newtle); + tlist = lappend(tlist, var); + resno++; + } + } + + parse->targetList = sub_tlist; + add_vars_to_targetlist(root, tlist, bms_make_singleton(0), false); + + pull_varattnos((Node *) sub_tlist, aggregaterel, &rte->selectedCols); + + foreach(lc, groupexprs) + { + Node *node = (Node *) lfirst(lc); + TargetEntry *tle; + Oid sortop; + Oid eqop; + bool hashable; + SortGroupClause *sgc; + + /* determine the eqop and optional sortop */ + get_sort_group_operators(exprType(node), + false, true, false, + &sortop, &eqop, NULL, + &hashable); + + tle = tlist_member(node, parse->targetList); + + if (!tle) /* shouldn't happen */ + elog(ERROR, "failed to find group expression in subplan tlist"); + + sgc = makeNode(SortGroupClause); + sgc->eqop = eqop; + sgc->hashable = hashable; + sgc->nulls_first = false; + sgc->sortop = sortop; + tle->ressortgroupref = sgc->tleSortGroupRef = assignSortGroupRef(tle, parse->targetList); + + parse->groupClause = lappend(parse->groupClause, sgc); + } + + rtr = makeNode(RangeTblRef); + rtr->rtindex = 1; + parse->jointree->fromlist = list_make1(rtr); + parse->resultRelation = 0; + parse->commandType = CMD_SELECT; + + //root->parse = (Query *) ReplaceVarsFromTargetList((Node *) root->parse, + // aggregaterel, 0, + // rt_fetch(aggregaterel, root->parse->rtable), + // parse->targetList, + // REPLACEVARS_SUBSTITUTE_NULL, 0, + // &parse->hasSubLinks); + + //root->parse->groupClause = NIL; + /*** TEMP HACKS SECTION ***/ + { + List *vars = pull_vars_of_level((Node *) root->parse->targetList, 0); + foreach(lc, vars) + { + Var *v = lfirst(lc); + if (v->varno == aggregaterel) + v->varno = groupbeforejoinrel; + } + } + + duplicate_eclass_exprs(root, aggregaterel, groupbeforejoinrel, oldtlist); + return joinlist; +} + +/* + * prepare_group_before_join + * Phase 1 in determining if GROUP BY and/or aggregation may be possible + * before joining to other relations. This determination is performed in + * multiple phases. In this phase we perform some simple checks in order + * to eliminated cases where this is absolutely not possible. + * If we determine the optimization maybe possible we add a dummy item to + * the rtable so as to reserve a slot in the simple rel arrays for this + * alternative way of performing the query. We return false if group + * before join is absolutely not possible, and true if it may be possible. + */ +bool +prepare_group_before_join(PlannerInfo *root, Query *parse) +{ + List *tlist = parse->targetList; + Index aggregaterel = 0; + Index grouprel = 0; + ListCell *l; + Relids relids; + + /* bail if earlygrouping GUC is disabled */ + if (!enable_earlygrouping) + return false; + + /* Nothing to do for queries which don't need aggregation */ + if (!parse->hasAggs && + parse->havingQual == NULL && + parse->groupClause == NIL) + return false; + + /* No joins? It'll be hard to group before that! */ + if (list_length(parse->rtable) == 1) + return false; + + /* XXX do we need to disable in this case? */ + if (expression_returns_set((Node *) parse->targetList)) + return false; + + /* + * For queries with aggregate functions, we're only able to perform this + * group before join optimization when all aggregate function parameters + * contain Vars from a single relation. If they do, then this relation will + * become the relation which we perform the grouping on. + */ + + /* + * We can skip performing any analysis on aggregate functions for queries + * which have none. + */ + if (parse->hasAggs) + { + Index curaggrel; + + foreach(l, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Aggref *aggref = (Aggref *) tle->expr; + Oid aggcombinefn; + HeapTuple aggTuple; + Form_pg_aggregate aggform; + + /* Skip over anything that's not an Aggref */ + if (!IsA(aggref, Aggref)) + continue; + + /* + * We're unable to apply the optimization if any of the aggregates + * have a DISTINCT or ORDER BY. The problem with this is that we + * currently have no infrastructure to ensure that the groups are + * not duplicated by some join that takes place after the grouping + * is performed. + */ + if (aggref->aggdistinct != NIL || aggref->aggorder != NIL) + return false; + + /* + * Now we must check if the aggregate function has a state combine + * function set. It's not possible to perform the final aggregate + * stage for aggregates which don't have one of these. + */ + aggTuple = SearchSysCache1(AGGFNOID, + ObjectIdGetDatum(aggref->aggfnoid)); + if (!HeapTupleIsValid(aggTuple)) + elog(ERROR, "cache lookup failed for aggregate %u", + aggref->aggfnoid); + aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); + aggcombinefn = aggform->aggcombinefn; + ReleaseSysCache(aggTuple); + + if (!OidIsValid(aggcombinefn)) + return false; + + if (!aggref->aggstar) + { + relids = pull_varnos((Node *) aggref); + + /* + * Check if the aggregate's parameters contain Vars from more + * than one rel, if it does then we'll need to disable the + * optimization, as we can only optimize cases where there's + * a single grouping relation. + */ + if (relids && !bms_get_singleton_member(relids, &curaggrel)) + return false; + + bms_free(relids); + + /* + * If we've found an Aggref where the parameter contains rels + * different from the last aggregate function call then we can't + * optimize this either, for the same reason as above. + */ + if (aggregaterel != 0 && aggregaterel != curaggrel) + return false; + + aggregaterel = curaggrel; + } + } + } + + /* + * If the query has no aggs, or the aggs contain no vars, e.g COUNT(*), + * then we'll just select any relation as the one to aggregate on + */ + if (aggregaterel == 0) + aggregaterel = 1; + + /* + * We'll make another pass over the targetlist, this time we know which + * relation that we might like to try grouping on. We'll need to ensure + * that no targets expression exist which have Vars which belong to our + * aggregate relation, and also contain Vars which don't belong to our + * aggregate relation. We'll also ensure that any Vars belonging to the + * aggregate relation are also present in the GROUP BY clause. This is + * possible as the SQL standard does allow columns which are functionally + * dependant on the GROUP BY clause. + */ + foreach(l, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Expr *expr = tle->expr; + + /* Skip over any Aggrefs, we've already checked these above. */ + if (IsA(expr, Aggref)) + continue; + + relids = pull_varnos((Node *) expr); + + /* Does this expression require the aggregate relation? */ + if (bms_is_member(aggregaterel, relids)) + { + if (bms_membership(relids) == BMS_MULTIPLE) + return false; + + if (tle->ressortgroupref == 0) + return false; + } + } + + /* + * Now we'll have a look at the GROUP BY clause if one exists. We need to + * ensure that all of the grouping expressions only contain Vars from a + * single rel. This does not have to be the same rel as the one we may have + * seen in the Aggref parameters above. The reason for this is that later + * we need to perform some analysis to ensure that the GROUP BY columns are + * functionally dependant on the join clause to our grouping relation, and + * the only way we currently have to test functional dependency is by + * looking at unique indexes, and a unique index can only be on a single + * relation. + */ + if (root->parse->groupClause != NIL) + { + List *groupExprs; + Relids grouprels; + + groupExprs = get_sortgrouplist_exprs(parse->groupClause, tlist); + grouprels = pull_varnos((Node *) groupExprs); + + if (!bms_get_singleton_member(grouprels, &grouprel)) + return false; + } + + /* + * At this stage we cannot be sure that performing GROUP BY early is + * actually possible as we still need to analyze the join condition for + * relations being joined to this one as we need to ensure we don't + * accidentally generate a smaller number of groups than is required of the + * main GROUP BY clause. + */ + root->aggregaterel = aggregaterel; + root->groupbyrel = grouprel; + root->groupbeforejoinrel = list_length(parse->rtable) + 1; + /* + * Another thing we must also do now is add an item to the query's rtable. + * We'll need this so we can consider a grouped RelOptInfo during the join + * search. We need to do this before setup_simple_rel_arrays() is called. + */ + parse->rtable = lappend(parse->rtable, makeNode(RangeTblEntry)); + + return true; /* Group before join may be possible */ +} diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 404c6f5..3ccb5ef 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1045,6 +1045,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) groupOperators, NIL, numGroups, + false, + true, subplan); } else @@ -4501,9 +4503,8 @@ Agg * make_agg(PlannerInfo *root, List *tlist, List *qual, AggStrategy aggstrategy, const AggClauseCosts *aggcosts, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, - List *groupingSets, - long numGroups, - Plan *lefttree) + List *groupingSets, long numGroups, bool combineStates, + bool finalizeAggs, Plan *lefttree) { Agg *node = makeNode(Agg); Plan *plan = &node->plan; @@ -4512,6 +4513,8 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, node->aggstrategy = aggstrategy; node->numCols = numGroupCols; + node->combineStates = combineStates; + node->finalizeAggs = finalizeAggs; node->grpColIdx = grpColIdx; node->grpOperators = grpOperators; node->numGroups = numGroups; diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 848df97..3651de6 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -20,11 +20,14 @@ */ #include "postgres.h" +#include "nodes/nodeFuncs.h" #include "optimizer/orclauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/placeholder.h" #include "optimizer/planmain.h" +#include "optimizer/tlist.h" +#include "optimizer/var.h" /* @@ -60,6 +63,7 @@ query_planner(PlannerInfo *root, List *tlist, RelOptInfo *final_rel; Index rti; double total_pages; + bool try_group_before_join; /* * If the query has an empty join tree, then it's something easy like @@ -107,6 +111,12 @@ query_planner(PlannerInfo *root, List *tlist, root->initial_rels = NIL; /* + * Determine if it might be possible to apply grouping of one relation + * before joining it to any other relations. + */ + try_group_before_join = prepare_group_before_join(root, parse); + + /* * Make a flattened version of the rangetable for faster access (this is * OK because the rangetable won't change any more), and set up an empty * array for indexing base relations. @@ -201,6 +211,10 @@ query_planner(PlannerInfo *root, List *tlist, */ extract_restriction_or_clauses(root); + /* run the final phase to determine if group before join is possible */ + if (try_group_before_join) + joinlist = setup_grouping_rel(root, joinlist); + /* * We should now have size estimates for every actual table involved in * the query, and we also know which if any have been deleted from the diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 06be922..4861093 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -80,7 +80,8 @@ typedef struct static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode); static Plan *inheritance_planner(PlannerInfo *root); -static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction); +static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction, + bool finalize_aggregates); static void preprocess_rowmarks(PlannerInfo *root); static double preprocess_limit(PlannerInfo *root, double tuple_fraction, @@ -271,7 +272,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) /* primary planning entry point (may recurse for subqueries) */ top_plan = subquery_planner(glob, parse, NULL, - false, tuple_fraction, &root); + false, true, tuple_fraction, &root); /* * If creating a plan for a scrollable cursor, make sure it can run @@ -373,8 +374,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) Plan * subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, - bool hasRecursion, double tuple_fraction, - PlannerInfo **subroot) + bool hasRecursion, bool finalize_aggregates, + double tuple_fraction, PlannerInfo **subroot) { PlannerInfo *root; Plan *plan; @@ -673,7 +674,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, plan = inheritance_planner(root); else { - plan = grouping_planner(root, tuple_fraction); + plan = grouping_planner(root, tuple_fraction, finalize_aggregates); /* If it's not SELECT, we need a ModifyTable node */ if (parse->commandType != CMD_SELECT) { @@ -1129,7 +1130,7 @@ inheritance_planner(PlannerInfo *root) subroot.hasInheritedTarget = true; /* Generate plan */ - subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ ); + subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ ,true); /* * Planning may have modified the query result relation (if there were @@ -1310,7 +1311,8 @@ inheritance_planner(PlannerInfo *root) *-------------------- */ static Plan * -grouping_planner(PlannerInfo *root, double tuple_fraction) +grouping_planner(PlannerInfo *root, double tuple_fraction, + bool finalize_aggregates) { Query *parse = root->parse; List *tlist = parse->targetList; @@ -1425,6 +1427,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) Path *cheapest_path; Path *sorted_path; Path *best_path; + bool combine_aggregates; MemSet(&agg_costs, 0, sizeof(AggClauseCosts)); @@ -1603,6 +1606,19 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) final_rel = query_planner(root, sub_tlist, standard_qp_callback, &qp_extra); + if (bms_is_member(root->groupbeforejoinrel, final_rel->relids)) + { + combine_aggregates = true; + tlist = (List *) ReplaceVarsFromTargetList((Node *) tlist, + root->aggregaterel, 0, + rt_fetch(root->aggregaterel, root->parse->rtable), + sub_tlist, + REPLACEVARS_SUBSTITUTE_NULL, 0, + &parse->hasSubLinks); + } + else + combine_aggregates = false; + /* * Extract rowcount and width estimates for use below. If final_rel * has been proven dummy, its rows estimate will be zero; clamp it to @@ -1983,6 +1999,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) extract_grouping_ops(parse->groupClause), NIL, numGroups, + combine_aggregates, + finalize_aggregates, result_plan); /* Hashed aggregation produces randomly-ordered results */ current_pathkeys = NIL; @@ -2294,6 +2312,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) extract_grouping_ops(parse->distinctClause), NIL, numDistinctRows, + false, + false, result_plan); /* Hashed aggregation produces randomly-ordered results */ current_pathkeys = NIL; @@ -2527,6 +2547,8 @@ build_grouping_chain(PlannerInfo *root, extract_grouping_ops(groupClause), gsets, numGroups, + false, + true, sort_plan); sort_plan->lefttree = NULL; @@ -2563,6 +2585,8 @@ build_grouping_chain(PlannerInfo *root, extract_grouping_ops(groupClause), gsets, numGroups, + false, + true, result_plan); ((Agg *) result_plan)->chain = chain; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index d0bc412..9da104d 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -532,7 +532,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, */ plan = subquery_planner(root->glob, subquery, root, - false, tuple_fraction, + false, true, tuple_fraction, &subroot); /* Isolate the params needed by this specific subplan */ @@ -571,7 +571,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, /* Generate the plan for the ANY subquery; we'll need all rows */ plan = subquery_planner(root->glob, subquery, root, - false, 0.0, + false, true, 0.0, &subroot); /* Isolate the params needed by this specific subplan */ @@ -1163,7 +1163,7 @@ SS_process_ctes(PlannerInfo *root) */ plan = subquery_planner(root->glob, subquery, root, - cte->cterecursive, 0.0, + cte->cterecursive, true, 0.0, &subroot); /* diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 8884fb1..224c575 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -247,7 +247,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, */ subplan = subquery_planner(root->glob, subquery, root, - false, tuple_fraction, + false, true, tuple_fraction, &subroot); /* Save subroot and subplan in RelOptInfo for setrefs.c */ @@ -775,6 +775,8 @@ make_union_unique(SetOperationStmt *op, Plan *plan, extract_grouping_ops(groupList), NIL, numGroups, + false, + true, plan); /* Hashed aggregation produces randomly-ordered results */ *sortClauses = NIL; diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 5b0d568..1dde1a7 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -1929,6 +1929,43 @@ build_aggregate_transfn_expr(Oid *agg_input_types, /* * Like build_aggregate_transfn_expr, but creates an expression tree for the + * combine function of an aggregate, rather than the transition function. + */ +void +build_aggregate_combinefn_expr(bool agg_variadic, + Oid agg_state_type, + Oid agg_input_collation, + Oid combinefn_oid, + Expr **combinefnexpr) +{ + Param *argp; + List *args; + FuncExpr *fexpr; + + /* Build arg list to use in the combinefn FuncExpr node. */ + argp = makeNode(Param); + argp->paramkind = PARAM_EXEC; + argp->paramid = -1; + argp->paramtype = agg_state_type; + argp->paramtypmod = -1; + argp->paramcollid = agg_input_collation; + argp->location = -1; + + /* trans state type is arg 1 and 2 */ + args = list_make2(argp, argp); + + fexpr = makeFuncExpr(combinefn_oid, + agg_state_type, + args, + InvalidOid, + agg_input_collation, + COERCE_EXPLICIT_CALL); + fexpr->funcvariadic = agg_variadic; + *combinefnexpr = (Expr *) fexpr; +} + +/* + * Like build_aggregate_transfn_expr, but creates an expression tree for the * final function of an aggregate, rather than the transition function. */ void diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 17053af..87052f2 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -854,6 +854,15 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, { + { "enable_earlygrouping", PGC_USERSET, QUERY_TUNING_METHOD, + gettext_noop("Enables the planner's ability to perform GROUP BY before joining to unrelated tables."), + NULL + }, + &enable_earlygrouping, + true, + NULL, NULL, NULL + }, + { {"geqo", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("Enables genetic query optimization."), gettext_noop("This algorithm attempts to do planning without " diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 7e75bc2..c180730 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12265,6 +12265,7 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo) PGresult *res; int i_aggtransfn; int i_aggfinalfn; + int i_aggcombinefn; int i_aggmtransfn; int i_aggminvtransfn; int i_aggmfinalfn; @@ -12281,6 +12282,7 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo) int i_convertok; const char *aggtransfn; const char *aggfinalfn; + const char *aggcombinefn; const char *aggmtransfn; const char *aggminvtransfn; const char *aggmfinalfn; @@ -12311,7 +12313,26 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo) selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name); /* Get aggregate-specific details */ - if (fout->remoteVersion >= 90400) + if (fout->remoteVersion >= 90600) + { + appendPQExpBuffer(query, "SELECT aggtransfn, " + "aggfinalfn, aggtranstype::pg_catalog.regtype, " + "aggcombinefn, aggmtransfn, aggminvtransfn, " + "aggmfinalfn, aggmtranstype::pg_catalog.regtype, " + "aggfinalextra, aggmfinalextra, " + "aggsortop::pg_catalog.regoperator, " + "(aggkind = 'h') AS hypothetical, " + "aggtransspace, agginitval, " + "aggmtransspace, aggminitval, " + "true AS convertok, " + "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, " + "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", + agginfo->aggfn.dobj.catId.oid); + } + else if (fout->remoteVersion >= 90400) { appendPQExpBuffer(query, "SELECT aggtransfn, " "aggfinalfn, aggtranstype::pg_catalog.regtype, " @@ -12421,6 +12442,7 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo) i_aggtransfn = PQfnumber(res, "aggtransfn"); i_aggfinalfn = PQfnumber(res, "aggfinalfn"); + i_aggcombinefn = PQfnumber(res, "aggcombinefn"); i_aggmtransfn = PQfnumber(res, "aggmtransfn"); i_aggminvtransfn = PQfnumber(res, "aggminvtransfn"); i_aggmfinalfn = PQfnumber(res, "aggmfinalfn"); @@ -12438,6 +12460,7 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo) aggtransfn = PQgetvalue(res, 0, i_aggtransfn); aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); + aggcombinefn = PQgetvalue(res, 0, i_aggcombinefn); aggmtransfn = PQgetvalue(res, 0, i_aggmtransfn); aggminvtransfn = PQgetvalue(res, 0, i_aggminvtransfn); aggmfinalfn = PQgetvalue(res, 0, i_aggmfinalfn); @@ -12526,6 +12549,11 @@ dumpAgg(Archive *fout, DumpOptions *dopt, AggInfo *agginfo) appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA"); } + if (strcmp(aggcombinefn, "-") != 0) + { + appendPQExpBuffer(details, ",\n CFUNC = %s", aggcombinefn); + } + if (strcmp(aggmtransfn, "-") != 0) { appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s", diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index dd6079f..b306f9b 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -33,6 +33,7 @@ * aggnumdirectargs number of arguments that are "direct" arguments * aggtransfn transition function * aggfinalfn final function (0 if none) + * aggcombinefn combine function (0 if none) * aggmtransfn forward function for moving-aggregate mode (0 if none) * aggminvtransfn inverse function for moving-aggregate mode (0 if none) * aggmfinalfn final function for moving-aggregate mode (0 if none) @@ -56,6 +57,7 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS int16 aggnumdirectargs; regproc aggtransfn; regproc aggfinalfn; + regproc aggcombinefn; regproc aggmtransfn; regproc aggminvtransfn; regproc aggmfinalfn; @@ -85,24 +87,25 @@ typedef FormData_pg_aggregate *Form_pg_aggregate; * ---------------- */ -#define Natts_pg_aggregate 17 +#define Natts_pg_aggregate 18 #define Anum_pg_aggregate_aggfnoid 1 #define Anum_pg_aggregate_aggkind 2 #define Anum_pg_aggregate_aggnumdirectargs 3 #define Anum_pg_aggregate_aggtransfn 4 #define Anum_pg_aggregate_aggfinalfn 5 -#define Anum_pg_aggregate_aggmtransfn 6 -#define Anum_pg_aggregate_aggminvtransfn 7 -#define Anum_pg_aggregate_aggmfinalfn 8 -#define Anum_pg_aggregate_aggfinalextra 9 -#define Anum_pg_aggregate_aggmfinalextra 10 -#define Anum_pg_aggregate_aggsortop 11 -#define Anum_pg_aggregate_aggtranstype 12 -#define Anum_pg_aggregate_aggtransspace 13 -#define Anum_pg_aggregate_aggmtranstype 14 -#define Anum_pg_aggregate_aggmtransspace 15 -#define Anum_pg_aggregate_agginitval 16 -#define Anum_pg_aggregate_aggminitval 17 +#define Anum_pg_aggregate_aggcombinefn 6 +#define Anum_pg_aggregate_aggmtransfn 7 +#define Anum_pg_aggregate_aggminvtransfn 8 +#define Anum_pg_aggregate_aggmfinalfn 9 +#define Anum_pg_aggregate_aggfinalextra 10 +#define Anum_pg_aggregate_aggmfinalextra 11 +#define Anum_pg_aggregate_aggsortop 12 +#define Anum_pg_aggregate_aggtranstype 13 +#define Anum_pg_aggregate_aggtransspace 14 +#define Anum_pg_aggregate_aggmtranstype 15 +#define Anum_pg_aggregate_aggmtransspace 16 +#define Anum_pg_aggregate_agginitval 17 +#define Anum_pg_aggregate_aggminitval 18 /* * Symbolic values for aggkind column. We distinguish normal aggregates @@ -126,184 +129,184 @@ typedef FormData_pg_aggregate *Form_pg_aggregate; */ /* avg */ -DATA(insert ( 2100 n 0 int8_avg_accum numeric_poly_avg int8_avg_accum int8_avg_accum_inv numeric_poly_avg f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2101 n 0 int4_avg_accum int8_avg int4_avg_accum int4_avg_accum_inv int8_avg f f 0 1016 0 1016 0 "{0,0}" "{0,0}" )); -DATA(insert ( 2102 n 0 int2_avg_accum int8_avg int2_avg_accum int2_avg_accum_inv int8_avg f f 0 1016 0 1016 0 "{0,0}" "{0,0}" )); -DATA(insert ( 2103 n 0 numeric_avg_accum numeric_avg numeric_avg_accum numeric_accum_inv numeric_avg f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2104 n 0 float4_accum float8_avg - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2105 n 0 float8_accum float8_avg - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2106 n 0 interval_accum interval_avg interval_accum interval_accum_inv interval_avg f f 0 1187 0 1187 0 "{0 second,0 second}" "{0 second,0 second}" )); +DATA(insert ( 2100 n 0 int8_avg_accum numeric_poly_avg - int8_avg_accum int8_avg_accum_inv numeric_poly_avg f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2101 n 0 int4_avg_accum int8_avg - int4_avg_accum int4_avg_accum_inv int8_avg f f 0 1016 0 1016 0 "{0,0}" "{0,0}" )); +DATA(insert ( 2102 n 0 int2_avg_accum int8_avg - int2_avg_accum int2_avg_accum_inv int8_avg f f 0 1016 0 1016 0 "{0,0}" "{0,0}" )); +DATA(insert ( 2103 n 0 numeric_avg_accum numeric_avg - numeric_avg_accum numeric_accum_inv numeric_avg f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2104 n 0 float4_accum float8_avg - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2105 n 0 float8_accum float8_avg - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2106 n 0 interval_accum interval_avg - interval_accum interval_accum_inv interval_avg f f 0 1187 0 1187 0 "{0 second,0 second}" "{0 second,0 second}" )); /* sum */ -DATA(insert ( 2107 n 0 int8_avg_accum numeric_poly_sum int8_avg_accum int8_avg_accum_inv numeric_poly_sum f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2108 n 0 int4_sum - int4_avg_accum int4_avg_accum_inv int2int4_sum f f 0 20 0 1016 0 _null_ "{0,0}" )); -DATA(insert ( 2109 n 0 int2_sum - int2_avg_accum int2_avg_accum_inv int2int4_sum f f 0 20 0 1016 0 _null_ "{0,0}" )); -DATA(insert ( 2110 n 0 float4pl - - - - f f 0 700 0 0 0 _null_ _null_ )); -DATA(insert ( 2111 n 0 float8pl - - - - f f 0 701 0 0 0 _null_ _null_ )); -DATA(insert ( 2112 n 0 cash_pl - cash_pl cash_mi - f f 0 790 0 790 0 _null_ _null_ )); -DATA(insert ( 2113 n 0 interval_pl - interval_pl interval_mi - f f 0 1186 0 1186 0 _null_ _null_ )); -DATA(insert ( 2114 n 0 numeric_avg_accum numeric_sum numeric_avg_accum numeric_accum_inv numeric_sum f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2107 n 0 int8_avg_accum numeric_poly_sum - int8_avg_accum int8_avg_accum_inv numeric_poly_sum f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2108 n 0 int4_sum - int8pl int4_avg_accum int4_avg_accum_inv int2int4_sum f f 0 20 0 1016 0 _null_ "{0,0}" )); +DATA(insert ( 2109 n 0 int2_sum - int8pl int2_avg_accum int2_avg_accum_inv int2int4_sum f f 0 20 0 1016 0 _null_ "{0,0}" )); +DATA(insert ( 2110 n 0 float4pl - float4pl - - - f f 0 700 0 0 0 _null_ _null_ )); +DATA(insert ( 2111 n 0 float8pl - float8pl - - - f f 0 701 0 0 0 _null_ _null_ )); +DATA(insert ( 2112 n 0 cash_pl - cash_pl cash_pl cash_mi - f f 0 790 0 790 0 _null_ _null_ )); +DATA(insert ( 2113 n 0 interval_pl - interval_pl interval_pl interval_mi - f f 0 1186 0 1186 0 _null_ _null_ )); +DATA(insert ( 2114 n 0 numeric_avg_accum numeric_sum - numeric_avg_accum numeric_accum_inv numeric_sum f f 0 2281 128 2281 128 _null_ _null_ )); /* max */ -DATA(insert ( 2115 n 0 int8larger - - - - f f 413 20 0 0 0 _null_ _null_ )); -DATA(insert ( 2116 n 0 int4larger - - - - f f 521 23 0 0 0 _null_ _null_ )); -DATA(insert ( 2117 n 0 int2larger - - - - f f 520 21 0 0 0 _null_ _null_ )); -DATA(insert ( 2118 n 0 oidlarger - - - - f f 610 26 0 0 0 _null_ _null_ )); -DATA(insert ( 2119 n 0 float4larger - - - - f f 623 700 0 0 0 _null_ _null_ )); -DATA(insert ( 2120 n 0 float8larger - - - - f f 674 701 0 0 0 _null_ _null_ )); -DATA(insert ( 2121 n 0 int4larger - - - - f f 563 702 0 0 0 _null_ _null_ )); -DATA(insert ( 2122 n 0 date_larger - - - - f f 1097 1082 0 0 0 _null_ _null_ )); -DATA(insert ( 2123 n 0 time_larger - - - - f f 1112 1083 0 0 0 _null_ _null_ )); -DATA(insert ( 2124 n 0 timetz_larger - - - - f f 1554 1266 0 0 0 _null_ _null_ )); -DATA(insert ( 2125 n 0 cashlarger - - - - f f 903 790 0 0 0 _null_ _null_ )); -DATA(insert ( 2126 n 0 timestamp_larger - - - - f f 2064 1114 0 0 0 _null_ _null_ )); -DATA(insert ( 2127 n 0 timestamptz_larger - - - - f f 1324 1184 0 0 0 _null_ _null_ )); -DATA(insert ( 2128 n 0 interval_larger - - - - f f 1334 1186 0 0 0 _null_ _null_ )); -DATA(insert ( 2129 n 0 text_larger - - - - f f 666 25 0 0 0 _null_ _null_ )); -DATA(insert ( 2130 n 0 numeric_larger - - - - f f 1756 1700 0 0 0 _null_ _null_ )); -DATA(insert ( 2050 n 0 array_larger - - - - f f 1073 2277 0 0 0 _null_ _null_ )); -DATA(insert ( 2244 n 0 bpchar_larger - - - - f f 1060 1042 0 0 0 _null_ _null_ )); -DATA(insert ( 2797 n 0 tidlarger - - - - f f 2800 27 0 0 0 _null_ _null_ )); -DATA(insert ( 3526 n 0 enum_larger - - - - f f 3519 3500 0 0 0 _null_ _null_ )); -DATA(insert ( 3564 n 0 network_larger - - - - f f 1205 869 0 0 0 _null_ _null_ )); +DATA(insert ( 2115 n 0 int8larger - int8larger - - - f f 413 20 0 0 0 _null_ _null_ )); +DATA(insert ( 2116 n 0 int4larger - int4larger - - - f f 521 23 0 0 0 _null_ _null_ )); +DATA(insert ( 2117 n 0 int2larger - int2larger - - - f f 520 21 0 0 0 _null_ _null_ )); +DATA(insert ( 2118 n 0 oidlarger - oidlarger - - - f f 610 26 0 0 0 _null_ _null_ )); +DATA(insert ( 2119 n 0 float4larger - float4larger - - - f f 623 700 0 0 0 _null_ _null_ )); +DATA(insert ( 2120 n 0 float8larger - float8larger - - - f f 674 701 0 0 0 _null_ _null_ )); +DATA(insert ( 2121 n 0 int4larger - int4larger - - - f f 563 702 0 0 0 _null_ _null_ )); +DATA(insert ( 2122 n 0 date_larger - date_larger - - - f f 1097 1082 0 0 0 _null_ _null_ )); +DATA(insert ( 2123 n 0 time_larger - time_larger - - - f f 1112 1083 0 0 0 _null_ _null_ )); +DATA(insert ( 2124 n 0 timetz_larger - timetz_larger - - - f f 1554 1266 0 0 0 _null_ _null_ )); +DATA(insert ( 2125 n 0 cashlarger - cashlarger - - - f f 903 790 0 0 0 _null_ _null_ )); +DATA(insert ( 2126 n 0 timestamp_larger - timestamp_larger - - - f f 2064 1114 0 0 0 _null_ _null_ )); +DATA(insert ( 2127 n 0 timestamptz_larger - timestamptz_larger - - - f f 1324 1184 0 0 0 _null_ _null_ )); +DATA(insert ( 2128 n 0 interval_larger - interval_larger - - - f f 1334 1186 0 0 0 _null_ _null_ )); +DATA(insert ( 2129 n 0 text_larger - text_larger - - - f f 666 25 0 0 0 _null_ _null_ )); +DATA(insert ( 2130 n 0 numeric_larger - numeric_larger - - - f f 1756 1700 0 0 0 _null_ _null_ )); +DATA(insert ( 2050 n 0 array_larger - array_larger - - - f f 1073 2277 0 0 0 _null_ _null_ )); +DATA(insert ( 2244 n 0 bpchar_larger - bpchar_larger - - - f f 1060 1042 0 0 0 _null_ _null_ )); +DATA(insert ( 2797 n 0 tidlarger - tidlarger - - - f f 2800 27 0 0 0 _null_ _null_ )); +DATA(insert ( 3526 n 0 enum_larger - enum_larger - - - f f 3519 3500 0 0 0 _null_ _null_ )); +DATA(insert ( 3564 n 0 network_larger - network_larger - - - f f 1205 869 0 0 0 _null_ _null_ )); /* min */ -DATA(insert ( 2131 n 0 int8smaller - - - - f f 412 20 0 0 0 _null_ _null_ )); -DATA(insert ( 2132 n 0 int4smaller - - - - f f 97 23 0 0 0 _null_ _null_ )); -DATA(insert ( 2133 n 0 int2smaller - - - - f f 95 21 0 0 0 _null_ _null_ )); -DATA(insert ( 2134 n 0 oidsmaller - - - - f f 609 26 0 0 0 _null_ _null_ )); -DATA(insert ( 2135 n 0 float4smaller - - - - f f 622 700 0 0 0 _null_ _null_ )); -DATA(insert ( 2136 n 0 float8smaller - - - - f f 672 701 0 0 0 _null_ _null_ )); -DATA(insert ( 2137 n 0 int4smaller - - - - f f 562 702 0 0 0 _null_ _null_ )); -DATA(insert ( 2138 n 0 date_smaller - - - - f f 1095 1082 0 0 0 _null_ _null_ )); -DATA(insert ( 2139 n 0 time_smaller - - - - f f 1110 1083 0 0 0 _null_ _null_ )); -DATA(insert ( 2140 n 0 timetz_smaller - - - - f f 1552 1266 0 0 0 _null_ _null_ )); -DATA(insert ( 2141 n 0 cashsmaller - - - - f f 902 790 0 0 0 _null_ _null_ )); -DATA(insert ( 2142 n 0 timestamp_smaller - - - - f f 2062 1114 0 0 0 _null_ _null_ )); -DATA(insert ( 2143 n 0 timestamptz_smaller - - - - f f 1322 1184 0 0 0 _null_ _null_ )); -DATA(insert ( 2144 n 0 interval_smaller - - - - f f 1332 1186 0 0 0 _null_ _null_ )); -DATA(insert ( 2145 n 0 text_smaller - - - - f f 664 25 0 0 0 _null_ _null_ )); -DATA(insert ( 2146 n 0 numeric_smaller - - - - f f 1754 1700 0 0 0 _null_ _null_ )); -DATA(insert ( 2051 n 0 array_smaller - - - - f f 1072 2277 0 0 0 _null_ _null_ )); -DATA(insert ( 2245 n 0 bpchar_smaller - - - - f f 1058 1042 0 0 0 _null_ _null_ )); -DATA(insert ( 2798 n 0 tidsmaller - - - - f f 2799 27 0 0 0 _null_ _null_ )); -DATA(insert ( 3527 n 0 enum_smaller - - - - f f 3518 3500 0 0 0 _null_ _null_ )); -DATA(insert ( 3565 n 0 network_smaller - - - - f f 1203 869 0 0 0 _null_ _null_ )); +DATA(insert ( 2131 n 0 int8smaller - int8smaller - - - f f 412 20 0 0 0 _null_ _null_ )); +DATA(insert ( 2132 n 0 int4smaller - int4smaller - - - f f 97 23 0 0 0 _null_ _null_ )); +DATA(insert ( 2133 n 0 int2smaller - int2smaller - - - f f 95 21 0 0 0 _null_ _null_ )); +DATA(insert ( 2134 n 0 oidsmaller - oidsmaller - - - f f 609 26 0 0 0 _null_ _null_ )); +DATA(insert ( 2135 n 0 float4smaller - float4smaller - - - f f 622 700 0 0 0 _null_ _null_ )); +DATA(insert ( 2136 n 0 float8smaller - float8smaller - - - f f 672 701 0 0 0 _null_ _null_ )); +DATA(insert ( 2137 n 0 int4smaller - int4smaller - - - f f 562 702 0 0 0 _null_ _null_ )); +DATA(insert ( 2138 n 0 date_smaller - date_smaller - - - f f 1095 1082 0 0 0 _null_ _null_ )); +DATA(insert ( 2139 n 0 time_smaller - time_smaller - - - f f 1110 1083 0 0 0 _null_ _null_ )); +DATA(insert ( 2140 n 0 timetz_smaller - timetz_smaller - - - f f 1552 1266 0 0 0 _null_ _null_ )); +DATA(insert ( 2141 n 0 cashsmaller - cashsmaller - - - f f 902 790 0 0 0 _null_ _null_ )); +DATA(insert ( 2142 n 0 timestamp_smaller - timestamp_smaller - - - f f 2062 1114 0 0 0 _null_ _null_ )); +DATA(insert ( 2143 n 0 timestamptz_smaller - timestamptz_smaller - - - f f 1322 1184 0 0 0 _null_ _null_ )); +DATA(insert ( 2144 n 0 interval_smaller - interval_smaller - - - f f 1332 1186 0 0 0 _null_ _null_ )); +DATA(insert ( 2145 n 0 text_smaller - text_smaller - - - f f 664 25 0 0 0 _null_ _null_ )); +DATA(insert ( 2146 n 0 numeric_smaller - numeric_smaller - - - f f 1754 1700 0 0 0 _null_ _null_ )); +DATA(insert ( 2051 n 0 array_smaller - array_smaller - - - f f 1072 2277 0 0 0 _null_ _null_ )); +DATA(insert ( 2245 n 0 bpchar_smaller - bpchar_smaller - - - f f 1058 1042 0 0 0 _null_ _null_ )); +DATA(insert ( 2798 n 0 tidsmaller - tidsmaller - - - f f 2799 27 0 0 0 _null_ _null_ )); +DATA(insert ( 3527 n 0 enum_smaller - enum_smaller - - - f f 3518 3500 0 0 0 _null_ _null_ )); +DATA(insert ( 3565 n 0 network_smaller - network_smaller - - - f f 1203 869 0 0 0 _null_ _null_ )); /* count */ -DATA(insert ( 2147 n 0 int8inc_any - int8inc_any int8dec_any - f f 0 20 0 20 0 "0" "0" )); -DATA(insert ( 2803 n 0 int8inc - int8inc int8dec - f f 0 20 0 20 0 "0" "0" )); +DATA(insert ( 2147 n 0 int8inc_any - int8pl int8inc_any int8dec_any - f f 0 20 0 20 0 "0" "0" )); +DATA(insert ( 2803 n 0 int8inc - int8pl int8inc int8dec - f f 0 20 0 20 0 "0" "0" )); /* var_pop */ -DATA(insert ( 2718 n 0 int8_accum numeric_var_pop int8_accum int8_accum_inv numeric_var_pop f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2719 n 0 int4_accum numeric_poly_var_pop int4_accum int4_accum_inv numeric_poly_var_pop f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2720 n 0 int2_accum numeric_poly_var_pop int2_accum int2_accum_inv numeric_poly_var_pop f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2721 n 0 float4_accum float8_var_pop - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2722 n 0 float8_accum float8_var_pop - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2723 n 0 numeric_accum numeric_var_pop numeric_accum numeric_accum_inv numeric_var_pop f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2718 n 0 int8_accum numeric_var_pop - int8_accum int8_accum_inv numeric_var_pop f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2719 n 0 int4_accum numeric_poly_var_pop - int4_accum int4_accum_inv numeric_poly_var_pop f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2720 n 0 int2_accum numeric_poly_var_pop - int2_accum int2_accum_inv numeric_poly_var_pop f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2721 n 0 float4_accum float8_var_pop - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2722 n 0 float8_accum float8_var_pop - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2723 n 0 numeric_accum numeric_var_pop - numeric_accum numeric_accum_inv numeric_var_pop f f 0 2281 128 2281 128 _null_ _null_ )); /* var_samp */ -DATA(insert ( 2641 n 0 int8_accum numeric_var_samp int8_accum int8_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2642 n 0 int4_accum numeric_poly_var_samp int4_accum int4_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2643 n 0 int2_accum numeric_poly_var_samp int2_accum int2_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2644 n 0 float4_accum float8_var_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2645 n 0 float8_accum float8_var_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2646 n 0 numeric_accum numeric_var_samp numeric_accum numeric_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2641 n 0 int8_accum numeric_var_samp - int8_accum int8_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2642 n 0 int4_accum numeric_poly_var_samp - int4_accum int4_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2643 n 0 int2_accum numeric_poly_var_samp - int2_accum int2_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2644 n 0 float4_accum float8_var_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2645 n 0 float8_accum float8_var_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2646 n 0 numeric_accum numeric_var_samp - numeric_accum numeric_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); /* variance: historical Postgres syntax for var_samp */ -DATA(insert ( 2148 n 0 int8_accum numeric_var_samp int8_accum int8_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2149 n 0 int4_accum numeric_poly_var_samp int4_accum int4_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2150 n 0 int2_accum numeric_poly_var_samp int2_accum int2_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2151 n 0 float4_accum float8_var_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2152 n 0 float8_accum float8_var_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2153 n 0 numeric_accum numeric_var_samp numeric_accum numeric_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2148 n 0 int8_accum numeric_var_samp - int8_accum int8_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2149 n 0 int4_accum numeric_poly_var_samp - int4_accum int4_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2150 n 0 int2_accum numeric_poly_var_samp - int2_accum int2_accum_inv numeric_poly_var_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2151 n 0 float4_accum float8_var_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2152 n 0 float8_accum float8_var_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2153 n 0 numeric_accum numeric_var_samp - numeric_accum numeric_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); /* stddev_pop */ -DATA(insert ( 2724 n 0 int8_accum numeric_stddev_pop int8_accum int8_accum_inv numeric_stddev_pop f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2725 n 0 int4_accum numeric_poly_stddev_pop int4_accum int4_accum_inv numeric_poly_stddev_pop f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2726 n 0 int2_accum numeric_poly_stddev_pop int2_accum int2_accum_inv numeric_poly_stddev_pop f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2727 n 0 float4_accum float8_stddev_pop - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2728 n 0 float8_accum float8_stddev_pop - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2729 n 0 numeric_accum numeric_stddev_pop numeric_accum numeric_accum_inv numeric_stddev_pop f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2724 n 0 int8_accum numeric_stddev_pop - int8_accum int8_accum_inv numeric_stddev_pop f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2725 n 0 int4_accum numeric_poly_stddev_pop - int4_accum int4_accum_inv numeric_poly_stddev_pop f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2726 n 0 int2_accum numeric_poly_stddev_pop - int2_accum int2_accum_inv numeric_poly_stddev_pop f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2727 n 0 float4_accum float8_stddev_pop - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2728 n 0 float8_accum float8_stddev_pop - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2729 n 0 numeric_accum numeric_stddev_pop - numeric_accum numeric_accum_inv numeric_stddev_pop f f 0 2281 128 2281 128 _null_ _null_ )); /* stddev_samp */ -DATA(insert ( 2712 n 0 int8_accum numeric_stddev_samp int8_accum int8_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2713 n 0 int4_accum numeric_poly_stddev_samp int4_accum int4_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2714 n 0 int2_accum numeric_poly_stddev_samp int2_accum int2_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2715 n 0 float4_accum float8_stddev_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2716 n 0 float8_accum float8_stddev_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2717 n 0 numeric_accum numeric_stddev_samp numeric_accum numeric_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2712 n 0 int8_accum numeric_stddev_samp - int8_accum int8_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2713 n 0 int4_accum numeric_poly_stddev_samp - int4_accum int4_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2714 n 0 int2_accum numeric_poly_stddev_samp - int2_accum int2_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2715 n 0 float4_accum float8_stddev_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2716 n 0 float8_accum float8_stddev_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2717 n 0 numeric_accum numeric_stddev_samp - numeric_accum numeric_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); /* stddev: historical Postgres syntax for stddev_samp */ -DATA(insert ( 2154 n 0 int8_accum numeric_stddev_samp int8_accum int8_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); -DATA(insert ( 2155 n 0 int4_accum numeric_poly_stddev_samp int4_accum int4_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2156 n 0 int2_accum numeric_poly_stddev_samp int2_accum int2_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); -DATA(insert ( 2157 n 0 float4_accum float8_stddev_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2158 n 0 float8_accum float8_stddev_samp - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); -DATA(insert ( 2159 n 0 numeric_accum numeric_stddev_samp numeric_accum numeric_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2154 n 0 int8_accum numeric_stddev_samp - int8_accum int8_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); +DATA(insert ( 2155 n 0 int4_accum numeric_poly_stddev_samp - int4_accum int4_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2156 n 0 int2_accum numeric_poly_stddev_samp - int2_accum int2_accum_inv numeric_poly_stddev_samp f f 0 2281 48 2281 48 _null_ _null_ )); +DATA(insert ( 2157 n 0 float4_accum float8_stddev_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2158 n 0 float8_accum float8_stddev_samp - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); +DATA(insert ( 2159 n 0 numeric_accum numeric_stddev_samp - numeric_accum numeric_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); /* SQL2003 binary regression aggregates */ -DATA(insert ( 2818 n 0 int8inc_float8_float8 - - - - f f 0 20 0 0 0 "0" _null_ )); -DATA(insert ( 2819 n 0 float8_regr_accum float8_regr_sxx - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2820 n 0 float8_regr_accum float8_regr_syy - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2821 n 0 float8_regr_accum float8_regr_sxy - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2822 n 0 float8_regr_accum float8_regr_avgx - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2823 n 0 float8_regr_accum float8_regr_avgy - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2824 n 0 float8_regr_accum float8_regr_r2 - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2825 n 0 float8_regr_accum float8_regr_slope - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2826 n 0 float8_regr_accum float8_regr_intercept - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2827 n 0 float8_regr_accum float8_covar_pop - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2828 n 0 float8_regr_accum float8_covar_samp - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); -DATA(insert ( 2829 n 0 float8_regr_accum float8_corr - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2818 n 0 int8inc_float8_float8 - - - - - f f 0 20 0 0 0 "0" _null_ )); +DATA(insert ( 2819 n 0 float8_regr_accum float8_regr_sxx - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2820 n 0 float8_regr_accum float8_regr_syy - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2821 n 0 float8_regr_accum float8_regr_sxy - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2822 n 0 float8_regr_accum float8_regr_avgx - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2823 n 0 float8_regr_accum float8_regr_avgy - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2824 n 0 float8_regr_accum float8_regr_r2 - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2825 n 0 float8_regr_accum float8_regr_slope - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2826 n 0 float8_regr_accum float8_regr_intercept - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2827 n 0 float8_regr_accum float8_covar_pop - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2828 n 0 float8_regr_accum float8_covar_samp - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); +DATA(insert ( 2829 n 0 float8_regr_accum float8_corr - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); /* boolean-and and boolean-or */ -DATA(insert ( 2517 n 0 booland_statefunc - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 16 _null_ _null_ )); -DATA(insert ( 2518 n 0 boolor_statefunc - bool_accum bool_accum_inv bool_anytrue f f 59 16 0 2281 16 _null_ _null_ )); -DATA(insert ( 2519 n 0 booland_statefunc - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 16 _null_ _null_ )); +DATA(insert ( 2517 n 0 booland_statefunc - - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 16 _null_ _null_ )); +DATA(insert ( 2518 n 0 boolor_statefunc - - bool_accum bool_accum_inv bool_anytrue f f 59 16 0 2281 16 _null_ _null_ )); +DATA(insert ( 2519 n 0 booland_statefunc - - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 16 _null_ _null_ )); /* bitwise integer */ -DATA(insert ( 2236 n 0 int2and - - - - f f 0 21 0 0 0 _null_ _null_ )); -DATA(insert ( 2237 n 0 int2or - - - - f f 0 21 0 0 0 _null_ _null_ )); -DATA(insert ( 2238 n 0 int4and - - - - f f 0 23 0 0 0 _null_ _null_ )); -DATA(insert ( 2239 n 0 int4or - - - - f f 0 23 0 0 0 _null_ _null_ )); -DATA(insert ( 2240 n 0 int8and - - - - f f 0 20 0 0 0 _null_ _null_ )); -DATA(insert ( 2241 n 0 int8or - - - - f f 0 20 0 0 0 _null_ _null_ )); -DATA(insert ( 2242 n 0 bitand - - - - f f 0 1560 0 0 0 _null_ _null_ )); -DATA(insert ( 2243 n 0 bitor - - - - f f 0 1560 0 0 0 _null_ _null_ )); +DATA(insert ( 2236 n 0 int2and - int2and - - - f f 0 21 0 0 0 _null_ _null_ )); +DATA(insert ( 2237 n 0 int2or - int2or - - - f f 0 21 0 0 0 _null_ _null_ )); +DATA(insert ( 2238 n 0 int4and - int4and - - - f f 0 23 0 0 0 _null_ _null_ )); +DATA(insert ( 2239 n 0 int4or - int4or - - - f f 0 23 0 0 0 _null_ _null_ )); +DATA(insert ( 2240 n 0 int8and - int8and - - - f f 0 20 0 0 0 _null_ _null_ )); +DATA(insert ( 2241 n 0 int8or - int8or - - - f f 0 20 0 0 0 _null_ _null_ )); +DATA(insert ( 2242 n 0 bitand - bitand - - - f f 0 1560 0 0 0 _null_ _null_ )); +DATA(insert ( 2243 n 0 bitor - bitor - - - f f 0 1560 0 0 0 _null_ _null_ )); /* xml */ -DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_ _null_ )); +DATA(insert ( 2901 n 0 xmlconcat2 - - - - - f f 0 142 0 0 0 _null_ _null_ )); /* array */ -DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 4053 n 0 array_agg_array_transfn array_agg_array_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 4053 n 0 array_agg_array_transfn array_agg_array_finalfn - - - - t f 0 2281 0 0 0 _null_ _null_ )); /* text */ -DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* bytea */ -DATA(insert ( 3545 n 0 bytea_string_agg_transfn bytea_string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3545 n 0 bytea_string_agg_transfn bytea_string_agg_finalfn - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* json */ -DATA(insert ( 3175 n 0 json_agg_transfn json_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3197 n 0 json_object_agg_transfn json_object_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3175 n 0 json_agg_transfn json_agg_finalfn - - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3197 n 0 json_object_agg_transfn json_object_agg_finalfn - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* jsonb */ -DATA(insert ( 3267 n 0 jsonb_agg_transfn jsonb_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3270 n 0 jsonb_object_agg_transfn jsonb_object_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3267 n 0 jsonb_agg_transfn jsonb_agg_finalfn - - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3270 n 0 jsonb_object_agg_transfn jsonb_object_agg_finalfn - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* ordered-set and hypothetical-set aggregates */ -DATA(insert ( 3972 o 1 ordered_set_transition percentile_disc_final - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3974 o 1 ordered_set_transition percentile_cont_float8_final - - - f f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3976 o 1 ordered_set_transition percentile_cont_interval_final - - - f f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3978 o 1 ordered_set_transition percentile_disc_multi_final - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3980 o 1 ordered_set_transition percentile_cont_float8_multi_final - - - f f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3982 o 1 ordered_set_transition percentile_cont_interval_multi_final - - - f f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3984 o 0 ordered_set_transition mode_final - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3986 h 1 ordered_set_transition_multi rank_final - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3988 h 1 ordered_set_transition_multi percent_rank_final - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3990 h 1 ordered_set_transition_multi cume_dist_final - - - t f 0 2281 0 0 0 _null_ _null_ )); -DATA(insert ( 3992 h 1 ordered_set_transition_multi dense_rank_final - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3972 o 1 ordered_set_transition percentile_disc_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3974 o 1 ordered_set_transition percentile_cont_float8_final - - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3976 o 1 ordered_set_transition percentile_cont_interval_final - - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3978 o 1 ordered_set_transition percentile_disc_multi_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3980 o 1 ordered_set_transition percentile_cont_float8_multi_final - - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3982 o 1 ordered_set_transition percentile_cont_interval_multi_final - - - - f f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3984 o 0 ordered_set_transition mode_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3986 h 1 ordered_set_transition_multi rank_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3988 h 1 ordered_set_transition_multi percent_rank_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3990 h 1 ordered_set_transition_multi cume_dist_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); +DATA(insert ( 3992 h 1 ordered_set_transition_multi dense_rank_final - - - - t f 0 2281 0 0 0 _null_ _null_ )); /* @@ -322,6 +325,7 @@ extern ObjectAddress AggregateCreate(const char *aggName, Oid variadicArgType, List *aggtransfnName, List *aggfinalfnName, + List *aggcombinefnName, List *aggmtransfnName, List *aggminvtransfnName, List *aggmfinalfnName, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 4ae2f3e..70e88c7 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1845,6 +1845,8 @@ typedef struct AggState AggStatePerTrans curpertrans; /* currently active trans state */ bool input_done; /* indicates end of input */ bool agg_done; /* indicates completion of Agg scan */ + bool combineStates; /* input tuples contain transition states */ + bool finalizeAggs; /* should we call the finalfn on agg states? */ int projected_set; /* The last projected grouping set */ int current_set; /* The current grouping set being evaluated */ Bitmapset *grouped_cols; /* grouped cols in current projection */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index cc259f1..6a1353d 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -715,6 +715,8 @@ typedef struct Agg AggStrategy aggstrategy; int numCols; /* number of grouping columns */ AttrNumber *grpColIdx; /* their indexes in the target list */ + bool combineStates; /* input tuples contain transition states */ + bool finalizeAggs; /* should we call the finalfn on agg states? */ Oid *grpOperators; /* equality operators to compare with */ long numGroups; /* estimated number of groups in input */ List *groupingSets; /* grouping sets to use */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 79bed33..e8ca077 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -16,6 +16,7 @@ #include "access/sdir.h" #include "lib/stringinfo.h" +#include "nodes/plannodes.h" #include "nodes/params.h" #include "nodes/parsenodes.h" #include "storage/block.h" @@ -260,7 +261,14 @@ typedef struct PlannerInfo bool hasPseudoConstantQuals; /* true if any RestrictInfo has * pseudoconstant = true */ bool hasRecursion; /* true if planning a recursive WITH item */ - + Index aggregaterel; /* Index of relation to perform early + * aggregation on, or 0 if not enabled */ + Index groupbyrel; /* Index of single relation to perform group by + * on, or 0 if not enabled. */ + Index groupbeforejoinrel; /* Index of additional rel which was added + * to rewrite the group by to happen before + * joining to any other relations. + */ /* These fields are used only when hasRecursion is true: */ int wt_param_id; /* PARAM_EXEC ID for the work table */ struct Plan *non_recursive_plan; /* plan for non-recursive term */ @@ -450,7 +458,8 @@ typedef struct RelOptInfo /* per-relation planner control flags */ bool consider_startup; /* keep cheap-startup-cost paths? */ bool consider_param_startup; /* ditto, for parameterized paths? */ - + bool partial_aggregate; /* true if aggregates should not be + * finalized. */ /* materialization information */ List *reltargetlist; /* Vars to be output by scan of relation */ List *pathlist; /* Path structures */ diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index dd43e45..aaa1e08 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -61,6 +61,7 @@ extern bool enable_nestloop; extern bool enable_material; extern bool enable_mergejoin; extern bool enable_hashjoin; +extern bool enable_earlygrouping; extern int constraint_exclusion; extern double clamp_row_est(double nrows); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 52b077a..9540061 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -36,6 +36,8 @@ extern RelOptInfo *query_planner(PlannerInfo *root, List *tlist, extern void preprocess_minmax_aggregates(PlannerInfo *root, List *tlist); extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist, const AggClauseCosts *aggcosts, Path *best_path); +extern List *setup_grouping_rel(PlannerInfo *root, List *joinlist); +extern bool prepare_group_before_join(PlannerInfo *root, Query *parse); /* * prototypes for plan/createplan.c @@ -59,9 +61,8 @@ extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls, extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual, AggStrategy aggstrategy, const AggClauseCosts *aggcosts, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, - List *groupingSets, - long numGroups, - Plan *lefttree); + List *groupingSets, long numGroups, bool combineStates, + bool finalizeAggs, Plan *lefttree); extern WindowAgg *make_windowagg(PlannerInfo *root, List *tlist, List *windowFuncs, Index winref, int partNumCols, AttrNumber *partColIdx, Oid *partOperators, diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index b10a504..cfedf1f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -32,8 +32,8 @@ extern PlannedStmt *standard_planner(Query *parse, int cursorOptions, extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, - bool hasRecursion, double tuple_fraction, - PlannerInfo **subroot); + bool hasRecursion, bool finalize_aggregates, + double tuple_fraction, PlannerInfo **subroot); extern void add_tlist_costs_to_plan(PlannerInfo *root, Plan *plan, List *tlist); diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h index e2b3894..621b6b9 100644 --- a/src/include/parser/parse_agg.h +++ b/src/include/parser/parse_agg.h @@ -46,6 +46,12 @@ extern void build_aggregate_transfn_expr(Oid *agg_input_types, Expr **transfnexpr, Expr **invtransfnexpr); +extern void build_aggregate_combinefn_expr(bool agg_variadic, + Oid agg_state_type, + Oid agg_input_collation, + Oid combinefn_oid, + Expr **combinefnexpr); + extern void build_aggregate_finalfn_expr(Oid *agg_input_types, int num_finalfn_inputs, Oid agg_state_type, diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out index 82a34fb..906c2b5 100644 --- a/src/test/regress/expected/create_aggregate.out +++ b/src/test/regress/expected/create_aggregate.out @@ -101,6 +101,13 @@ CREATE AGGREGATE sumdouble (float8) msfunc = float8pl, minvfunc = float8mi ); +-- aggregate combine functions +CREATE AGGREGATE mymax (int) +( + stype = int4, + sfunc = int4larger, + cfunc = int4larger +); -- invalid: nonstrict inverse with strict forward function CREATE FUNCTION float8mi_n(float8, float8) RETURNS float8 AS $$ SELECT $1 - $2; $$ diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 00ef421..af3f9b7 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -2,6 +2,7 @@ SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%'; name | setting ----------------------+--------- enable_bitmapscan | on + enable_earlygrouping | on enable_hashagg | on enable_hashjoin | on enable_indexonlyscan | on @@ -12,7 +13,7 @@ SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%'; enable_seqscan | on enable_sort | on enable_tidscan | on -(11 rows) +(12 rows) CREATE TABLE foo2(fooid int, f2 int); INSERT INTO foo2 VALUES(1, 11); diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql index 0ec1572..0ebd0d9 100644 --- a/src/test/regress/sql/create_aggregate.sql +++ b/src/test/regress/sql/create_aggregate.sql @@ -115,6 +115,14 @@ CREATE AGGREGATE sumdouble (float8) minvfunc = float8mi ); +-- aggregate combine functions +CREATE AGGREGATE mymax (int) +( + stype = int4, + sfunc = int4larger, + cfunc = int4larger +); + -- invalid: nonstrict inverse with strict forward function CREATE FUNCTION float8mi_n(float8, float8) RETURNS float8 AS