diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b2dc451..5524f1b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1228,6 +1228,7 @@ _copyAggref(const Aggref *from) COPY_SCALAR_FIELD(aggfnoid); COPY_SCALAR_FIELD(aggtype); + COPY_SCALAR_FIELD(aggtranstype); COPY_SCALAR_FIELD(aggcollid); COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(aggdirectargs); @@ -1238,6 +1239,7 @@ _copyAggref(const Aggref *from) COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggvariadic); COPY_SCALAR_FIELD(aggkind); + COPY_SCALAR_FIELD(aggskipfinalize); COPY_SCALAR_FIELD(agglevelsup); COPY_LOCATION_FIELD(location); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 356fcaf..8bcbb75 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -191,6 +191,7 @@ _equalAggref(const Aggref *a, const Aggref *b) { COMPARE_SCALAR_FIELD(aggfnoid); COMPARE_SCALAR_FIELD(aggtype); + COMPARE_SCALAR_FIELD(aggtranstype); COMPARE_SCALAR_FIELD(aggcollid); COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(aggdirectargs); @@ -201,6 +202,11 @@ _equalAggref(const Aggref *a, const Aggref *b) COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(aggvariadic); COMPARE_SCALAR_FIELD(aggkind); + + /* Commented out for now as this causes: + "variable not found in subplan target list" + COMPARE_SCALAR_FIELD(aggskipfinalize); + */ COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_LOCATION_FIELD(location); diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index a11cb9f..5f547bd 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -57,7 +57,13 @@ exprType(const Node *expr) type = ((const Param *) expr)->paramtype; break; case T_Aggref: - type = ((const Aggref *) expr)->aggtype; + { + Aggref *aggref = (Aggref *) expr; + if (aggref->aggskipfinalize) + type = aggref->aggtranstype; + else + type = aggref->aggtype; + } break; case T_GroupingFunc: type = INT4OID; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 6f6ccdc..6870e39 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1016,6 +1016,7 @@ _outAggref(StringInfo str, const Aggref *node) WRITE_OID_FIELD(aggfnoid); WRITE_OID_FIELD(aggtype); + WRITE_OID_FIELD(aggtranstype); WRITE_OID_FIELD(aggcollid); WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(aggdirectargs); @@ -1026,6 +1027,7 @@ _outAggref(StringInfo str, const Aggref *node) WRITE_BOOL_FIELD(aggstar); WRITE_BOOL_FIELD(aggvariadic); WRITE_CHAR_FIELD(aggkind); + WRITE_BOOL_FIELD(aggskipfinalize); WRITE_UINT_FIELD(agglevelsup); WRITE_LOCATION_FIELD(location); } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 67d630f..c5c8b0d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -134,7 +134,104 @@ static Plan *build_grouping_chain(PlannerInfo *root, AttrNumber *groupColIdx, AggClauseCosts *agg_costs, long numGroups, - Plan *result_plan); + Plan *result_plan, + bool combineAggs); + +static List * +make_aggregate_tlist(PlannerInfo *root, + List *tlist, + AttrNumber **groupColIdx) +{ + Query *parse = root->parse; + List *sub_tlist; + List *non_group_cols; + List *non_group_vars; + int numCols; + ListCell *tl; + + *groupColIdx = NULL; + + /* + * Otherwise, we must build a tlist containing all grouping columns, plus + * any other Vars mentioned in the targetlist and HAVING qual. + */ + sub_tlist = NIL; + non_group_cols = NIL; + + numCols = list_length(parse->groupClause); + if (numCols > 0) + { + /* + * If grouping, create sub_tlist entries for all GROUP BY columns, and + * make an array showing where the group columns are in the sub_tlist. + * + * Note: with this implementation, the array entries will always be + * 1..N, but we don't want callers to assume that. + */ + AttrNumber *grpColIdx; + + grpColIdx = (AttrNumber *) palloc0(sizeof(AttrNumber) * numCols); + *groupColIdx = grpColIdx; + + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + int colno; + + colno = get_grouping_column_index(parse, tle); + if (colno >= 0) + { + /* + * It's a grouping column, so add it to the result tlist and + * remember its resno in grpColIdx[]. + */ + TargetEntry *newtle; + + newtle = makeTargetEntry((Expr *) copyObject(tle->expr), + list_length(sub_tlist) + 1, + tle->resname ? pstrdup(tle->resname) : NULL, + tle->resjunk); + newtle->ressortgroupref = tle->ressortgroupref; + sub_tlist = lappend(sub_tlist, newtle); + + Assert(grpColIdx[colno] == 0); /* no dups expected */ + grpColIdx[colno] = newtle->resno; + } + else + { + non_group_cols = lappend(non_group_cols, tle->expr); + } + } + } + else + { + foreach (tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + non_group_cols = lappend(non_group_cols, tle->expr); + } + } + + /* + * If there's a HAVING clause, we'll need need to ensure all Aggrefs from + * there are also in the targetlist + */ + if (parse->havingQual) + non_group_cols = lappend(non_group_cols, parse->havingQual); + + + non_group_vars = pull_var_clause((Node *) non_group_cols, + PVC_INCLUDE_AGGREGATES, + PVC_INCLUDE_PLACEHOLDERS); + + sub_tlist = add_to_flat_tlist(sub_tlist, non_group_vars); + + /* clean up cruft */ + list_free(non_group_vars); + list_free(non_group_cols); + + return sub_tlist; +} /***************************************************************************** * @@ -1889,6 +1986,15 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * results. */ bool need_sort_for_grouping = false; + bool combineaggs; + + /* + * Enable partial aggregation test if all aggregates support + * partial aggregation, and we have no grouping sets + */ + combineaggs = parse->groupingSets == NIL && + aggregates_allow_partial((Node *) tlist) && + aggregates_allow_partial(root->parse->havingQual); result_plan = create_plan(root, best_path); current_pathkeys = best_path->pathkeys; @@ -1906,6 +2012,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) need_tlist_eval = true; } + if (combineaggs) + need_tlist_eval = true; + /* * create_plan returns a plan with just a "flat" tlist of required * Vars. Usually we need to insert the sub_tlist as the tlist of @@ -1984,20 +2093,83 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ if (use_hashed_grouping) { - /* Hashed aggregate plan --- no sort needed */ - result_plan = (Plan *) make_agg(root, - tlist, - (List *) parse->havingQual, - AGG_HASHED, - &agg_costs, - numGroupCols, - groupColIdx, - extract_grouping_ops(parse->groupClause), - NIL, - numGroups, - false, - true, - result_plan); + if (combineaggs) + { + AttrNumber *groupColIdx; + List *aggtlist; + + /* Hack hack */ + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + { + ListCell *lc; + foreach(lc, aggtlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + Aggref *aggref = (Aggref *) tle->expr; + if (IsA(aggref, Aggref)) + aggref->aggskipfinalize = true; + } + } + /* End of hack */ + + /* Hashed aggregate plan --- no sort needed */ + result_plan = (Plan *) make_agg(root, + aggtlist, + NIL, + AGG_HASHED, + &agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(parse->groupClause), + NIL, + numGroups, + false, + false, + result_plan); + + result_plan->targetlist = aggtlist; + + /* + * Also, account for the cost of evaluation of the sub_tlist. + * See comments for add_tlist_costs_to_plan() for more info. + */ + add_tlist_costs_to_plan(root, result_plan, aggtlist); + + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + + result_plan = (Plan *) make_agg(root, + aggtlist, + (List *) parse->havingQual, + AGG_HASHED, + &agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(parse->groupClause), + NIL, + numGroups, + true, + true, + result_plan); + result_plan->targetlist = tlist; + + } + else + { + /* Hashed aggregate plan --- no sort needed */ + result_plan = (Plan *) make_agg(root, + tlist, + (List *) parse->havingQual, + AGG_HASHED, + &agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(parse->groupClause), + NIL, + numGroups, + false, + true, + result_plan); + } /* Hashed aggregation produces randomly-ordered results */ current_pathkeys = NIL; } @@ -2023,7 +2195,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) groupColIdx, &agg_costs, numGroups, - result_plan); + result_plan, + combineaggs); /* * these are destroyed by build_grouping_chain, so make sure @@ -2477,7 +2650,8 @@ build_grouping_chain(PlannerInfo *root, AttrNumber *groupColIdx, AggClauseCosts *agg_costs, long numGroups, - Plan *result_plan) + Plan *result_plan, + bool combineAggs) { AttrNumber *top_grpColIdx = groupColIdx; List *chain = NIL; @@ -2571,20 +2745,79 @@ build_grouping_chain(PlannerInfo *root, else numGroupCols = list_length(parse->groupClause); - result_plan = (Plan *) make_agg(root, - tlist, - (List *) parse->havingQual, - (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, - agg_costs, - numGroupCols, - top_grpColIdx, - extract_grouping_ops(groupClause), - gsets, - numGroups, - false, - true, - result_plan); + if (combineAggs) + { + AttrNumber *groupColIdx; + List *aggtlist; + + /* Hack hack */ + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + { + ListCell *lc; + foreach(lc, aggtlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + Aggref *aggref = (Aggref *) tle->expr; + if (IsA(aggref, Aggref)) + aggref->aggskipfinalize = true; + } + } + + /* End of hack */ + result_plan = (Plan *) make_agg(root, + aggtlist, + NIL, + (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, + agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(groupClause), + gsets, + numGroups, + false, + false, + result_plan); + result_plan->targetlist = aggtlist; + /* + * Also, account for the cost of evaluation of the sub_tlist. + * See comments for add_tlist_costs_to_plan() for more info. + */ + add_tlist_costs_to_plan(root, result_plan, aggtlist); + + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + + result_plan = (Plan *) make_agg(root, + aggtlist, + (List *) parse->havingQual, + (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, + agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(groupClause), + gsets, + numGroups, + true, + true, + result_plan); + result_plan->targetlist = tlist; + } + else + { + result_plan = (Plan *) make_agg(root, + tlist, + (List *) parse->havingQual, + (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, + agg_costs, + numGroupCols, + top_grpColIdx, + extract_grouping_ops(groupClause), + gsets, + numGroups, + false, + true, + result_plan); + } ((Agg *) result_plan)->chain = chain; /* diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 12e9290..fb423fc 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -139,6 +139,15 @@ static List *set_returning_clause_references(PlannerInfo *root, static bool fix_opfuncids_walker(Node *node, void *context); static bool extract_query_dependencies_walker(Node *node, PlannerInfo *context); +static void set_agg_references(PlannerInfo *root, Plan *plan, int rtoffset); +static Node *fix_combine_agg_expr(PlannerInfo *root, + Node *node, + indexed_tlist *subplan_itlist, + Index newvarno, + int rtoffset); +static Node *fix_combine_agg_expr_mutator(Node *node, + fix_upper_expr_context *context); + /***************************************************************************** * @@ -668,7 +677,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) } break; case T_Agg: - set_upper_references(root, plan, rtoffset); + set_agg_references(root, plan, rtoffset); break; case T_Group: set_upper_references(root, plan, rtoffset); @@ -2432,3 +2441,211 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) return expression_tree_walker(node, extract_query_dependencies_walker, (void *) context); } + +/* + * set_upper_references + * Update the targetlist and quals of an upper-level plan node + * to refer to the tuples returned by its lefttree subplan. + * Also perform opcode lookup for these expressions, and + * add regclass OIDs to root->glob->relationOids. + * + * This is used for single-input plan types like Agg, Group, Result. + * + * In most cases, we have to match up individual Vars in the tlist and + * qual expressions with elements of the subplan's tlist (which was + * generated by flatten_tlist() from these selfsame expressions, so it + * should have all the required variables). There is an important exception, + * however: GROUP BY and ORDER BY expressions will have been pushed into the + * subplan tlist unflattened. If these values are also needed in the output + * then we want to reference the subplan tlist element rather than recomputing + * the expression. + */ +static void +set_agg_references(PlannerInfo *root, Plan *plan, int rtoffset) +{ + Agg *agg = (Agg*)plan; + Plan *subplan = plan->lefttree; + indexed_tlist *subplan_itlist; + List *output_targetlist; + ListCell *l; + + subplan_itlist = build_tlist_index(subplan->targetlist); + + output_targetlist = NIL; + + if (agg->combineStates) + { + foreach(l, plan->targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Node *newexpr; + + /* If it's a non-Var sort/group item, first try to match by sortref */ + if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var)) + { + newexpr = (Node *) + search_indexed_tlist_for_sortgroupref((Node *) tle->expr, + tle->ressortgroupref, + subplan_itlist, + OUTER_VAR); + if (!newexpr) + newexpr = fix_combine_agg_expr(root, + (Node *) tle->expr, + subplan_itlist, + OUTER_VAR, + rtoffset); + } + else + newexpr = fix_combine_agg_expr(root, + (Node *) tle->expr, + subplan_itlist, + OUTER_VAR, + rtoffset); + tle = flatCopyTargetEntry(tle); + tle->expr = (Expr *) newexpr; + output_targetlist = lappend(output_targetlist, tle); + } + } + else + { + foreach(l, plan->targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Node *newexpr; + + /* If it's a non-Var sort/group item, first try to match by sortref */ + if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var)) + { + newexpr = (Node *) + search_indexed_tlist_for_sortgroupref((Node *) tle->expr, + tle->ressortgroupref, + subplan_itlist, + OUTER_VAR); + if (!newexpr) + newexpr = fix_upper_expr(root, + (Node *) tle->expr, + subplan_itlist, + OUTER_VAR, + rtoffset); + } + else + newexpr = fix_upper_expr(root, + (Node *) tle->expr, + subplan_itlist, + OUTER_VAR, + rtoffset); + tle = flatCopyTargetEntry(tle); + tle->expr = (Expr *) newexpr; + output_targetlist = lappend(output_targetlist, tle); + } + } + + plan->targetlist = output_targetlist; + + plan->qual = (List *) + fix_upper_expr(root, + (Node *) plan->qual, + subplan_itlist, + OUTER_VAR, + rtoffset); + + pfree(subplan_itlist); +} + + +/* + * This function is only used by combineAgg to set the Var nodes as args of + * Aggref reference output of a Gather plan. + */ +static Node * +fix_combine_agg_expr(PlannerInfo *root, + Node *node, + indexed_tlist *subplan_itlist, + Index newvarno, + int rtoffset) +{ + fix_upper_expr_context context; + + context.root = root; + context.subplan_itlist = subplan_itlist; + context.newvarno = newvarno; + context.rtoffset = rtoffset; + return fix_combine_agg_expr_mutator(node, &context); +} + +static Node * +fix_combine_agg_expr_mutator(Node *node, fix_upper_expr_context *context) +{ + Var *newvar; + + if (node == NULL) + return NULL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + newvar = search_indexed_tlist_for_var(var, + context->subplan_itlist, + context->newvarno, + context->rtoffset); + if (!newvar) + elog(ERROR, "variable not found in subplan target list"); + return (Node *) newvar; + } + if (IsA(node, Aggref)) + { + TargetEntry *tle; + Aggref *aggref = (Aggref*)node; + List *args = NIL; + + tle = tlist_member(node, context->subplan_itlist->tlist); + if (tle) + { + /* Found a matching subplan output expression */ + Var *newvar; + TargetEntry *newtle; + + newvar = makeVarFromTargetEntry(context->newvarno, tle); + newvar->varnoold = 0; /* wasn't ever a plain Var */ + newvar->varoattno = 0; + newvar->vartype = aggref->aggtranstype; /* hack hack */ + + /* update the args in the aggref */ + + /* makeTargetEntry ,always set resno to one for finialize agg */ + newtle = makeTargetEntry((Expr*)newvar,1,NULL,false); + args = lappend(args,newtle); + + /* + * Updated the args, let the newvar refer to the right position of + * the agg function in the subplan + */ + aggref->args = args; + + return (Node *) aggref; + } + } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + /* See if the PlaceHolderVar has bubbled up from a lower plan node */ + if (context->subplan_itlist->has_ph_vars) + { + newvar = search_indexed_tlist_for_non_var((Node *) phv, + context->subplan_itlist, + context->newvarno); + if (newvar) + return (Node *) newvar; + } + /* If not supplied by input plan, evaluate the contained expr */ + return fix_upper_expr_mutator((Node *) phv->phexpr, context); + } + if (IsA(node, Param)) + return fix_param_node(context->root, (Param *) node); + + fix_expr_common(context->root, node); + return expression_tree_mutator(node, + fix_combine_agg_expr_mutator, + (void *) context); +} diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 00f5ce3..4d3a24e 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -445,6 +445,7 @@ partial_aggregate_walker(Node *node, void *context) aggref->aggfnoid); aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); aggcombinefn = aggform->aggcombinefn; + aggref->aggtranstype = aggform->aggtranstype; /* Hack hack */ ReleaseSysCache(aggTuple); /* Do we have a combine function? */ diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index fa93a8c..5f37f14 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -3352,6 +3352,68 @@ numeric_avg_accum(PG_FUNCTION_ARGS) } /* + * Generic combine function for numeric aggregates + */ +Datum +numeric_avg_combine(PG_FUNCTION_ARGS) +{ + NumericAggState *state1; + NumericAggState *state2; + + state1 = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (NumericAggState *) PG_GETARG_POINTER(1); + + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + MemoryContext old_context; + + state1 = makeNumericAggState(fcinfo, false); + state1->N = state2->N; + state1->NaNcount = state2->NaNcount; + state1->maxScale = state2->maxScale; + state1->maxScaleCount = state2->maxScaleCount; + + old_context = MemoryContextSwitchTo(state1->agg_context); + + init_var(&state1->sumX); + set_var_from_var(&state2->sumX, &state1->sumX); + + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(state1); + } + + if (state2->N > 0) + { + MemoryContext old_context; + + state1->N += state2->N; + state1->NaNcount += state2->NaNcount; + + if (state2->maxScale > state1->maxScale) + state1->maxScale = state2->maxScale; + else if (state2->maxScale == state1->maxScale) + state1->maxScale += state2->maxScale; + + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(state1->agg_context); + + /* Accumulate sums */ + add_var(&(state1->sumX), &(state2->sumX), &(state1->sumX)); + + if (state1->calcSumX2) + add_var(&(state1->sumX2), &(state2->sumX2), &(state1->sumX2)); + + MemoryContextSwitchTo(old_context); + } + PG_RETURN_POINTER(state1); +} + +/* * Generic inverse transition function for numeric aggregates * (with or without requirement for X^2). */ diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index b306f9b..6ea8fbb 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -145,7 +145,7 @@ DATA(insert ( 2110 n 0 float4pl - float4pl - - - f f 0 700 0 0 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_ )); +DATA(insert ( 2114 n 0 numeric_avg_accum numeric_sum numeric_avg_combine numeric_avg_accum numeric_accum_inv numeric_sum f f 0 2281 128 2281 128 _null_ _null_ )); /* max */ DATA(insert ( 2115 n 0 int8larger - int8larger - - - f f 413 20 0 0 0 _null_ _null_ )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index d8640db..f328007 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2515,6 +2515,8 @@ DESCR("aggregate final function"); DATA(insert OID = 1833 ( numeric_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_accum _null_ _null_ _null_ )); DESCR("aggregate transition function"); DATA(insert OID = 2858 ( numeric_avg_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ )); +DESCR("aggregate combine function"); +DATA(insert OID = 3317 ( numeric_avg_combine PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ numeric_avg_combine _null_ _null_ _null_ )); DESCR("aggregate transition function"); DATA(insert OID = 3548 ( numeric_accum_inv PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_accum_inv _null_ _null_ _null_ )); DESCR("aggregate transition function"); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 60c1ca2..81dd4f8 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -256,6 +256,7 @@ typedef struct Aggref Expr xpr; Oid aggfnoid; /* pg_proc Oid of the aggregate */ Oid aggtype; /* type Oid of result of the aggregate */ + Oid aggtranstype; /* Hack hack */ Oid aggcollid; /* OID of collation of result */ Oid inputcollid; /* OID of collation that function should use */ List *aggdirectargs; /* direct arguments, if an ordered-set agg */ @@ -267,6 +268,7 @@ typedef struct Aggref bool aggvariadic; /* true if variadic arguments have been * combined into an array last argument */ char aggkind; /* aggregate kind (see pg_aggregate.h) */ + bool aggskipfinalize;/* TRUE if Aggref should skip finalization */ Index agglevelsup; /* > 0 if agg belongs to outer query */ int location; /* token location, or -1 if unknown */ } Aggref;