diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c new file mode 100644 index 930f2f1..59836ba *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** _copyAggref(const Aggref *from) *** 1245,1250 **** --- 1245,1251 ---- COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggvariadic); COPY_SCALAR_FIELD(aggkind); + COPY_SCALAR_FIELD(aggtransmultifn); COPY_SCALAR_FIELD(agglevelsup); COPY_SCALAR_FIELD(aggsplit); COPY_LOCATION_FIELD(location); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c new file mode 100644 index 806d0a9..8e789fe *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** _outPlannerInfo(StringInfo str, const Pl *** 2046,2051 **** --- 2046,2053 ---- WRITE_NODE_FIELD(append_rel_list); WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(placeholder_list); + WRITE_NODE_FIELD(grouped_var_list); + WRITE_BOOL_FIELD(all_baserels_grouped); WRITE_NODE_FIELD(fkey_list); WRITE_NODE_FIELD(query_pathkeys); WRITE_NODE_FIELD(group_pathkeys); diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c new file mode 100644 index b5cab0c..f89406d *** a/src/backend/optimizer/geqo/geqo_eval.c --- b/src/backend/optimizer/geqo/geqo_eval.c *************** merge_clump(PlannerInfo *root, List *clu *** 265,271 **** if (joinrel) { /* Create GatherPaths for any useful partial paths for rel */ ! generate_gather_paths(root, joinrel); /* Find and save the cheapest paths for this joinrel */ set_cheapest(joinrel); --- 265,271 ---- if (joinrel) { /* Create GatherPaths for any useful partial paths for rel */ ! generate_gather_paths(root, joinrel, false); /* Find and save the cheapest paths for this joinrel */ set_cheapest(joinrel); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c new file mode 100644 index 46d7d06..0ae0a1b *** a/src/backend/optimizer/path/allpaths.c --- b/src/backend/optimizer/path/allpaths.c *************** *** 24,29 **** --- 24,30 ---- #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "foreign/fdwapi.h" + #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG *************** *** 44,49 **** --- 45,51 ---- #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" + #include "utils/selfuncs.h" /* results of subquery_is_pushdown_safe */ *************** static void set_rel_pathlist(PlannerInfo *** 76,81 **** --- 78,85 ---- static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel); + static void create_plain_grouped_path(PlannerInfo *root, RelOptInfo *rel, + Path *subpath); static void set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, *************** set_rel_pathlist(PlannerInfo *root, RelO *** 457,463 **** * we'll consider gathering partial paths for the parent appendrel.) */ if (rel->reloptkind == RELOPT_BASEREL) ! generate_gather_paths(root, rel); /* * Allow a plugin to editorialize on the set of Paths for this base --- 461,470 ---- * we'll consider gathering partial paths for the parent appendrel.) */ if (rel->reloptkind == RELOPT_BASEREL) ! { ! generate_gather_paths(root, rel, false); ! generate_gather_paths(root, rel, true); ! } /* * Allow a plugin to editorialize on the set of Paths for this base *************** set_plain_rel_pathlist(PlannerInfo *root *** 656,662 **** required_outer = rel->lateral_relids; /* Consider sequential scan */ ! add_path(rel, create_seqscan_path(root, rel, required_outer, 0)); /* If appropriate, consider parallel sequential scan */ if (rel->consider_parallel && required_outer == NULL) --- 663,669 ---- required_outer = rel->lateral_relids; /* Consider sequential scan */ ! add_path(rel, create_seqscan_path(root, rel, required_outer, 0), false); /* If appropriate, consider parallel sequential scan */ if (rel->consider_parallel && required_outer == NULL) *************** static void *** 677,682 **** --- 684,690 ---- create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel) { int parallel_workers; + Path *path; /* * If the user has set the parallel_workers reloption, use that; otherwise *************** create_plain_partial_paths(PlannerInfo * *** 727,733 **** return; /* Add an unordered partial path based on a parallel sequential scan. */ ! add_partial_path(rel, create_seqscan_path(root, rel, NULL, parallel_workers)); } /* --- 735,888 ---- return; /* Add an unordered partial path based on a parallel sequential scan. */ ! path = create_seqscan_path(root, rel, NULL, parallel_workers); ! add_partial_path(rel, path, false); ! ! /* ! * Do partial aggregation at base relation level if the relation is ! * eligible for it. ! */ ! if (rel->reltarget_grouped) ! create_plain_grouped_path(root, rel, path); ! } ! ! /* ! * Apply (partial) aggregation to partial subpath. ! * ! * As we modify the subpath here, copy is taken before we adjust it. ! */ ! static void ! create_plain_grouped_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath) ! { ! Query *parse = root->parse; ! ListCell *lc; ! Expr *texpr; ! AggClauseCosts agg_costs; ! AggPath *agg_path; ! List *group_clause = NIL; ! List *group_exprs = NIL; ! List *agg_exprs = NIL; ! bool can_hash; ! Size hashaggtablesize; ! int i; ! double dNumGroups; ! Path *subpath_tmp; ! PathTarget *target = rel->reltarget_grouped; ! ! /* Copy the subpath before doing changes. */ ! subpath_tmp = makeNode(Path); ! memcpy(subpath_tmp, subpath, sizeof(Path)); ! subpath = subpath_tmp; ! ! /* Apply the specific target. */ ! subpath->pathtarget = target; ! ! /* ! * Find one grouping clause per grouping column. ! * ! * The "grouped target" should contain grouping expressions, and these ! * should have non-zero sortgroupref. ! */ ! Assert(target->sortgrouprefs != NULL); ! ! i = 0; ! foreach(lc, target->exprs) ! { ! Index sortgroupref; ! SortGroupClause *cl; ! ! texpr = (Expr *) lfirst(lc); ! sortgroupref = target->sortgrouprefs[i++]; ! ! if (sortgroupref == 0) ! { ! /* ! * Looks like "grouped var" representing Aggref - these should ! * appear at the end of the list. ! */ ! break; ! } ! ! /* ! * Find the clause by sortgroupref. ! * ! * All that create_agg_plan eventually needs of the clause is ! * tleSortGroupRef, so we don't have to care that the clause ! * expression might differ from texpr, in case texpr was derived from ! * EC. ! */ ! cl = get_sortgroupref_clause(sortgroupref, root->parse->groupClause); ! group_clause = lappend(group_clause, cl); ! group_exprs = lappend(group_exprs, texpr); ! } ! ! /* Now collect the aggregates. */ ! while (lc != NULL) ! { ! texpr = (Expr *) lfirst(lc); ! ! /* ! * texpr still contains the replacement var, so restore the actual ! * Aggref expression. ! */ ! Assert(IsA(texpr, Var)); ! agg_exprs = lappend(agg_exprs, find_grouped_var_expr(root, ! (Var *) texpr)); ! lc = lnext(lc); ! } ! ! Assert(agg_exprs != NIL); ! MemSet(&agg_costs, 0, sizeof(AggClauseCosts)); ! get_agg_clause_costs(root, (Node *) agg_exprs, AGGSPLIT_INITIAL_SERIAL, ! &agg_costs); ! ! can_hash = (parse->groupClause != NIL && ! parse->groupingSets == NIL && ! agg_costs.numOrderedAggs == 0 && ! grouping_is_hashable(parse->groupClause)); ! ! /* TODO Consider other kinds of aggregation. */ ! if (!can_hash) ! return; ! ! Assert(group_exprs != NIL); ! dNumGroups = estimate_num_groups(root, group_exprs, subpath->rows, NULL); ! ! hashaggtablesize = estimate_hashagg_tablesize(subpath, &agg_costs, ! dNumGroups); ! ! if (hashaggtablesize < work_mem * 1024L) ! { ! /* ! * Create the partial aggregation path. ! * ! * Note that target contains "grouped vars" (see GroupedVarInfo) ! * instead of Aggref expressions so far. Thus set_upper_references can ! * easily link TLEs of the upper node (which is not necessarily the ! * final Agg node) to the output of the patial Agg plan. Once ! * descended to this partial Agg node, set_upper_references will ! * install the Aggref expressions. ! */ ! Assert(group_clause != NIL); ! agg_path = create_agg_path(root, ! rel, ! subpath, ! target, ! AGG_HASHED, ! AGGSPLIT_INITIAL_SERIAL, ! group_clause, ! NIL, ! &agg_costs, ! dNumGroups); ! ! /* ! * The agg path should require no fewer parameters than the plain one. ! */ ! agg_path->path.param_info = subpath->param_info; ! ! /* Finally add the grouped path to the list of grouped base paths. */ ! add_partial_path(rel, (Path *) agg_path, true); ! } } /* *************** set_tablesample_rel_pathlist(PlannerInfo *** 813,819 **** path = (Path *) create_material_path(rel, path); } ! add_path(rel, path); /* For the moment, at least, there are no other paths to consider */ } --- 968,974 ---- path = (Path *) create_material_path(rel, path); } ! add_path(rel, path, false); /* For the moment, at least, there are no other paths to consider */ } *************** set_append_rel_pathlist(PlannerInfo *roo *** 1267,1273 **** * if we have zero or one live subpath due to constraint exclusion.) */ if (subpaths_valid) ! add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0)); /* * Consider an append of partial unordered, unparameterized partial paths. --- 1422,1429 ---- * if we have zero or one live subpath due to constraint exclusion.) */ if (subpaths_valid) ! add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0), ! false); /* * Consider an append of partial unordered, unparameterized partial paths. *************** set_append_rel_pathlist(PlannerInfo *roo *** 1295,1301 **** /* Generate a partial append path. */ appendpath = create_append_path(rel, partial_subpaths, NULL, parallel_workers); ! add_partial_path(rel, (Path *) appendpath); } /* --- 1451,1457 ---- /* Generate a partial append path. */ appendpath = create_append_path(rel, partial_subpaths, NULL, parallel_workers); ! add_partial_path(rel, (Path *) appendpath, false); } /* *************** set_append_rel_pathlist(PlannerInfo *roo *** 1346,1352 **** if (subpaths_valid) add_path(rel, (Path *) ! create_append_path(rel, subpaths, required_outer, 0)); } } --- 1502,1509 ---- if (subpaths_valid) add_path(rel, (Path *) ! create_append_path(rel, subpaths, required_outer, 0), ! false); } } *************** generate_mergeappend_paths(PlannerInfo * *** 1438,1450 **** rel, startup_subpaths, pathkeys, ! NULL)); if (startup_neq_total) add_path(rel, (Path *) create_merge_append_path(root, rel, total_subpaths, pathkeys, ! NULL)); } } --- 1595,1607 ---- rel, startup_subpaths, pathkeys, ! NULL), false); if (startup_neq_total) add_path(rel, (Path *) create_merge_append_path(root, rel, total_subpaths, pathkeys, ! NULL), false); } } *************** set_dummy_rel_pathlist(RelOptInfo *rel) *** 1576,1582 **** rel->pathlist = NIL; rel->partial_pathlist = NIL; ! add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0)); /* * We set the cheapest path immediately, to ensure that IS_DUMMY_REL() --- 1733,1739 ---- rel->pathlist = NIL; rel->partial_pathlist = NIL; ! add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0), false); /* * We set the cheapest path immediately, to ensure that IS_DUMMY_REL() *************** set_subquery_pathlist(PlannerInfo *root, *** 1789,1795 **** /* Generate outer path using this subpath */ add_path(rel, (Path *) create_subqueryscan_path(root, rel, subpath, ! pathkeys, required_outer)); } } --- 1946,1952 ---- /* Generate outer path using this subpath */ add_path(rel, (Path *) create_subqueryscan_path(root, rel, subpath, ! pathkeys, required_outer), false); } } *************** set_function_pathlist(PlannerInfo *root, *** 1858,1864 **** /* Generate appropriate path */ add_path(rel, create_functionscan_path(root, rel, ! pathkeys, required_outer)); } /* --- 2015,2021 ---- /* Generate appropriate path */ add_path(rel, create_functionscan_path(root, rel, ! pathkeys, required_outer), false); } /* *************** set_values_pathlist(PlannerInfo *root, R *** 1878,1884 **** required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_valuesscan_path(root, rel, required_outer)); } /* --- 2035,2041 ---- required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_valuesscan_path(root, rel, required_outer), false); } /* *************** set_cte_pathlist(PlannerInfo *root, RelO *** 1944,1950 **** required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_ctescan_path(root, rel, required_outer)); } /* --- 2101,2107 ---- required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_ctescan_path(root, rel, required_outer), false); } /* *************** set_worktable_pathlist(PlannerInfo *root *** 1994,2000 **** required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_worktablescan_path(root, rel, required_outer)); } /* --- 2151,2158 ---- required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_worktablescan_path(root, rel, required_outer), ! false); } /* *************** set_worktable_pathlist(PlannerInfo *root *** 2007,2019 **** * path that some GatherPath has a reference to.) */ void ! generate_gather_paths(PlannerInfo *root, RelOptInfo *rel) { Path *cheapest_partial_path; Path *simple_gather_path; /* If there are no partial paths, there's nothing to do here. */ ! if (rel->partial_pathlist == NIL) return; /* --- 2165,2182 ---- * path that some GatherPath has a reference to.) */ void ! generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool grouped) { Path *cheapest_partial_path; Path *simple_gather_path; + List *pathlist; + PathTarget *partial_target; + + pathlist = !grouped ? rel->partial_pathlist : + rel->partial_grouped_pathlist; /* If there are no partial paths, there's nothing to do here. */ ! if (pathlist == NIL) return; /* *************** generate_gather_paths(PlannerInfo *root, *** 2027,2037 **** * could usefully generate such a path from each partial path that has * non-NIL pathkeys. */ ! cheapest_partial_path = linitial(rel->partial_pathlist); simple_gather_path = (Path *) ! create_gather_path(root, rel, cheapest_partial_path, rel->reltarget, NULL, NULL); ! add_path(rel, simple_gather_path); } /* --- 2190,2203 ---- * could usefully generate such a path from each partial path that has * non-NIL pathkeys. */ ! cheapest_partial_path = linitial(pathlist); ! ! partial_target = !grouped ? rel->reltarget : rel->reltarget_grouped; ! simple_gather_path = (Path *) ! create_gather_path(root, rel, cheapest_partial_path, partial_target, NULL, NULL); ! add_path(rel, simple_gather_path, grouped); } /* *************** standard_join_search(PlannerInfo *root, *** 2196,2202 **** rel = (RelOptInfo *) lfirst(lc); /* Create GatherPaths for any useful partial paths for rel */ ! generate_gather_paths(root, rel); /* Find and save the cheapest paths for this rel */ set_cheapest(rel); --- 2362,2368 ---- rel = (RelOptInfo *) lfirst(lc); /* Create GatherPaths for any useful partial paths for rel */ ! generate_gather_paths(root, rel, false); /* Find and save the cheapest paths for this rel */ set_cheapest(rel); diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c new file mode 100644 index 7b43c4a..7c44938 *** a/src/backend/optimizer/path/indxpath.c --- b/src/backend/optimizer/path/indxpath.c *************** create_index_paths(PlannerInfo *root, Re *** 338,344 **** bitmapqual = choose_bitmap_and(root, rel, bitindexpaths); bpath = create_bitmap_heap_path(root, rel, bitmapqual, rel->lateral_relids, 1.0); ! add_path(rel, (Path *) bpath); } /* --- 338,344 ---- bitmapqual = choose_bitmap_and(root, rel, bitindexpaths); bpath = create_bitmap_heap_path(root, rel, bitmapqual, rel->lateral_relids, 1.0); ! add_path(rel, (Path *) bpath, false); } /* *************** create_index_paths(PlannerInfo *root, Re *** 411,417 **** loop_count = get_loop_count(root, rel->relid, required_outer); bpath = create_bitmap_heap_path(root, rel, bitmapqual, required_outer, loop_count); ! add_path(rel, (Path *) bpath); } } } --- 411,417 ---- loop_count = get_loop_count(root, rel->relid, required_outer); bpath = create_bitmap_heap_path(root, rel, bitmapqual, required_outer, loop_count); ! add_path(rel, (Path *) bpath, false); } } } *************** get_index_paths(PlannerInfo *root, RelOp *** 785,791 **** IndexPath *ipath = (IndexPath *) lfirst(lc); if (index->amhasgettuple) ! add_path(rel, (Path *) ipath); if (index->amhasgetbitmap && (ipath->path.pathkeys == NIL || --- 785,791 ---- IndexPath *ipath = (IndexPath *) lfirst(lc); if (index->amhasgettuple) ! add_path(rel, (Path *) ipath, false); if (index->amhasgetbitmap && (ipath->path.pathkeys == NIL || diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c new file mode 100644 index 7c30ec6..45318c8 *** a/src/backend/optimizer/path/joinpath.c --- b/src/backend/optimizer/path/joinpath.c *************** static void consider_parallel_nestloop(P *** 39,48 **** RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, ! JoinPathExtraData *extra); static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, ! JoinType jointype, JoinPathExtraData *extra); static List *select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, --- 39,49 ---- RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, ! JoinPathExtraData *extra, ! bool grouped); static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, ! JoinType jointype, JoinPathExtraData *extra, bool grouped); static List *select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, *************** add_paths_to_joinrel(PlannerInfo *root, *** 217,224 **** * joins, because there may be no other alternative. */ if (enable_hashjoin || jointype == JOIN_FULL) hash_inner_and_outer(root, joinrel, outerrel, innerrel, ! jointype, &extra); /* * 5. If inner and outer relations are foreign tables (or joins) belonging --- 218,229 ---- * joins, because there may be no other alternative. */ if (enable_hashjoin || jointype == JOIN_FULL) + { hash_inner_and_outer(root, joinrel, outerrel, innerrel, ! jointype, &extra, false); ! hash_inner_and_outer(root, joinrel, outerrel, innerrel, ! jointype, &extra, true); ! } /* * 5. If inner and outer relations are foreign tables (or joins) belonging *************** try_nestloop_path(PlannerInfo *root, *** 323,329 **** if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, ! pathkeys, required_outer)) { add_path(joinrel, (Path *) create_nestloop_path(root, --- 328,334 ---- if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, ! pathkeys, required_outer, false)) { add_path(joinrel, (Path *) create_nestloop_path(root, *************** try_nestloop_path(PlannerInfo *root, *** 336,342 **** inner_path, extra->restrictlist, pathkeys, ! required_outer)); } else { --- 341,348 ---- inner_path, extra->restrictlist, pathkeys, ! required_outer), ! false); } else { *************** try_partial_nestloop_path(PlannerInfo *r *** 357,363 **** Path *inner_path, List *pathkeys, JoinType jointype, ! JoinPathExtraData *extra) { JoinCostWorkspace workspace; --- 363,370 ---- Path *inner_path, List *pathkeys, JoinType jointype, ! JoinPathExtraData *extra, ! bool grouped) { JoinCostWorkspace workspace; *************** try_partial_nestloop_path(PlannerInfo *r *** 383,389 **** initial_cost_nestloop(root, &workspace, jointype, outer_path, inner_path, extra->sjinfo, &extra->semifactors); ! if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys)) return; /* Might be good enough to be worth trying, so let's try it. */ --- 390,397 ---- initial_cost_nestloop(root, &workspace, jointype, outer_path, inner_path, extra->sjinfo, &extra->semifactors); ! if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys, ! false)) return; /* Might be good enough to be worth trying, so let's try it. */ *************** try_partial_nestloop_path(PlannerInfo *r *** 398,404 **** inner_path, extra->restrictlist, pathkeys, ! NULL)); } /* --- 406,413 ---- inner_path, extra->restrictlist, pathkeys, ! NULL), ! grouped); } /* *************** try_mergejoin_path(PlannerInfo *root, *** 456,462 **** if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, ! pathkeys, required_outer)) { add_path(joinrel, (Path *) create_mergejoin_path(root, --- 465,471 ---- if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, ! pathkeys, required_outer, false)) { add_path(joinrel, (Path *) create_mergejoin_path(root, *************** try_mergejoin_path(PlannerInfo *root, *** 471,477 **** required_outer, mergeclauses, outersortkeys, ! innersortkeys)); } else { --- 480,486 ---- required_outer, mergeclauses, outersortkeys, ! innersortkeys), false); } else { *************** try_hashjoin_path(PlannerInfo *root, *** 492,498 **** Path *inner_path, List *hashclauses, JoinType jointype, ! JoinPathExtraData *extra) { Relids required_outer; JoinCostWorkspace workspace; --- 501,508 ---- Path *inner_path, List *hashclauses, JoinType jointype, ! JoinPathExtraData *extra, ! bool grouped) { Relids required_outer; JoinCostWorkspace workspace; *************** try_hashjoin_path(PlannerInfo *root, *** 521,527 **** if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, ! NIL, required_outer)) { add_path(joinrel, (Path *) create_hashjoin_path(root, --- 531,537 ---- if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, ! NIL, required_outer, grouped)) { add_path(joinrel, (Path *) create_hashjoin_path(root, *************** try_hashjoin_path(PlannerInfo *root, *** 534,540 **** inner_path, extra->restrictlist, required_outer, ! hashclauses)); } else { --- 544,551 ---- inner_path, extra->restrictlist, required_outer, ! hashclauses, ! grouped), grouped); } else { *************** try_partial_hashjoin_path(PlannerInfo *r *** 555,561 **** Path *inner_path, List *hashclauses, JoinType jointype, ! JoinPathExtraData *extra) { JoinCostWorkspace workspace; --- 566,573 ---- Path *inner_path, List *hashclauses, JoinType jointype, ! JoinPathExtraData *extra, ! bool grouped) { JoinCostWorkspace workspace; *************** try_partial_hashjoin_path(PlannerInfo *r *** 581,587 **** initial_cost_hashjoin(root, &workspace, jointype, hashclauses, outer_path, inner_path, extra->sjinfo, &extra->semifactors); ! if (!add_partial_path_precheck(joinrel, workspace.total_cost, NIL)) return; /* Might be good enough to be worth trying, so let's try it. */ --- 593,599 ---- initial_cost_hashjoin(root, &workspace, jointype, hashclauses, outer_path, inner_path, extra->sjinfo, &extra->semifactors); ! if (!add_partial_path_precheck(joinrel, workspace.total_cost, NIL, false)) return; /* Might be good enough to be worth trying, so let's try it. */ *************** try_partial_hashjoin_path(PlannerInfo *r *** 596,602 **** inner_path, extra->restrictlist, NULL, ! hashclauses)); } /* --- 608,616 ---- inner_path, extra->restrictlist, NULL, ! hashclauses, ! grouped), ! grouped); } /* *************** match_unsorted_outer(PlannerInfo *root, *** 1233,1240 **** if (joinrel->consider_parallel && nestjoinOK && save_jointype != JOIN_UNIQUE_OUTER && bms_is_empty(joinrel->lateral_relids)) consider_parallel_nestloop(root, joinrel, outerrel, innerrel, ! save_jointype, extra); } /* --- 1247,1258 ---- if (joinrel->consider_parallel && nestjoinOK && save_jointype != JOIN_UNIQUE_OUTER && bms_is_empty(joinrel->lateral_relids)) + { consider_parallel_nestloop(root, joinrel, outerrel, innerrel, ! save_jointype, extra, false); ! consider_parallel_nestloop(root, joinrel, outerrel, innerrel, ! save_jointype, extra, true); ! } } /* *************** consider_parallel_nestloop(PlannerInfo * *** 1254,1268 **** RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, ! JoinPathExtraData *extra) { JoinType save_jointype = jointype; ListCell *lc1; if (jointype == JOIN_UNIQUE_INNER) jointype = JOIN_INNER; ! foreach(lc1, outerrel->partial_pathlist) { Path *outerpath = (Path *) lfirst(lc1); List *pathkeys; --- 1272,1291 ---- RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, ! JoinPathExtraData *extra, ! bool grouped) { JoinType save_jointype = jointype; + List *outerrel_pathlist; ListCell *lc1; if (jointype == JOIN_UNIQUE_INNER) jointype = JOIN_INNER; ! outerrel_pathlist = !grouped ? outerrel->partial_pathlist : ! outerrel->partial_grouped_pathlist; ! ! foreach(lc1, outerrel_pathlist) { Path *outerpath = (Path *) lfirst(lc1); List *pathkeys; *************** consider_parallel_nestloop(PlannerInfo * *** 1304,1310 **** } try_partial_nestloop_path(root, joinrel, outerpath, innerpath, ! pathkeys, jointype, extra); } } } --- 1327,1333 ---- } try_partial_nestloop_path(root, joinrel, outerpath, innerpath, ! pathkeys, jointype, extra, grouped); } } } *************** hash_inner_and_outer(PlannerInfo *root, *** 1326,1332 **** RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, ! JoinPathExtraData *extra) { JoinType save_jointype = jointype; bool isouterjoin = IS_OUTER_JOIN(jointype); --- 1349,1356 ---- RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, ! JoinPathExtraData *extra, ! bool grouped) { JoinType save_jointype = jointype; bool isouterjoin = IS_OUTER_JOIN(jointype); *************** hash_inner_and_outer(PlannerInfo *root, *** 1382,1394 **** * can't use a hashjoin. (There's no use looking for alternative * input paths, since these should already be the least-parameterized * available paths.) */ if (PATH_PARAM_BY_REL(cheapest_total_outer, innerrel) || PATH_PARAM_BY_REL(cheapest_total_inner, outerrel)) return; /* Unique-ify if need be; we ignore parameterized possibilities */ ! if (jointype == JOIN_UNIQUE_OUTER) { cheapest_total_outer = (Path *) create_unique_path(root, outerrel, --- 1406,1445 ---- * can't use a hashjoin. (There's no use looking for alternative * input paths, since these should already be the least-parameterized * available paths.) + * + * (The same check should work for grouped paths, as these don't + * differ in parameterization.) */ if (PATH_PARAM_BY_REL(cheapest_total_outer, innerrel) || PATH_PARAM_BY_REL(cheapest_total_inner, outerrel)) return; + if (grouped) + { + /* + * The result of grouped path should be quite different from that + * of the non-grouped one as such, so don't bother combining + * multiple kinds of grouped paths. + * + * TODO As for JOIN_UNIQUE_OUTER and JOIN_UNIQUE_INNER, consider + * if the unique-ificiation is worth the effort. + */ + if (jointype != JOIN_UNIQUE_OUTER && + jointype != JOIN_UNIQUE_INNER && + outerrel->grouped_pathlist && innerrel->grouped_pathlist) + { + try_hashjoin_path(root, + joinrel, + (Path *) linitial(outerrel->grouped_pathlist), + (Path *) linitial(innerrel->grouped_pathlist), + hashclauses, + jointype, + extra, + true); + } + } /* Unique-ify if need be; we ignore parameterized possibilities */ ! else if (jointype == JOIN_UNIQUE_OUTER) { cheapest_total_outer = (Path *) create_unique_path(root, outerrel, *************** hash_inner_and_outer(PlannerInfo *root, *** 1401,1407 **** cheapest_total_inner, hashclauses, jointype, ! extra); /* no possibility of cheap startup here */ } else if (jointype == JOIN_UNIQUE_INNER) --- 1452,1459 ---- cheapest_total_inner, hashclauses, jointype, ! extra, ! false); /* no possibility of cheap startup here */ } else if (jointype == JOIN_UNIQUE_INNER) *************** hash_inner_and_outer(PlannerInfo *root, *** 1417,1423 **** cheapest_total_inner, hashclauses, jointype, ! extra); if (cheapest_startup_outer != NULL && cheapest_startup_outer != cheapest_total_outer) try_hashjoin_path(root, --- 1469,1476 ---- cheapest_total_inner, hashclauses, jointype, ! extra, ! false); if (cheapest_startup_outer != NULL && cheapest_startup_outer != cheapest_total_outer) try_hashjoin_path(root, *************** hash_inner_and_outer(PlannerInfo *root, *** 1426,1432 **** cheapest_total_inner, hashclauses, jointype, ! extra); } else { --- 1479,1486 ---- cheapest_total_inner, hashclauses, jointype, ! extra, ! false); } else { *************** hash_inner_and_outer(PlannerInfo *root, *** 1447,1453 **** cheapest_total_inner, hashclauses, jointype, ! extra); foreach(lc1, outerrel->cheapest_parameterized_paths) { --- 1501,1508 ---- cheapest_total_inner, hashclauses, jointype, ! extra, ! false); foreach(lc1, outerrel->cheapest_parameterized_paths) { *************** hash_inner_and_outer(PlannerInfo *root, *** 1481,1487 **** innerpath, hashclauses, jointype, ! extra); } } } --- 1536,1543 ---- innerpath, hashclauses, jointype, ! extra, ! false); } } } *************** hash_inner_and_outer(PlannerInfo *root, *** 1537,1543 **** try_partial_hashjoin_path(root, joinrel, cheapest_partial_outer, cheapest_safe_inner, ! hashclauses, jointype, extra); } } } --- 1593,1613 ---- try_partial_hashjoin_path(root, joinrel, cheapest_partial_outer, cheapest_safe_inner, ! hashclauses, jointype, extra, ! false); ! ! /* ! * If partial grouped path exists for either side, join the ! * cheapest ones into a new grouped path. ! */ ! if (outerrel->partial_grouped_pathlist && ! innerrel->partial_grouped_pathlist) ! try_partial_hashjoin_path(root, ! joinrel, ! (Path *) linitial(outerrel->partial_grouped_pathlist), ! (Path *) linitial(innerrel->partial_grouped_pathlist), ! hashclauses, jointype, extra, ! true); } } } diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c new file mode 100644 index 6f3c20b..51e01d7 *** a/src/backend/optimizer/path/joinrels.c --- b/src/backend/optimizer/path/joinrels.c *************** mark_dummy_rel(RelOptInfo *rel) *** 1197,1203 **** rel->partial_pathlist = NIL; /* Set up the dummy path */ ! add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0)); /* Set or update cheapest_total_path and related fields */ set_cheapest(rel); --- 1197,1203 ---- rel->partial_pathlist = NIL; /* Set up the dummy path */ ! add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0), false); /* Set or update cheapest_total_path and related fields */ set_cheapest(rel); diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c new file mode 100644 index 240ade6..6c3a77a *** a/src/backend/optimizer/path/tidpath.c --- b/src/backend/optimizer/path/tidpath.c *************** create_tidscan_paths(PlannerInfo *root, *** 263,267 **** if (tidquals) add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals, ! required_outer)); } --- 263,267 ---- if (tidquals) add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals, ! required_outer), false); } diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c new file mode 100644 index 6ceb801..6712f8f *** a/src/backend/optimizer/plan/initsplan.c --- b/src/backend/optimizer/plan/initsplan.c *************** *** 14,20 **** --- 14,25 ---- */ #include "postgres.h" + #include "access/htup_details.h" + #include "access/sysattr.h" + #include "catalog/pg_aggregate.h" + #include "catalog/pg_operator.h" #include "catalog/pg_type.h" + #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" *************** *** 26,35 **** --- 31,42 ---- #include "optimizer/planner.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" + #include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/analyze.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" + #include "utils/syscache.h" /* These parameters are set by GUC */ *************** static List *deconstruct_recurse(Planner *** 51,56 **** --- 58,67 ---- bool below_outer_join, Relids *qualscope, Relids *inner_join_rels, List **postponed_qual_list); + static void build_base_rel_tlist_grouped(PlannerInfo *root, RelOptInfo *rel, + List *aggregates, Aggref *countagg, + List **countagg_vars); + static void finalize_grouped_vars(PlannerInfo *root, List *countagg_vars); static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root, Relids left_rels, Relids right_rels, Relids inner_join_rels, *************** add_vars_to_targetlist(PlannerInfo *root *** 236,241 **** --- 247,861 ---- } } + /* + * Initialize rel->reltarget_grouped where possible. + * + * root->group_pathkeys must be setup before this function is called. + */ + extern void + build_base_rel_tlists_grouped(PlannerInfo *root) + { + List *tlist_vars; + List *aggregates = NIL; + ListCell *lc; + bool aggs_acceptable = true; + Aggref *countagg = NULL; + List *countagg_vars = NIL; + int nbaserels = 0; + int i; + + /* No grouping in the query? */ + if (!root->parse->groupClause || root->group_pathkeys == NIL) + return; + + /* TODO This is just for PoC. Relax the limitation later. */ + if (root->parse->havingQual) + return; + + /* + * If no join is expected, aggregation at base relation level makes no + * sense. XXX Is there simpler way to find out? (We're not interested in + * RELOPT_OTHER_MEMBER_REL, so simple_rel_array_size does not help.) + */ + for (i = 1; i < root->simple_rel_array_size; i++) + { + RelOptInfo *rel; + + rel = find_base_rel(root, i); + if (rel->reloptkind == RELOPT_BASEREL) + { + nbaserels++; + /* + * We only want to know whether the number of relations is greater + * than one. + */ + if (nbaserels > 1) + break; + } + } + if (nbaserels <= 1) + return; + + tlist_vars = pull_var_clause((Node *) root->processed_tlist, + PVC_INCLUDE_AGGREGATES); + if (tlist_vars == NIL) + return; + + /* tlist_vars may also contain Vars, but we only need Aggrefs. */ + foreach(lc, tlist_vars) + { + Expr *expr = (Expr *) lfirst(lc); + + if (!IsA(expr, Var)) + { + Aggref *aggref = (Aggref *) expr; + + Assert(IsA(aggref, Aggref)); + + /* TODO Think if (some of) these can be handled. */ + if (aggref->aggstar || aggref->aggvariadic || aggref->aggdirectargs || + aggref->aggorder || aggref->aggdistinct || aggref->aggfilter) + { + aggs_acceptable = false; + break; + } + + aggregates = lappend(aggregates, expr); + } + } + + if (aggregates != NIL && aggs_acceptable) + { + int i; + + if (countagg == NULL) + { + /* count(*) aggregate for base relation grouping. */ + countagg = makeNode(Aggref); + countagg->aggfnoid = COUNTFNOID; + countagg->aggkind = AGGKIND_NORMAL; + countagg->aggtype = INT8OID; + countagg->aggtranstype = INT8OID; + countagg->aggstar = true; + } + /* Process the individual base relations. */ + root->all_baserels_grouped = true; + for (i = 1; i < root->simple_rel_array_size; i++) + { + RelOptInfo *rel; + + rel = root->simple_rel_array[i]; + if (rel != NULL) + build_base_rel_tlist_grouped(root, rel, aggregates, countagg, + &countagg_vars); + + /* + * Don't bother processing the rest if the current rel prevents us + * from forming the final "grouped join". + */ + if (!root->all_baserels_grouped) + break; + } + + if (root->all_baserels_grouped) + finalize_grouped_vars(root, countagg_vars); + } + else + root->all_baserels_grouped = false; + + if (aggregates != NIL) + list_free(aggregates); + list_free(tlist_vars); + } + + /* + * Construct target of grouped relation - that can only contain grouping + * expressions and aggregates. Such a target can't be created if anything else + * is required on the output. + * + * countagg represents count(*) aggregate that each grouped rel must have to + * collect information of per-group row count. This information is used to + * adjust the transient state in the output of the final join. + * + * Variable representing the result of countagg is added to *countagg_vars + * list (besides being added to root->grouped_var_list as well, so that it the + * actual count(*) expression be restored by set_upper_references.) + */ + static void + build_base_rel_tlist_grouped(PlannerInfo *root, RelOptInfo *rel, + List *aggregates, Aggref *countagg, + List **countagg_vars) + { + RangeTblEntry *rte; + List *rel_aggregates; + ListCell *lc; + Relids agg_arg_rels_all, agg_arg_attrs_all; + PathTarget *reltarget_grouped; + bool first; + + rte = root->simple_rte_array[rel->relid]; + + /* + * XXX Append relation, as well as rtekind != RTE_RELATION seem to deserve + * separate patches. + */ + if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL || + rte->rtekind != RTE_RELATION) + { + root->all_baserels_grouped = false; + return; + } + + /* Caller should only pass elements of root->simple_rel_array. */ + Assert(rel->reloptkind == RELOPT_BASEREL); + Assert(bms_membership(rel->relids) == BMS_SINGLETON); + + /* + * If any outer join can set the attribute value to NULL, the aggregate + * would receive different input at the base rel level. + * + * TODO This as well as some other limitations below should only apply to + * the PoC version of the patch. In the future we should handle + * non-grouped relations by joining them to the grouped ones and applying + * additional partial aggregation to the final join. + */ + if (bms_overlap(rel->relids, root->nullable_baserels)) + { + root->all_baserels_grouped = false; + return; + } + + /* Collect aggregates applicable to the current relation. */ + rel_aggregates = NIL; + agg_arg_rels_all = NULL; + agg_arg_attrs_all = NULL; + foreach(lc, aggregates) + { + Aggref *aggref = (Aggref *) lfirst(lc); + Relids agg_arg_rels, agg_arg_attrs = NULL; + + /* TODO Does it matter if any argument contains PHV ? */ + agg_arg_rels = pull_varnos((Node *) aggref->args); + + /* Skip aggregates which don't reference rel at all. */ + if (!bms_overlap(rel->relids, agg_arg_rels)) + continue; + + /* + * If the aggregate references multiple rels, it must be processed + * higher in the join tree. + */ + if (bms_membership(agg_arg_rels) != BMS_SINGLETON) + { + /* + * TODO Consider relaxing this limitation by adding one extra + * partial aggregation below the final one. + */ + root->all_baserels_grouped = false; + + return; + } + + /* Accept this aggregate. */ + rel_aggregates = lappend(rel_aggregates, aggref); + agg_arg_rels_all = bms_union(agg_arg_rels_all, agg_arg_rels); + + /* Collect attnos while being here. */ + pull_varattnos((Node *) aggref->args, rel->relid, &agg_arg_attrs); + agg_arg_attrs_all = bms_union(agg_arg_attrs_all, agg_arg_attrs); + } + + Assert(rel->reltarget_grouped == NULL); + + /* + * Create a separate target which represents the actual output of grouped + * relation. This target will have aggregates replaced with special vars + * which will ensure propagation of the result of the partial aggregation + * of the base relation to the final Agg node. + * + * This target won't contain expressions that the grouped relation cannot + * emit. Another difference from rel->reltarget is that sortgroupref is + * set for each grouping expression. This is because the same target will + * be used for the partial aggregation. + * + * (Do not set rel->reltarget_grouped yet, as we might return + * prematurely.) + */ + reltarget_grouped = create_empty_pathtarget(); + + /* + * Check if all the output columns can be used as grouping expressions. + * + * TODO + * + * 1. How about PHVs? + * + * 2. Consider additional grouping expressions, just to be able to emit + * the columns that the query group clause doesn't mention. (These + * additional expressions wouldn't be used during the final aggregation.) + * Does this also mean that we shouldn't check existence of + * parse->groupClause earlier in this function? + */ + foreach(lc, rel->reltarget->exprs) + { + ListCell *lc2; + Expr *texpr = (Expr *) lfirst(lc); + bool is_grouping = false; + bool ec_found = false; + bool agg_arg_only = false; + + /* + * First, check if the query group clause contains exactly this + * expression. + */ + foreach(lc2, root->processed_tlist) + { + TargetEntry *te = (TargetEntry *) lfirst(lc2); + + Assert(IsA(te, TargetEntry)); + + if (equal(texpr, te->expr) && te->ressortgroupref > 0) + { + add_column_to_pathtarget(reltarget_grouped, texpr, + te->ressortgroupref); + is_grouping = true; + break; + } + } + + /* Go for the next expression if matched the current one. */ + if (is_grouping) + continue; + + /* + * If exactly this expression is not there, check if a grouping clause + * exists that belongs to the same equivalence class as the + * expression. + */ + foreach(lc2, root->group_pathkeys) + { + PathKey *pk = (PathKey *) lfirst(lc2); + EquivalenceClass *ec = pk->pk_eclass; + ListCell *lm; + EquivalenceMember *em; + Expr *em_expr = NULL; + Query *query = root->parse; + Index sortgroupref; + + /* + * Single-member EC cannot provide us with additional + * expression. + */ + if (list_length(ec->ec_members) < 2) + continue; + + /* We need equality anywhere in the join tree. */ + if (ec->ec_below_outer_join) + continue; + + /* + * TODO Reconsider this restriction. As the grouping expression is + * only evaluated at the relation level (and only the result will + * be propagated to the final targetlist), volatile function might + * be o.k. Need to think what volatile EC exactly means. + */ + if (ec->ec_has_volatile) + continue; + + foreach(lm, ec->ec_members) + { + em = (EquivalenceMember *) lfirst(lm); + + /* The EC has !ec_below_outer_join. */ + Assert(!em->em_nullable_relids); + if (equal(em->em_expr, texpr)) + { + em_expr = (Expr *) em->em_expr; + break; + } + } + + if (em_expr == NULL) + /* Go for the next EC. */ + continue; + + /* + * Find the corresponding SortGroupClause, which provides us + * with sortgroupref. (It can belong to any EC member.) + */ + sortgroupref = 0; + foreach(lm, ec->ec_members) + { + ListCell *lsg; + + em = (EquivalenceMember *) lfirst(lm); + foreach(lsg, query->groupClause) + { + SortGroupClause *sgc; + Expr *texpr; + + sgc = (SortGroupClause *) lfirst(lsg); + texpr = (Expr *) get_sortgroupclause_expr(sgc, + query->targetList); + if (equal(em->em_expr, texpr)) + { + Assert(sgc->tleSortGroupRef > 0); + sortgroupref = sgc->tleSortGroupRef; + break; + } + } + + if (sortgroupref > 0) + break; + } + + /* + * At least one EM of this EC should have correspond to a + * SortGroupClause, otherwise the EC could hardly exist. + */ + if (sortgroupref == 0) + elog(ERROR, "Grouping EC does not match any grouping clause."); + + /* It's o.k. to use the target expression for grouping. */ + add_column_to_pathtarget(reltarget_grouped, texpr, sortgroupref); + ec_found = true; + break; + } + + /* + * It may still be o.k. if the expression is only contained in Aggref + * - then it's not expected in the grouped output. + * + * TODO Try to handle generic expression, not only Var. That might + * require us to create rel->reltarget of the grouping rel in + * parallel to that of the plain rel, and adding whole expressions + * instead of individual vars. + */ + if (IsA(texpr, Var) && + bms_is_member(((Var *) texpr)->varattno - + FirstLowInvalidHeapAttributeNumber, agg_arg_attrs_all)) + agg_arg_only = true; + + /* + * A single mismatched expression makes the whole relation useless + * for grouping at base level. + */ + if (!ec_found && !agg_arg_only) + return; + } + + /* + * Add the count(*) aggregate, as its processing is nearly identical to + * that of other aggregates. It should be located in front of the other + * vars so they can reference it. + * + * TODO Avoid this if no aggregate in the query has aggtransmultifn. + */ + rel_aggregates = lcons(countagg, rel_aggregates); + + /* + * Add each aggregate to reltarget_grouped in the form of special + * variable, for which GroupedVarInfo will also be added to + * root->grouped_var_list. + * + * It doesn't matter that the relation contains no such variables - they + * will be replaced with the partially-aggregated Aggref before execution + * starts - see restore_grouped_var_expr. + */ + first = true; + foreach(lc, rel_aggregates) + { + Aggref *aggref = (Aggref *) lfirst(lc); + AttrNumber varattno; + Var *var; + GroupedVarInfo *gvi; + + rel->max_attr++; + varattno = rel->max_attr; + + var = makeVar(rel->relid, varattno, InvalidOid, 0, InvalidOid, 0); + add_new_column_to_pathtarget(reltarget_grouped, (Expr *) var); + + /* + * Create writable copy so that it can be marked as partial, and + * eventually be associated with the final aggregate - see + * set_upper_references. (XXX Is copy necessary for the count(*) + * aggregate as well?) + */ + aggref = copyObject(aggref); + + if (first) + { + /* count(*) should be the first item of the list. */ + Assert(aggref->aggfnoid == COUNTFNOID && aggref->aggstar); + + /* + * The variable representing the auxiliary count(*) is not subject + * to the final aggregation (in fact the result is used as + * argument of aggtransmultifn, which prepares input value for the + * final aggregationq), so there's never reason to restore the + * count(*) expression. Moreover, restoration of the count(*) + * expression outside Aggref (e.g. in the targetlist of the final + * join) would cause ERROR during plan initialization. + * + * So instead of creating GroupedVarInfo we only add the var to a + * list of vars that GroupedVarInfo.expr_intermediate can + * reference. + */ + *countagg_vars = lappend(*countagg_vars, var); + + first = false; + } + + /* + * Associate the expression with the corresponding variable, so that + * set_upper_references can do the changes explained above. + */ + gvi = (GroupedVarInfo *) palloc0(sizeof(GroupedVarInfo)); + gvi->var = var; + + /* The variable represents result of the partial aggregation. */ + mark_partial_aggref(aggref, AGGSPLIT_INITIAL_SERIAL); + /* The variable type info should reflect the transient type. */ + var->vartype = exprType((Node *) aggref); + var->vartypmod = exprTypmod((Node *) aggref); + var->varcollid = exprCollation((Node *) aggref); + + gvi->expr = (Expr *) aggref; + gvi->sortgroupref = 0; + root->grouped_var_list = lappend(root->grouped_var_list, gvi); + + /* + * Add the variable to rel->attr_needed to ensure propagation to the + * top-level target list. (Non-grouped path are not aware of the + * variable, and they should already be generated by now.) + */ + rel->attr_needed = (Relids *) + repalloc(rel->attr_needed, (rel->max_attr - rel->min_attr + 1) * + sizeof(Relids)); + rel->attr_needed[varattno - rel->min_attr] = bms_make_singleton(0); + + /* + * As set_rel_width should already have completed, it makes no sense + * to add the variable to attr_widths. Instead we only adjust + * rel->reltarget->width. + */ + /* TODO Adjust rel->reltarget->width */ + } + + rel->reltarget_grouped = reltarget_grouped; + } + + /* + * Initialize expr_intermediate of each GroupedVarInfo that requires it. + * This expression will adjust partial state of an aggregate prior to final + * aggregation, so it reflects existence of joins. + * + * If a single relation is aggregated, only the table determines how many + * times each value of aggregate argument appears in the relevant group. But + * if relation is joined to another one, the aggregate executed on the final + * join (i.e. w/o the relation-level aggregation) can actually receive the + * whole input set multiple times: some values of the grouping key present in + * the relation can match multiple values in the other table(s). + * + * sum() is an example of an aggregate where join matters, avg() is one where + * it does not. + * + * We simulate the effect of joins by applying aggtransmultifn (see + * pg_aggregate) to the result of relation-level aggregation. + */ + static void + finalize_grouped_vars(PlannerInfo *root, List *countagg_vars) + { + ListCell *lc; + Oid op_int8mul = InvalidOid; + + foreach(lc, root->grouped_var_list) + { + GroupedVarInfo *gvi = (GroupedVarInfo *) lfirst(lc); + Aggref *aggref = (Aggref *) gvi->expr; + ListCell *l; + Expr *factor = NULL; + + Assert(gvi->expr_intermediate == NULL); + Assert(IsA(aggref, Aggref)); + + /* Is transient state multiplication needed for this aggregate? */ + if (aggref->aggtransmultifn == InvalidOid) + continue; + + /* + * If there was no relation-level aggregation, each table joined to + * the one gvi->varno points at would increase the frequency of a + * value within a group by the factor which equals to the frequency of + * the corresponding grouping key in the table joined. + * + * To prepare the correct input for the final aggregation, the + * relation-level per-group state must be "multiplied" by the number + * of grouping key values that join of the *other* tables (i.e. all + * but the one referenced by the current aggregate) generates. + * + * This is why count(*) was added to each base relation. + */ + foreach(l, countagg_vars) + { + Var *var = (Var *) lfirst(l); + + /* Only the other tables do matter. */ + if (var->varno == gvi->var->varno) + continue; + + if (factor == NULL) + /* The first value. */ + factor = (Expr *) var; + else + { + OpExpr *op = makeNode(OpExpr); + + /* + * Multiply the intermediate result by the result of the next + * count(*) aggregate. + */ + op->opno = OPERATOR_INT8MUL; + if (op_int8mul == InvalidOid) + { + set_opfuncid(op); + op_int8mul = op->opfuncid; + } + else + op->opfuncid = op_int8mul; + + op->opresulttype = INT8OID; + op->opretset = false; + op->opcollid = InvalidOid; + op->inputcollid = InvalidOid; + op->args = list_make2(factor, var); + op->location = -1; + + factor = (Expr *) op; + } + } + + /* Construct the function call if needed. */ + if (factor != NULL) + { + FuncExpr *func = makeNode(FuncExpr); + + func->funcid = aggref->aggtransmultifn; + func->funcresulttype = aggref->aggtranstype; + func->funcretset = false; + func->funcvariadic = false; + func->funccollid = InvalidOid; + func->inputcollid = InvalidOid; + Assert(gvi->var != NULL); + func->args = list_make2(gvi->var, factor); + func->location = -1; + + gvi->expr_intermediate = (Expr *) func; + } + } + } + /***************************************************************************** * diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c new file mode 100644 index c3fbf3c..5b3bc71 *** a/src/backend/optimizer/plan/planagg.c --- b/src/backend/optimizer/plan/planagg.c *************** preprocess_minmax_aggregates(PlannerInfo *** 223,229 **** create_minmaxagg_path(root, grouped_rel, create_pathtarget(root, tlist), aggs_list, ! (List *) parse->havingQual)); } /* --- 223,229 ---- create_minmaxagg_path(root, grouped_rel, create_pathtarget(root, tlist), aggs_list, ! (List *) parse->havingQual), false); } /* diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c new file mode 100644 index e880759..c7d98ca *** a/src/backend/optimizer/plan/planmain.c --- b/src/backend/optimizer/plan/planmain.c *************** query_planner(PlannerInfo *root, List *t *** 83,89 **** add_path(final_rel, (Path *) create_result_path(root, final_rel, final_rel->reltarget, ! (List *) parse->jointree->quals)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(final_rel); --- 83,89 ---- add_path(final_rel, (Path *) create_result_path(root, final_rel, final_rel->reltarget, ! (List *) parse->jointree->quals), false); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(final_rel); *************** query_planner(PlannerInfo *root, List *t *** 176,181 **** --- 176,183 ---- */ (*qp_callback) (root, qp_extra); + build_base_rel_tlists_grouped(root); + /* * Examine any "placeholder" expressions generated during subquery pullup. * Make sure that the Vars they need are marked as needed at the relevant diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c new file mode 100644 index 207290f..eeac4ac *** a/src/backend/optimizer/plan/planner.c --- b/src/backend/optimizer/plan/planner.c *************** static double get_number_of_groups(Plann *** 109,117 **** double path_rows, List *rollup_lists, List *rollup_groupclauses); - static Size estimate_hashagg_tablesize(Path *path, - const AggClauseCosts *agg_costs, - double dNumGroups); static RelOptInfo *create_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, PathTarget *target, --- 109,114 ---- *************** inheritance_planner(PlannerInfo *root) *** 1396,1402 **** returningLists, rowMarks, NULL, ! SS_assign_special_param(root))); } /*-------------------- --- 1393,1399 ---- returningLists, rowMarks, NULL, ! SS_assign_special_param(root)), false); } /*-------------------- *************** grouping_planner(PlannerInfo *root, bool *** 2061,2067 **** } /* And shove it into final_rel */ ! add_path(final_rel, path); } /* --- 2058,2064 ---- } /* And shove it into final_rel */ ! add_path(final_rel, path, false); } /* *************** get_number_of_groups(PlannerInfo *root, *** 3270,3304 **** } /* - * estimate_hashagg_tablesize - * estimate the number of bytes that a hash aggregate hashtable will - * require based on the agg_costs, path width and dNumGroups. - */ - static Size - estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs, - double dNumGroups) - { - Size hashentrysize; - - /* Estimate per-hash-entry space at tuple width... */ - hashentrysize = MAXALIGN(path->pathtarget->width) + - MAXALIGN(SizeofMinimalTupleHeader); - - /* plus space for pass-by-ref transition values... */ - hashentrysize += agg_costs->transitionSpace; - /* plus the per-hash-entry overhead */ - hashentrysize += hash_agg_entry_size(agg_costs->numAggs); - - /* - * Note that this disregards the effect of fill-factor and growth policy - * of the hash-table. That's probably ok, given default the default - * fill-factor is relatively high. It'd be hard to meaningfully factor in - * "double-in-size" growth policies here. - */ - return hashentrysize * dNumGroups; - } - - /* * create_grouping_paths * * Build a new upperrel containing Paths for grouping and/or aggregation. --- 3267,3272 ---- *************** create_grouping_paths(PlannerInfo *root, *** 3419,3425 **** (List *) parse->havingQual); } ! add_path(grouped_rel, path); /* No need to consider any other alternatives. */ set_cheapest(grouped_rel); --- 3387,3393 ---- (List *) parse->havingQual); } ! add_path(grouped_rel, path, false); /* No need to consider any other alternatives. */ set_cheapest(grouped_rel); *************** create_grouping_paths(PlannerInfo *root, *** 3592,3598 **** parse->groupClause, NIL, &agg_partial_costs, ! dNumPartialGroups)); else add_partial_path(grouped_rel, (Path *) create_group_path(root, --- 3560,3567 ---- parse->groupClause, NIL, &agg_partial_costs, ! dNumPartialGroups), ! false); else add_partial_path(grouped_rel, (Path *) create_group_path(root, *************** create_grouping_paths(PlannerInfo *root, *** 3601,3607 **** partial_grouping_target, parse->groupClause, NIL, ! dNumPartialGroups)); } } } --- 3570,3577 ---- partial_grouping_target, parse->groupClause, NIL, ! dNumPartialGroups), ! false); } } } *************** create_grouping_paths(PlannerInfo *root, *** 3632,3638 **** parse->groupClause, NIL, &agg_partial_costs, ! dNumPartialGroups)); } } } --- 3602,3609 ---- parse->groupClause, NIL, &agg_partial_costs, ! dNumPartialGroups), ! false); } } } *************** create_grouping_paths(PlannerInfo *root, *** 3677,3683 **** rollup_lists, rollup_groupclauses, agg_costs, ! dNumGroups)); } else if (parse->hasAggs) { --- 3648,3654 ---- rollup_lists, rollup_groupclauses, agg_costs, ! dNumGroups), false); } else if (parse->hasAggs) { *************** create_grouping_paths(PlannerInfo *root, *** 3695,3701 **** parse->groupClause, (List *) parse->havingQual, agg_costs, ! dNumGroups)); } else if (parse->groupClause) { --- 3666,3672 ---- parse->groupClause, (List *) parse->havingQual, agg_costs, ! dNumGroups), false); } else if (parse->groupClause) { *************** create_grouping_paths(PlannerInfo *root, *** 3710,3716 **** target, parse->groupClause, (List *) parse->havingQual, ! dNumGroups)); } else { --- 3681,3687 ---- target, parse->groupClause, (List *) parse->havingQual, ! dNumGroups), false); } else { *************** create_grouping_paths(PlannerInfo *root, *** 3760,3766 **** parse->groupClause, (List *) parse->havingQual, &agg_final_costs, ! dNumGroups)); else add_path(grouped_rel, (Path *) create_group_path(root, --- 3731,3737 ---- parse->groupClause, (List *) parse->havingQual, &agg_final_costs, ! dNumGroups), false); else add_path(grouped_rel, (Path *) create_group_path(root, *************** create_grouping_paths(PlannerInfo *root, *** 3769,3775 **** target, parse->groupClause, (List *) parse->havingQual, ! dNumGroups)); } } --- 3740,3746 ---- target, parse->groupClause, (List *) parse->havingQual, ! dNumGroups), false); } } *************** create_grouping_paths(PlannerInfo *root, *** 3801,3807 **** parse->groupClause, (List *) parse->havingQual, agg_costs, ! dNumGroups)); } /* --- 3772,3778 ---- parse->groupClause, (List *) parse->havingQual, agg_costs, ! dNumGroups), false); } /* *************** create_grouping_paths(PlannerInfo *root, *** 3838,3846 **** parse->groupClause, (List *) parse->havingQual, &agg_final_costs, ! dNumGroups)); } } } /* Give a helpful error if we failed to find any implementation */ --- 3809,3895 ---- parse->groupClause, (List *) parse->havingQual, &agg_final_costs, ! dNumGroups), false); } } + + /* + * If input_rel has partially aggregated partial paths, perform the + * final aggregation. + * + * TODO Allow havingQual - currently not supported at base relation + * level. + */ + if (input_rel->partial_grouped_pathlist != NIL && + !parse->havingQual) + { + Path *path = (Path *) linitial(input_rel->partial_grouped_pathlist); + double total_groups = path->rows * path->parallel_workers; + + path = (Path *) create_gather_path(root, + input_rel, + path, + path->pathtarget, + NULL, + &total_groups); + + /* + * The input path is partially aggregated and the final + * aggregation - if the path wins - will be done below. So we're + * done with it for now. + */ + add_path(input_rel, path, true); + } + + /* + * If input_rel has partially aggregated paths, perform the final + * aggregation. + * + * TODO Allow havingQual - currently not supported at base relation + * level. + */ + if (input_rel->grouped_pathlist != NIL && + !parse->havingQual) + { + Path *pre_agg = (Path *) linitial(input_rel->grouped_pathlist); + PathTarget *proj_target; + + /* + * For each grouped variable in pre_agg->pathtarget ensure + * evaluation of expr_intermediate (see GroupedVarInfo). + */ + proj_target = + create_intermediate_grouping_target(root, + pre_agg->pathtarget); + pre_agg = (Path *) create_projection_path(root, input_rel, + pre_agg, + proj_target); + + dNumGroups = get_number_of_groups(root, + pre_agg->rows, + rollup_lists, + rollup_groupclauses); + + MemSet(&agg_final_costs, 0, sizeof(AggClauseCosts)); + get_agg_clause_costs(root, (Node *) target->exprs, + AGGSPLIT_FINAL_DESERIAL, + &agg_final_costs); + /* get_agg_clause_costs(root, parse->havingQual, */ + /* AGGSPLIT_FINAL_DESERIAL, */ + /* &agg_final_costs); */ + + add_path(grouped_rel, + (Path *) create_agg_path(root, grouped_rel, + pre_agg, + target, + AGG_HASHED, + AGGSPLIT_FINAL_DESERIAL, + parse->groupClause, + (List *) parse->havingQual, + &agg_final_costs, + dNumGroups), + false); + } } /* Give a helpful error if we failed to find any implementation */ *************** create_one_window_path(PlannerInfo *root *** 4053,4059 **** window_pathkeys); } ! add_path(window_rel, path); } /* --- 4102,4108 ---- window_pathkeys); } ! add_path(window_rel, path, false); } /* *************** create_distinct_paths(PlannerInfo *root, *** 4159,4165 **** create_upper_unique_path(root, distinct_rel, path, list_length(root->distinct_pathkeys), ! numDistinctRows)); } } --- 4208,4214 ---- create_upper_unique_path(root, distinct_rel, path, list_length(root->distinct_pathkeys), ! numDistinctRows), false); } } *************** create_distinct_paths(PlannerInfo *root, *** 4186,4192 **** create_upper_unique_path(root, distinct_rel, path, list_length(root->distinct_pathkeys), ! numDistinctRows)); } /* --- 4235,4241 ---- create_upper_unique_path(root, distinct_rel, path, list_length(root->distinct_pathkeys), ! numDistinctRows), false); } /* *************** create_distinct_paths(PlannerInfo *root, *** 4233,4239 **** parse->distinctClause, NIL, NULL, ! numDistinctRows)); } /* Give a helpful error if we failed to find any implementation */ --- 4282,4288 ---- parse->distinctClause, NIL, NULL, ! numDistinctRows), false); } /* Give a helpful error if we failed to find any implementation */ *************** create_ordered_paths(PlannerInfo *root, *** 4331,4337 **** path = apply_projection_to_path(root, ordered_rel, path, target); ! add_path(ordered_rel, path); } } --- 4380,4386 ---- path = apply_projection_to_path(root, ordered_rel, path, target); ! add_path(ordered_rel, path, false); } } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c new file mode 100644 index 413a0d9..632f4bb *** a/src/backend/optimizer/plan/setrefs.c --- b/src/backend/optimizer/plan/setrefs.c *************** set_upper_references(PlannerInfo *root, *** 1677,1686 **** indexed_tlist *subplan_itlist; List *output_targetlist; ListCell *l; ! subplan_itlist = build_tlist_index(subplan->targetlist); output_targetlist = NIL; foreach(l, plan->targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); --- 1677,1744 ---- indexed_tlist *subplan_itlist; List *output_targetlist; ListCell *l; + List *sub_tlist_save = NIL; + bool install_partial_aggrefs = false; ! if (root->grouped_var_list != NIL) ! { ! if (IsA(plan, Agg)) ! { ! Agg *agg = (Agg *) plan; ! ! if (agg->aggsplit == AGGSPLIT_FINAL_DESERIAL) ! { ! /* ! * convert_combining_aggrefs could have replaced some vars ! * with Aggref expressions representing the partial ! * aggregation. We need to restore the same Aggrefs in the ! * subplan targetlist, but this would break the subplan if ! * it's something else than the partial aggregation (i.e. the ! * partial aggregation takes place lower in the plan tree). So ! * we'll need restore the original list when done with the ! * references. ! */ ! if (!IsA(subplan, Agg)) ! sub_tlist_save = copyObject(subplan->targetlist); ! #ifdef USE_ASSERT_CHECKING ! else ! Assert(((Agg *) subplan)->aggsplit == AGGSPLIT_INITIAL_SERIAL); ! #endif /* USE_ASSERT_CHECKING */ ! ! /* ! * Restore the aggregate expressions that we might have ! * removed when planning for aggregation at base relation ! * level. ! * ! * TODO Optimize restore_grouping_expressions using a new ! * parameter indicating whether the targetlist can contain ! * "intermediate expression". Only Result (or also Gather if ! * we use its target list to evaluate "intermediate ! * expressions"?) node should contain those. ! */ ! subplan->targetlist = ! restore_grouping_expressions(root, subplan->targetlist); ! } ! else if (agg->aggsplit == AGGSPLIT_INITIAL_SERIAL) ! install_partial_aggrefs = true; ! } ! } + /* + * AGGSPLIT_INITIAL_SERIAL might be there just to implement grouped base + * relation. Since the parent node does not necessarily call + * set_upper_references() (note that there might be various nodes between + * the initial and the final aggregation), special effort is needed to + * ensure that "grouped vars" are replaced with the corresponding + * expressions (see GroupedVarInfo). + */ + if (install_partial_aggrefs) + plan->targetlist = restore_grouping_expressions(root, + plan->targetlist); + + subplan_itlist = build_tlist_index(subplan->targetlist); output_targetlist = NIL; + foreach(l, plan->targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); *************** set_upper_references(PlannerInfo *root, *** 1720,1725 **** --- 1778,1787 ---- OUTER_VAR, rtoffset); + /* Restore the original list if appropriate. */ + if (sub_tlist_save != NIL) + subplan->targetlist = sub_tlist_save; + pfree(subplan_itlist); } *************** convert_combining_aggrefs(Node *node, vo *** 1796,1801 **** --- 1858,1864 ---- (void *) context); } + /* * set_dummy_tlist_references * Replace the targetlist of an upper-level plan node with a simple *************** fix_join_expr_mutator(Node *node, fix_jo *** 2235,2240 **** --- 2298,2304 ---- (void *) context); } + /* * fix_upper_expr * Modifies an expression tree so that all Var nodes reference outputs diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c new file mode 100644 index 1bbbc29..e0b31c3 *** a/src/backend/optimizer/prep/prepunion.c --- b/src/backend/optimizer/prep/prepunion.c *************** plan_set_operations(PlannerInfo *root) *** 208,214 **** root->processed_tlist = top_tlist; /* Add only the final path to the SETOP upperrel. */ ! add_path(setop_rel, path); /* Let extensions possibly add some more paths */ if (create_upper_paths_hook) --- 208,214 ---- root->processed_tlist = top_tlist; /* Add only the final path to the SETOP upperrel. */ ! add_path(setop_rel, path, false); /* Let extensions possibly add some more paths */ if (create_upper_paths_hook) diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c new file mode 100644 index 3b7c56d..aefb79e *** a/src/backend/optimizer/util/pathnode.c --- b/src/backend/optimizer/util/pathnode.c *************** set_cheapest(RelOptInfo *parent_rel) *** 409,416 **** * Returns nothing, but modifies parent_rel->pathlist. */ void ! add_path(RelOptInfo *parent_rel, Path *new_path) { bool accept_new = true; /* unless we find a superior old path */ ListCell *insert_after = NULL; /* where to insert new item */ List *new_path_pathkeys; --- 409,417 ---- * Returns nothing, but modifies parent_rel->pathlist. */ void ! add_path(RelOptInfo *parent_rel, Path *new_path, bool grouped) { + List *pathlist; bool accept_new = true; /* unless we find a superior old path */ ListCell *insert_after = NULL; /* where to insert new item */ List *new_path_pathkeys; *************** add_path(RelOptInfo *parent_rel, Path *n *** 427,432 **** --- 428,435 ---- /* Pretend parameterized paths have no pathkeys, per comment above */ new_path_pathkeys = new_path->param_info ? NIL : new_path->pathkeys; + pathlist = !grouped ? parent_rel->pathlist : parent_rel->grouped_pathlist; + /* * Loop to check proposed new path against old paths. Note it is possible * for more than one old path to be tossed out because new_path dominates *************** add_path(RelOptInfo *parent_rel, Path *n *** 436,442 **** * list cell. */ p1_prev = NULL; ! for (p1 = list_head(parent_rel->pathlist); p1 != NULL; p1 = p1_next) { Path *old_path = (Path *) lfirst(p1); bool remove_old = false; /* unless new proves superior */ --- 439,445 ---- * list cell. */ p1_prev = NULL; ! for (p1 = list_head(pathlist); p1 != NULL; p1 = p1_next) { Path *old_path = (Path *) lfirst(p1); bool remove_old = false; /* unless new proves superior */ *************** add_path(RelOptInfo *parent_rel, Path *n *** 582,589 **** */ if (remove_old) { ! parent_rel->pathlist = list_delete_cell(parent_rel->pathlist, ! p1, p1_prev); /* * Delete the data pointed-to by the deleted cell, if possible --- 585,591 ---- */ if (remove_old) { ! pathlist = list_delete_cell(pathlist, p1, p1_prev); /* * Delete the data pointed-to by the deleted cell, if possible *************** add_path(RelOptInfo *parent_rel, Path *n *** 614,622 **** { /* Accept the new path: insert it at proper place in pathlist */ if (insert_after) ! lappend_cell(parent_rel->pathlist, insert_after, new_path); else ! parent_rel->pathlist = lcons(new_path, parent_rel->pathlist); } else { --- 616,624 ---- { /* Accept the new path: insert it at proper place in pathlist */ if (insert_after) ! lappend_cell(pathlist, insert_after, new_path); else ! pathlist = lcons(new_path, pathlist); } else { *************** add_path(RelOptInfo *parent_rel, Path *n *** 624,629 **** --- 626,636 ---- if (!IsA(new_path, IndexPath)) pfree(new_path); } + + if (!grouped) + parent_rel->pathlist = pathlist; + else + parent_rel->grouped_pathlist = pathlist; } /* *************** add_path(RelOptInfo *parent_rel, Path *n *** 646,653 **** bool add_path_precheck(RelOptInfo *parent_rel, Cost startup_cost, Cost total_cost, ! List *pathkeys, Relids required_outer) { List *new_path_pathkeys; bool consider_startup; ListCell *p1; --- 653,661 ---- bool add_path_precheck(RelOptInfo *parent_rel, Cost startup_cost, Cost total_cost, ! List *pathkeys, Relids required_outer, bool grouped) { + List *pathlist; List *new_path_pathkeys; bool consider_startup; ListCell *p1; *************** add_path_precheck(RelOptInfo *parent_rel *** 658,664 **** /* Decide whether new path's startup cost is interesting */ consider_startup = required_outer ? parent_rel->consider_param_startup : parent_rel->consider_startup; ! foreach(p1, parent_rel->pathlist) { Path *old_path = (Path *) lfirst(p1); PathKeysComparison keyscmp; --- 666,674 ---- /* Decide whether new path's startup cost is interesting */ consider_startup = required_outer ? parent_rel->consider_param_startup : parent_rel->consider_startup; ! pathlist = !grouped ? parent_rel->pathlist : parent_rel->grouped_pathlist; ! ! foreach(p1, pathlist) { Path *old_path = (Path *) lfirst(p1); PathKeysComparison keyscmp; *************** add_path_precheck(RelOptInfo *parent_rel *** 750,772 **** * isn't an IndexPath. */ void ! add_partial_path(RelOptInfo *parent_rel, Path *new_path) { bool accept_new = true; /* unless we find a superior old path */ ListCell *insert_after = NULL; /* where to insert new item */ ListCell *p1; ListCell *p1_prev; ListCell *p1_next; /* Check for query cancel. */ CHECK_FOR_INTERRUPTS(); /* * As in add_path, throw out any paths which are dominated by the new * path, but throw out the new path if some existing path dominates it. */ p1_prev = NULL; ! for (p1 = list_head(parent_rel->partial_pathlist); p1 != NULL; p1 = p1_next) { Path *old_path = (Path *) lfirst(p1); --- 760,786 ---- * isn't an IndexPath. */ void ! add_partial_path(RelOptInfo *parent_rel, Path *new_path, bool grouped) { bool accept_new = true; /* unless we find a superior old path */ ListCell *insert_after = NULL; /* where to insert new item */ ListCell *p1; ListCell *p1_prev; ListCell *p1_next; + List *pathlist; /* Check for query cancel. */ CHECK_FOR_INTERRUPTS(); + pathlist = !grouped ? parent_rel->partial_pathlist : + parent_rel->partial_grouped_pathlist; + /* * As in add_path, throw out any paths which are dominated by the new * path, but throw out the new path if some existing path dominates it. */ p1_prev = NULL; ! for (p1 = list_head(pathlist); p1 != NULL; p1 = p1_next) { Path *old_path = (Path *) lfirst(p1); *************** add_partial_path(RelOptInfo *parent_rel, *** 820,831 **** } /* ! * Remove current element from partial_pathlist if dominated by new. */ if (remove_old) { ! parent_rel->partial_pathlist = ! list_delete_cell(parent_rel->partial_pathlist, p1, p1_prev); /* we should not see IndexPaths here, so always safe to delete */ Assert(!IsA(old_path, IndexPath)); pfree(old_path); --- 834,844 ---- } /* ! * Remove current element from pathlist if dominated by new. */ if (remove_old) { ! pathlist = list_delete_cell(pathlist, p1, p1_prev); /* we should not see IndexPaths here, so always safe to delete */ Assert(!IsA(old_path, IndexPath)); pfree(old_path); *************** add_partial_path(RelOptInfo *parent_rel, *** 842,848 **** /* * If we found an old path that dominates new_path, we can quit ! * scanning the partial_pathlist; we will not add new_path, and we * assume new_path cannot dominate any later path. */ if (!accept_new) --- 855,861 ---- /* * If we found an old path that dominates new_path, we can quit ! * scanning the pathlist; we will not add new_path, and we * assume new_path cannot dominate any later path. */ if (!accept_new) *************** add_partial_path(RelOptInfo *parent_rel, *** 853,862 **** { /* Accept the new path: insert it at proper place */ if (insert_after) ! lappend_cell(parent_rel->partial_pathlist, insert_after, new_path); else ! parent_rel->partial_pathlist = ! lcons(new_path, parent_rel->partial_pathlist); } else { --- 866,874 ---- { /* Accept the new path: insert it at proper place */ if (insert_after) ! lappend_cell(pathlist, insert_after, new_path); else ! pathlist = lcons(new_path, pathlist); } else { *************** add_partial_path(RelOptInfo *parent_rel, *** 865,870 **** --- 877,887 ---- /* Reject and recycle the new path */ pfree(new_path); } + + if (!grouped) + parent_rel->partial_pathlist = pathlist; + else + parent_rel->partial_grouped_pathlist = pathlist; } /* *************** add_partial_path(RelOptInfo *parent_rel, *** 879,887 **** */ bool add_partial_path_precheck(RelOptInfo *parent_rel, Cost total_cost, ! List *pathkeys) { ListCell *p1; /* * Our goal here is twofold. First, we want to find out whether this path --- 896,906 ---- */ bool add_partial_path_precheck(RelOptInfo *parent_rel, Cost total_cost, ! List *pathkeys, bool grouped) { ListCell *p1; + List *pathlist = !grouped ? parent_rel->partial_pathlist : + parent_rel->partial_grouped_pathlist; /* * Our goal here is twofold. First, we want to find out whether this path *************** add_partial_path_precheck(RelOptInfo *pa *** 891,900 **** * final cost computations. If so, we definitely want to consider it. * * Unlike add_path(), we always compare pathkeys here. This is because we ! * expect partial_pathlist to be very short, and getting a definitive ! * answer at this stage avoids the need to call add_path_precheck. */ ! foreach(p1, parent_rel->partial_pathlist) { Path *old_path = (Path *) lfirst(p1); PathKeysComparison keyscmp; --- 910,920 ---- * final cost computations. If so, we definitely want to consider it. * * Unlike add_path(), we always compare pathkeys here. This is because we ! * expect partial_pathlist / grouped_pathlist to be very short, and ! * getting a definitive answer at this stage avoids the need to call ! * add_path_precheck. */ ! foreach(p1, pathlist) { Path *old_path = (Path *) lfirst(p1); PathKeysComparison keyscmp; *************** add_partial_path_precheck(RelOptInfo *pa *** 923,929 **** * completion. */ if (!add_path_precheck(parent_rel, total_cost, total_cost, pathkeys, ! NULL)) return false; return true; --- 943,949 ---- * completion. */ if (!add_path_precheck(parent_rel, total_cost, total_cost, pathkeys, ! NULL, false)) return false; return true; *************** create_hashjoin_path(PlannerInfo *root, *** 2108,2120 **** Path *inner_path, List *restrict_clauses, Relids required_outer, ! List *hashclauses) { HashPath *pathnode = makeNode(HashPath); pathnode->jpath.path.pathtype = T_HashJoin; pathnode->jpath.path.parent = joinrel; ! pathnode->jpath.path.pathtarget = joinrel->reltarget; pathnode->jpath.path.param_info = get_joinrel_parampathinfo(root, joinrel, --- 2128,2142 ---- Path *inner_path, List *restrict_clauses, Relids required_outer, ! List *hashclauses, ! bool grouped) { HashPath *pathnode = makeNode(HashPath); pathnode->jpath.path.pathtype = T_HashJoin; pathnode->jpath.path.parent = joinrel; ! pathnode->jpath.path.pathtarget = !grouped ? ! joinrel->reltarget : joinrel->reltarget_grouped; pathnode->jpath.path.param_info = get_joinrel_parampathinfo(root, joinrel, diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c new file mode 100644 index 7a8674d..126120f *** a/src/backend/optimizer/util/relnode.c --- b/src/backend/optimizer/util/relnode.c *************** typedef struct JoinHashEntry *** 33,39 **** } JoinHashEntry; static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, ! RelOptInfo *input_rel); static List *build_joinrel_restrictlist(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outer_rel, --- 33,39 ---- } JoinHashEntry; static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, ! RelOptInfo *input_rel, bool grouped); static List *build_joinrel_restrictlist(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outer_rel, *************** build_simple_rel(PlannerInfo *root, int *** 106,114 **** --- 106,117 ---- rel->consider_param_startup = false; /* might get changed later */ rel->consider_parallel = false; /* might get changed later */ rel->reltarget = create_empty_pathtarget(); + /* Not all relations have this target. */ + rel->reltarget_grouped = NULL; rel->pathlist = NIL; rel->ppilist = NIL; rel->partial_pathlist = NIL; + rel->partial_grouped_pathlist = NIL; rel->cheapest_startup_path = NULL; rel->cheapest_total_path = NULL; rel->cheapest_unique_path = NULL; *************** build_join_rel(PlannerInfo *root, *** 371,379 **** --- 374,385 ---- joinrel->consider_param_startup = false; joinrel->consider_parallel = false; joinrel->reltarget = create_empty_pathtarget(); + /* Not all joins have this target. */ + joinrel->reltarget_grouped = NULL; joinrel->pathlist = NIL; joinrel->ppilist = NIL; joinrel->partial_pathlist = NIL; + joinrel->partial_grouped_pathlist = NIL; joinrel->cheapest_startup_path = NULL; joinrel->cheapest_total_path = NULL; joinrel->cheapest_unique_path = NULL; *************** build_join_rel(PlannerInfo *root, *** 459,468 **** * and inner rels we first try to build it from. But the contents should * be the same regardless. */ ! build_joinrel_tlist(root, joinrel, outer_rel); ! build_joinrel_tlist(root, joinrel, inner_rel); add_placeholders_to_joinrel(root, joinrel, outer_rel, inner_rel); /* * add_placeholders_to_joinrel also took care of adding the ph_lateral * sets of any PlaceHolderVars computed here to direct_lateral_relids, so --- 465,488 ---- * and inner rels we first try to build it from. But the contents should * be the same regardless. */ ! build_joinrel_tlist(root, joinrel, outer_rel, false); ! build_joinrel_tlist(root, joinrel, inner_rel, false); add_placeholders_to_joinrel(root, joinrel, outer_rel, inner_rel); + /* Build reltarget_grouped if appropriate. */ + if (outer_rel->reltarget_grouped && inner_rel->reltarget_grouped && + root->all_baserels_grouped) + { + build_joinrel_tlist(root, joinrel, outer_rel, true); + build_joinrel_tlist(root, joinrel, inner_rel, true); + + /* + * No need to add PHVs - if there were some, it'd mean that the join + * is below nullable side of outer join, in which case no + * pre-aggregation should take place. + */ + } + /* * add_placeholders_to_joinrel also took care of adding the ph_lateral * sets of any PlaceHolderVars computed here to direct_lateral_relids, so *************** min_join_parameterization(PlannerInfo *r *** 607,622 **** */ static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, ! RelOptInfo *input_rel) { Relids relids = joinrel->relids; ListCell *vars; ! foreach(vars, input_rel->reltarget->exprs) { Var *var = (Var *) lfirst(vars); RelOptInfo *baserel; int ndx; /* * Ignore PlaceHolderVars in the input tlists; we'll make our own --- 627,665 ---- */ static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, ! RelOptInfo *input_rel, bool grouped) { Relids relids = joinrel->relids; + PathTarget *reltarget_input, *reltarget_result; ListCell *vars; + int i = -1; ! if (!grouped) ! { ! reltarget_input = input_rel->reltarget; ! reltarget_result = joinrel->reltarget; ! } ! else ! { ! reltarget_input = input_rel->reltarget_grouped; ! /* Shouldn't be called otherwise. */ ! Assert(reltarget_input != NULL); ! ! /* Called first time for this joinrel? */ ! if (joinrel->reltarget_grouped == NULL) ! joinrel->reltarget_grouped = create_empty_pathtarget(); ! ! reltarget_result = joinrel->reltarget_grouped; ! } ! ! foreach(vars, reltarget_input->exprs) { Var *var = (Var *) lfirst(vars); RelOptInfo *baserel; int ndx; + Index sortgroupref = 0; + + i++; /* * Ignore PlaceHolderVars in the input tlists; we'll make our own *************** build_joinrel_tlist(PlannerInfo *root, R *** 642,650 **** if (bms_nonempty_difference(baserel->attr_needed[ndx], relids)) { /* Yup, add it to the output */ ! joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, var); /* Vars have cost zero, so no need to adjust reltarget->cost */ ! joinrel->reltarget->width += baserel->attr_widths[ndx]; } } } --- 685,697 ---- if (bms_nonempty_difference(baserel->attr_needed[ndx], relids)) { /* Yup, add it to the output */ ! if (reltarget_input->sortgrouprefs) ! sortgroupref = reltarget_input->sortgrouprefs[i]; ! add_column_to_pathtarget(reltarget_result, (Expr *) var, ! sortgroupref); ! /* Vars have cost zero, so no need to adjust reltarget->cost */ ! reltarget_result->width += baserel->attr_widths[ndx]; } } } diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c new file mode 100644 index 45205a8..423a39e *** a/src/backend/optimizer/util/tlist.c --- b/src/backend/optimizer/util/tlist.c *************** apply_pathtarget_labeling_to_tlist(List *** 759,761 **** --- 759,896 ---- i++; } } + + /* + * Replace each "grouped var" in the source target with its "intermediate + * expression" if one exists, i.e. apply the effect of joins to the result of + * partial aggregation emitted by base relation. + */ + PathTarget * + create_intermediate_grouping_target(PlannerInfo *root, PathTarget *src) + { + PathTarget *result = create_empty_pathtarget(); + int i = 0; + ListCell *l; + + foreach(l, src->exprs) + { + Expr *expr, *expr_new; + Index sortgroupref = 0; + + if (src->sortgrouprefs) + sortgroupref = src->sortgrouprefs[i]; + + expr_new = expr = (Expr *) lfirst(l); + if (IsA(expr, Var)) + { + Var *var = (Var *) expr; + ListCell *lc; + + foreach(lc, root->grouped_var_list) + { + GroupedVarInfo *gvi = (GroupedVarInfo *) lfirst(lc); + + if (gvi->expr_intermediate && + gvi->var->varno == var->varno && + gvi->var->varattno == var->varattno) + { + /* XXX Need a copy? */ + expr_new = gvi->expr_intermediate; + break; + } + } + } + add_column_to_pathtarget(result, expr_new, sortgroupref); + + i++; + } + + return result; + } + + /* + * Replace each "grouped var" in the source targetlist with the original + * expression. This includes the items already replaced by "intermediate + * expression", see create_intermediate_grouping_target. + * + * TODO Think of more suitable name. Although "grouped var" may substitute for + * grouping expressions in the future, currently Aggref is the only outcome of + * the replacement. undo_grouped_var_substitutions? + */ + List * + restore_grouping_expressions(PlannerInfo *root, List *src) + { + List *result = NIL; + ListCell *l; + + foreach(l, src) + { + TargetEntry *te, *te_new; + Expr *expr_new = NULL; + + te = (TargetEntry *) lfirst(l); + + if (IsA(te->expr, Var)) + { + Var *var = (Var *) te->expr; + + expr_new = find_grouped_var_expr(root, var); + } + else + { + /* Is this the "intermediate expression"? */ + ListCell *lc; + + foreach(lc, root->grouped_var_list) + { + GroupedVarInfo *gvi = (GroupedVarInfo *) lfirst(lc); + + /* + * If expr_intermediate was valid, the var should already have + * been replaced by the "intermediate expression" (which + * should be non-Var). + */ + if (gvi->expr_intermediate != NULL && + equal(te->expr, gvi->expr_intermediate)) + { + /* XXX Need a copy? */ + expr_new = gvi->expr; + break; + } + } + } + if (expr_new != NULL) + { + te_new = flatCopyTargetEntry(te); + te_new->expr = expr_new; + } + else + te_new = te; + result = lappend(result, te_new); + } + + return result; + } + + /* + * Find the expression that we've replaced with grouped var earlier. + */ + Expr * + find_grouped_var_expr(PlannerInfo *root, Var *var) + { + ListCell *lc; + + foreach(lc, root->grouped_var_list) + { + GroupedVarInfo *gvi = (GroupedVarInfo *) lfirst(lc); + + if (gvi->var->varno == var->varno && + gvi->var->varattno == var->varattno) + { + /* XXX Need a copy? */ + return gvi->expr; + } + } + + return NULL; + } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c new file mode 100644 index 1297960..ed521bb *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** ParseFuncOrColumn(ParseState *pstate, Li *** 95,100 **** --- 95,101 ---- FuncDetailCode fdresult; char aggkind = 0; ParseCallbackState pcbstate; + Oid aggtransmultifn = InvalidOid; /* * If there's an aggregate filter, transform it using transformWhereClause *************** ParseFuncOrColumn(ParseState *pstate, Li *** 318,323 **** --- 319,325 ---- classForm = (Form_pg_aggregate) GETSTRUCT(tup); aggkind = classForm->aggkind; catDirectArgs = classForm->aggnumdirectargs; + aggtransmultifn = classForm->aggtransmultifn; ReleaseSysCache(tup); /* Now check various disallowed cases. */ *************** ParseFuncOrColumn(ParseState *pstate, Li *** 662,667 **** --- 664,670 ---- aggref->aggstar = agg_star; aggref->aggvariadic = func_variadic; aggref->aggkind = aggkind; + aggref->aggtransmultifn = aggtransmultifn; /* agglevelsup will be set by transformAggregateCall */ aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */ aggref->location = location; diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c new file mode 100644 index 86b46de..be7ef22 *** a/src/backend/utils/adt/float.c --- b/src/backend/utils/adt/float.c *************** float4_accum(PG_FUNCTION_ARGS) *** 2630,2635 **** --- 2630,2663 ---- } } + /* + * State multiplication function for sum(float4) aggregate. + */ + Datum + float4_sum_mul(PG_FUNCTION_ARGS) + { + float4 state = PG_GETARG_FLOAT4(0); + int64 factor; + + #ifndef USE_FLOAT8_BYVAL /* controls int8 too */ + { + int64 *factor_p = (int64 *) PG_GETARG_POINTER(1); + + factor = *factor_p; + } + #else + factor = PG_GETARG_INT64(1); + #endif + + /* + * Sum of a single group is added each time the same input set is + * aggregated again. + */ + state *= (float8) factor; + + PG_RETURN_FLOAT4(state); + } + Datum float8_avg(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c new file mode 100644 index 301dffa..063adbb *** a/src/backend/utils/adt/selfuncs.c --- b/src/backend/utils/adt/selfuncs.c *************** *** 111,116 **** --- 111,117 ---- #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "executor/executor.h" + #include "executor/nodeAgg.h" #include "mb/pg_wchar.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" *************** estimate_hash_bucketsize(PlannerInfo *ro *** 3657,3662 **** --- 3658,3691 ---- return (Selectivity) estfract; } + /* + * estimate_hashagg_tablesize + * estimate the number of bytes that a hash aggregate hashtable will + * require based on the agg_costs, path width and dNumGroups. + */ + Size + estimate_hashagg_tablesize(Path *path, const AggClauseCosts *agg_costs, + double dNumGroups) + { + Size hashentrysize; + + /* Estimate per-hash-entry space at tuple width... */ + hashentrysize = MAXALIGN(path->pathtarget->width) + + MAXALIGN(SizeofMinimalTupleHeader); + + /* plus space for pass-by-ref transition values... */ + hashentrysize += agg_costs->transitionSpace; + /* plus the per-hash-entry overhead */ + hashentrysize += hash_agg_entry_size(agg_costs->numAggs); + + /* + * Note that this disregards the effect of fill-factor and growth policy + * of the hash-table. That's probably ok, given default the default + * fill-factor is relatively high. It'd be hard to meaningfully factor in + * "double-in-size" growth policies here. + */ + return hashentrysize * dNumGroups; + } /*------------------------------------------------------------------------- * diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h new file mode 100644 index 1ffde6c..f0b5498 *** a/src/include/catalog/pg_aggregate.h --- b/src/include/catalog/pg_aggregate.h *************** *** 32,37 **** --- 32,38 ---- * aggkind aggregate kind, see AGGKIND_ categories below * aggnumdirectargs number of arguments that are "direct" arguments * aggtransfn transition function + * aggtransmultifn transition state multiplication function * aggfinalfn final function (0 if none) * aggcombinefn combine function (0 if none) * aggserialfn function to convert transtype to bytea (0 if none) *************** CATALOG(pg_aggregate,2600) BKI_WITHOUT_O *** 58,63 **** --- 59,65 ---- char aggkind; int16 aggnumdirectargs; regproc aggtransfn; + regproc aggtransmultifn; regproc aggfinalfn; regproc aggcombinefn; regproc aggserialfn; *************** typedef FormData_pg_aggregate *Form_pg_a *** 91,117 **** * ---------------- */ ! #define Natts_pg_aggregate 20 #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_aggcombinefn 6 ! #define Anum_pg_aggregate_aggserialfn 7 ! #define Anum_pg_aggregate_aggdeserialfn 8 ! #define Anum_pg_aggregate_aggmtransfn 9 ! #define Anum_pg_aggregate_aggminvtransfn 10 ! #define Anum_pg_aggregate_aggmfinalfn 11 ! #define Anum_pg_aggregate_aggfinalextra 12 ! #define Anum_pg_aggregate_aggmfinalextra 13 ! #define Anum_pg_aggregate_aggsortop 14 ! #define Anum_pg_aggregate_aggtranstype 15 ! #define Anum_pg_aggregate_aggtransspace 16 ! #define Anum_pg_aggregate_aggmtranstype 17 ! #define Anum_pg_aggregate_aggmtransspace 18 ! #define Anum_pg_aggregate_agginitval 19 ! #define Anum_pg_aggregate_aggminitval 20 /* * Symbolic values for aggkind column. We distinguish normal aggregates --- 93,120 ---- * ---------------- */ ! #define Natts_pg_aggregate 21 #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_aggtransmultifn 5 ! #define Anum_pg_aggregate_aggfinalfn 6 ! #define Anum_pg_aggregate_aggcombinefn 7 ! #define Anum_pg_aggregate_aggserialfn 8 ! #define Anum_pg_aggregate_aggdeserialfn 9 ! #define Anum_pg_aggregate_aggmtransfn 10 ! #define Anum_pg_aggregate_aggminvtransfn 11 ! #define Anum_pg_aggregate_aggmfinalfn 12 ! #define Anum_pg_aggregate_aggfinalextra 13 ! #define Anum_pg_aggregate_aggmfinalextra 14 ! #define Anum_pg_aggregate_aggsortop 15 ! #define Anum_pg_aggregate_aggtranstype 16 ! #define Anum_pg_aggregate_aggtransspace 17 ! #define Anum_pg_aggregate_aggmtranstype 18 ! #define Anum_pg_aggregate_aggmtransspace 19 ! #define Anum_pg_aggregate_agginitval 20 ! #define Anum_pg_aggregate_aggminitval 21 /* * Symbolic values for aggkind column. We distinguish normal aggregates *************** typedef FormData_pg_aggregate *Form_pg_a *** 135,318 **** */ /* avg */ ! DATA(insert ( 2100 n 0 int8_avg_accum numeric_poly_avg int8_avg_combine int8_avg_serialize int8_avg_deserialize 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_combine - - 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 int4_avg_combine - - 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_combine numeric_avg_serialize numeric_avg_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2105 n 0 float8_accum float8_avg float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2106 n 0 interval_accum interval_avg interval_combine - - 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_combine int8_avg_serialize int8_avg_deserialize 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_combine numeric_avg_serialize numeric_avg_deserialize 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_ )); ! 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 - 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 - 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 numeric_combine numeric_serialize numeric_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2722 n 0 float8_accum float8_var_pop float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2723 n 0 numeric_accum numeric_var_pop numeric_combine numeric_serialize numeric_deserialize 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 numeric_combine numeric_serialize numeric_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2645 n 0 float8_accum float8_var_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2646 n 0 numeric_accum numeric_var_samp numeric_combine numeric_serialize numeric_deserialize 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 numeric_combine numeric_serialize numeric_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2152 n 0 float8_accum float8_var_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2153 n 0 numeric_accum numeric_var_samp numeric_combine numeric_serialize numeric_deserialize 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 numeric_combine numeric_serialize numeric_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2728 n 0 float8_accum float8_stddev_pop float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2729 n 0 numeric_accum numeric_stddev_pop numeric_combine numeric_serialize numeric_deserialize 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 numeric_combine numeric_serialize numeric_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2716 n 0 float8_accum float8_stddev_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2717 n 0 numeric_accum numeric_stddev_samp numeric_combine numeric_serialize numeric_deserialize 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 numeric_combine numeric_serialize numeric_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2158 n 0 float8_accum float8_stddev_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2159 n 0 numeric_accum numeric_stddev_samp numeric_combine numeric_serialize numeric_deserialize 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 - int8pl - - - - - f f 0 20 0 0 0 "0" _null_ )); ! DATA(insert ( 2819 n 0 float8_regr_accum float8_regr_sxx float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - 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 float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2829 n 0 float8_regr_accum float8_corr float8_regr_combine - - - - - 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 - 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 - 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 - 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 - 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_ )); /* 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_ )); /* text */ ! 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_ )); /* 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_ )); /* 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_ )); /* 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_ )); /* --- 138,322 ---- */ /* avg */ ! DATA(insert ( 2100 n 0 int8_avg_accum 0 numeric_poly_avg int8_avg_combine int8_avg_serialize int8_avg_deserialize 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 0 int8_avg int4_avg_combine - - 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 0 int8_avg int4_avg_combine - - 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 0 numeric_avg numeric_avg_combine numeric_avg_serialize numeric_avg_deserialize numeric_avg_accum numeric_accum_inv numeric_avg f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2104 n 0 float4_accum 0 float8_avg float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2105 n 0 float8_accum 0 float8_avg float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2106 n 0 interval_accum 0 interval_avg interval_combine - - 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 0 numeric_poly_sum int8_avg_combine int8_avg_serialize int8_avg_deserialize 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 0 - 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 0 - 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 4002 - float4pl - - - - - f f 0 700 0 0 0 _null_ _null_ )); ! DATA(insert ( 2111 n 0 float8pl 0 - float8pl - - - - - f f 0 701 0 0 0 _null_ _null_ )); ! DATA(insert ( 2112 n 0 cash_pl 0 - cash_pl - - cash_pl cash_mi - f f 0 790 0 790 0 _null_ _null_ )); ! DATA(insert ( 2113 n 0 interval_pl 0 - interval_pl - - interval_pl interval_mi - f f 0 1186 0 1186 0 _null_ _null_ )); ! DATA(insert ( 2114 n 0 numeric_avg_accum 0 numeric_sum numeric_avg_combine numeric_avg_serialize numeric_avg_deserialize numeric_avg_accum numeric_accum_inv numeric_sum f f 0 2281 128 2281 128 _null_ _null_ )); /* max */ ! DATA(insert ( 2115 n 0 int8larger 0 - int8larger - - - - - f f 413 20 0 0 0 _null_ _null_ )); ! DATA(insert ( 2116 n 0 int4larger 0 - int4larger - - - - - f f 521 23 0 0 0 _null_ _null_ )); ! DATA(insert ( 2117 n 0 int2larger 0 - int2larger - - - - - f f 520 21 0 0 0 _null_ _null_ )); ! DATA(insert ( 2118 n 0 oidlarger 0 - oidlarger - - - - - f f 610 26 0 0 0 _null_ _null_ )); ! DATA(insert ( 2119 n 0 float4larger 0 - float4larger - - - - - f f 623 700 0 0 0 _null_ _null_ )); ! DATA(insert ( 2120 n 0 float8larger 0 - float8larger - - - - - f f 674 701 0 0 0 _null_ _null_ )); ! DATA(insert ( 2121 n 0 int4larger 0 - int4larger - - - - - f f 563 702 0 0 0 _null_ _null_ )); ! DATA(insert ( 2122 n 0 date_larger 0 - date_larger - - - - - f f 1097 1082 0 0 0 _null_ _null_ )); ! DATA(insert ( 2123 n 0 time_larger 0 - time_larger - - - - - f f 1112 1083 0 0 0 _null_ _null_ )); ! DATA(insert ( 2124 n 0 timetz_larger 0 - timetz_larger - - - - - f f 1554 1266 0 0 0 _null_ _null_ )); ! DATA(insert ( 2125 n 0 cashlarger 0 - cashlarger - - - - - f f 903 790 0 0 0 _null_ _null_ )); ! DATA(insert ( 2126 n 0 timestamp_larger 0 - timestamp_larger - - - - - f f 2064 1114 0 0 0 _null_ _null_ )); ! DATA(insert ( 2127 n 0 timestamptz_larger 0 - timestamptz_larger - - - - - f f 1324 1184 0 0 0 _null_ _null_ )); ! DATA(insert ( 2128 n 0 interval_larger 0 - interval_larger - - - - - f f 1334 1186 0 0 0 _null_ _null_ )); ! DATA(insert ( 2129 n 0 text_larger 0 - text_larger - - - - - f f 666 25 0 0 0 _null_ _null_ )); ! DATA(insert ( 2130 n 0 numeric_larger 0 - numeric_larger - - - - - f f 1756 1700 0 0 0 _null_ _null_ )); ! DATA(insert ( 2050 n 0 array_larger 0 - array_larger - - - - - f f 1073 2277 0 0 0 _null_ _null_ )); ! DATA(insert ( 2244 n 0 bpchar_larger 0 - bpchar_larger - - - - - f f 1060 1042 0 0 0 _null_ _null_ )); ! DATA(insert ( 2797 n 0 tidlarger 0 - tidlarger - - - - - f f 2800 27 0 0 0 _null_ _null_ )); ! DATA(insert ( 3526 n 0 enum_larger 0 - enum_larger - - - - - f f 3519 3500 0 0 0 _null_ _null_ )); ! DATA(insert ( 3564 n 0 network_larger 0 - network_larger - - - - - f f 1205 869 0 0 0 _null_ _null_ )); /* min */ ! DATA(insert ( 2131 n 0 int8smaller 0 - int8smaller - - - - - f f 412 20 0 0 0 _null_ _null_ )); ! DATA(insert ( 2132 n 0 int4smaller 0 - int4smaller - - - - - f f 97 23 0 0 0 _null_ _null_ )); ! DATA(insert ( 2133 n 0 int2smaller 0 - int2smaller - - - - - f f 95 21 0 0 0 _null_ _null_ )); ! DATA(insert ( 2134 n 0 oidsmaller 0 - oidsmaller - - - - - f f 609 26 0 0 0 _null_ _null_ )); ! DATA(insert ( 2135 n 0 float4smaller 0 - float4smaller - - - - - f f 622 700 0 0 0 _null_ _null_ )); ! DATA(insert ( 2136 n 0 float8smaller 0 - float8smaller - - - - - f f 672 701 0 0 0 _null_ _null_ )); ! DATA(insert ( 2137 n 0 int4smaller 0 - int4smaller - - - - - f f 562 702 0 0 0 _null_ _null_ )); ! DATA(insert ( 2138 n 0 date_smaller 0 - date_smaller - - - - - f f 1095 1082 0 0 0 _null_ _null_ )); ! DATA(insert ( 2139 n 0 time_smaller 0 - time_smaller - - - - - f f 1110 1083 0 0 0 _null_ _null_ )); ! DATA(insert ( 2140 n 0 timetz_smaller 0 - timetz_smaller - - - - - f f 1552 1266 0 0 0 _null_ _null_ )); ! DATA(insert ( 2141 n 0 cashsmaller 0 - cashsmaller - - - - - f f 902 790 0 0 0 _null_ _null_ )); ! DATA(insert ( 2142 n 0 timestamp_smaller 0 - timestamp_smaller - - - - - f f 2062 1114 0 0 0 _null_ _null_ )); ! DATA(insert ( 2143 n 0 timestamptz_smaller 0 - timestamptz_smaller - - - - - f f 1322 1184 0 0 0 _null_ _null_ )); ! DATA(insert ( 2144 n 0 interval_smaller 0 - interval_smaller - - - - - f f 1332 1186 0 0 0 _null_ _null_ )); ! DATA(insert ( 2145 n 0 text_smaller 0 - text_smaller - - - - - f f 664 25 0 0 0 _null_ _null_ )); ! DATA(insert ( 2146 n 0 numeric_smaller 0 - numeric_smaller - - - - - f f 1754 1700 0 0 0 _null_ _null_ )); ! DATA(insert ( 2051 n 0 array_smaller 0 - array_smaller - - - - - f f 1072 2277 0 0 0 _null_ _null_ )); ! DATA(insert ( 2245 n 0 bpchar_smaller 0 - bpchar_smaller - - - - - f f 1058 1042 0 0 0 _null_ _null_ )); ! DATA(insert ( 2798 n 0 tidsmaller 0 - tidsmaller - - - - - f f 2799 27 0 0 0 _null_ _null_ )); ! DATA(insert ( 3527 n 0 enum_smaller 0 - enum_smaller - - - - - f f 3518 3500 0 0 0 _null_ _null_ )); ! DATA(insert ( 3565 n 0 network_smaller 0 - network_smaller - - - - - f f 1203 869 0 0 0 _null_ _null_ )); /* count */ ! DATA(insert ( 2147 n 0 int8inc_any 0 - int8pl - - int8inc_any int8dec_any - f f 0 20 0 20 0 "0" "0" )); ! DATA(insert ( 2803 n 0 int8inc 0 - int8pl - - int8inc int8dec - f f 0 20 0 20 0 "0" "0" )); ! #define COUNTFNOID 2803 /* var_pop */ ! DATA(insert ( 2718 n 0 int8_accum 0 numeric_var_pop numeric_combine numeric_serialize numeric_deserialize int8_accum int8_accum_inv numeric_var_pop f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2719 n 0 int4_accum 0 numeric_poly_var_pop numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 numeric_poly_var_pop numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 float8_var_pop float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2722 n 0 float8_accum 0 float8_var_pop float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2723 n 0 numeric_accum 0 numeric_var_pop numeric_combine numeric_serialize numeric_deserialize 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 0 numeric_var_samp numeric_combine numeric_serialize numeric_deserialize int8_accum int8_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2642 n 0 int4_accum 0 numeric_poly_var_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 numeric_poly_var_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 float8_var_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2645 n 0 float8_accum 0 float8_var_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2646 n 0 numeric_accum 0 numeric_var_samp numeric_combine numeric_serialize numeric_deserialize 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 0 numeric_var_samp numeric_combine numeric_serialize numeric_deserialize int8_accum int8_accum_inv numeric_var_samp f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2149 n 0 int4_accum 0 numeric_poly_var_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 numeric_poly_var_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 float8_var_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2152 n 0 float8_accum 0 float8_var_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2153 n 0 numeric_accum 0 numeric_var_samp numeric_combine numeric_serialize numeric_deserialize 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 0 numeric_stddev_pop numeric_combine numeric_serialize numeric_deserialize int8_accum int8_accum_inv numeric_stddev_pop f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2725 n 0 int4_accum 0 numeric_poly_stddev_pop numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 numeric_poly_stddev_pop numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 float8_stddev_pop float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2728 n 0 float8_accum 0 float8_stddev_pop float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2729 n 0 numeric_accum 0 numeric_stddev_pop numeric_combine numeric_serialize numeric_deserialize 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 0 numeric_stddev_samp numeric_combine numeric_serialize numeric_deserialize int8_accum int8_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2713 n 0 int4_accum 0 numeric_poly_stddev_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 numeric_poly_stddev_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 float8_stddev_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2716 n 0 float8_accum 0 float8_stddev_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2717 n 0 numeric_accum 0 numeric_stddev_samp numeric_combine numeric_serialize numeric_deserialize 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 0 numeric_stddev_samp numeric_combine numeric_serialize numeric_deserialize int8_accum int8_accum_inv numeric_stddev_samp f f 0 2281 128 2281 128 _null_ _null_ )); ! DATA(insert ( 2155 n 0 int4_accum 0 numeric_poly_stddev_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 numeric_poly_stddev_samp numeric_poly_combine numeric_poly_serialize numeric_poly_deserialize 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 0 float8_stddev_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2158 n 0 float8_accum 0 float8_stddev_samp float8_combine - - - - - f f 0 1022 0 0 0 "{0,0,0}" _null_ )); ! DATA(insert ( 2159 n 0 numeric_accum 0 numeric_stddev_samp numeric_combine numeric_serialize numeric_deserialize 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 0 - int8pl - - - - - f f 0 20 0 0 0 "0" _null_ )); ! DATA(insert ( 2819 n 0 float8_regr_accum 0 float8_regr_sxx float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2820 n 0 float8_regr_accum 0 float8_regr_syy float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2821 n 0 float8_regr_accum 0 float8_regr_sxy float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2822 n 0 float8_regr_accum 0 float8_regr_avgx float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2823 n 0 float8_regr_accum 0 float8_regr_avgy float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2824 n 0 float8_regr_accum 0 float8_regr_r2 float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2825 n 0 float8_regr_accum 0 float8_regr_slope float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2826 n 0 float8_regr_accum 0 float8_regr_intercept float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2827 n 0 float8_regr_accum 0 float8_covar_pop float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2828 n 0 float8_regr_accum 0 float8_covar_samp float8_regr_combine - - - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); ! DATA(insert ( 2829 n 0 float8_regr_accum 0 float8_corr float8_regr_combine - - - - - 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 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 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 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 0 - int2and - - - - - f f 0 21 0 0 0 _null_ _null_ )); ! DATA(insert ( 2237 n 0 int2or 0 - int2or - - - - - f f 0 21 0 0 0 _null_ _null_ )); ! DATA(insert ( 2238 n 0 int4and 0 - int4and - - - - - f f 0 23 0 0 0 _null_ _null_ )); ! DATA(insert ( 2239 n 0 int4or 0 - int4or - - - - - f f 0 23 0 0 0 _null_ _null_ )); ! DATA(insert ( 2240 n 0 int8and 0 - int8and - - - - - f f 0 20 0 0 0 _null_ _null_ )); ! DATA(insert ( 2241 n 0 int8or 0 - int8or - - - - - f f 0 20 0 0 0 _null_ _null_ )); ! DATA(insert ( 2242 n 0 bitand 0 - bitand - - - - - f f 0 1560 0 0 0 _null_ _null_ )); ! DATA(insert ( 2243 n 0 bitor 0 - bitor - - - - - f f 0 1560 0 0 0 _null_ _null_ )); /* xml */ ! DATA(insert ( 2901 n 0 xmlconcat2 0 - - - - - - - f f 0 142 0 0 0 _null_ _null_ )); /* array */ ! DATA(insert ( 2335 n 0 array_agg_transfn 0 array_agg_finalfn - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 4053 n 0 array_agg_array_transfn 0 array_agg_array_finalfn - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); /* text */ ! DATA(insert ( 3538 n 0 string_agg_transfn 0 string_agg_finalfn - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* bytea */ ! DATA(insert ( 3545 n 0 bytea_string_agg_transfn 0 bytea_string_agg_finalfn - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* json */ ! DATA(insert ( 3175 n 0 json_agg_transfn 0 json_agg_finalfn - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3197 n 0 json_object_agg_transfn 0 json_object_agg_finalfn - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); /* jsonb */ ! DATA(insert ( 3267 n 0 jsonb_agg_transfn 0 jsonb_agg_finalfn - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3270 n 0 jsonb_object_agg_transfn 0 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 0 percentile_disc_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3974 o 1 ordered_set_transition 0 percentile_cont_float8_final - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3976 o 1 ordered_set_transition 0 percentile_cont_interval_final - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3978 o 1 ordered_set_transition 0 percentile_disc_multi_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3980 o 1 ordered_set_transition 0 percentile_cont_float8_multi_final - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3982 o 1 ordered_set_transition 0 percentile_cont_interval_multi_final - - - - - - f f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3984 o 0 ordered_set_transition 0 mode_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3986 h 1 ordered_set_transition_multi 0 rank_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3988 h 1 ordered_set_transition_multi 0 percent_rank_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3990 h 1 ordered_set_transition_multi 0 cume_dist_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); ! DATA(insert ( 3992 h 1 ordered_set_transition_multi 0 dense_rank_final - - - - - - t f 0 2281 0 0 0 _null_ _null_ )); /* diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h new file mode 100644 index aeb7927..3095380 *** a/src/include/catalog/pg_operator.h --- b/src/include/catalog/pg_operator.h *************** DATA(insert OID = 685 ( "-" PGNSP PG *** 547,552 **** --- 547,553 ---- DESCR("subtract"); DATA(insert OID = 686 ( "*" PGNSP PGUID b f f 20 20 20 686 0 int8mul - - )); DESCR("multiply"); + #define OPERATOR_INT8MUL 686 DATA(insert OID = 687 ( "/" PGNSP PGUID b f f 20 20 20 0 0 int8div - - )); DESCR("divide"); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h new file mode 100644 index 37e022d..51d1b74 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DESCR("pg_controldata recovery state inf *** 5345,5350 **** --- 5345,5353 ---- DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ )); DESCR("pg_controldata init state information as a function"); + DATA(insert OID = 4002 ( float4_sum_mul PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1022 "1022 20" _null_ _null_ _null_ _null_ _null_ float4_sum_mul _null_ _null_ _null_ )); + DESCR("aggregate state multiplication function"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h new file mode 100644 index f72ec24..dd76d9b *** a/src/include/nodes/primnodes.h --- b/src/include/nodes/primnodes.h *************** typedef struct Aggref *** 284,289 **** --- 284,290 ---- bool aggvariadic; /* true if variadic arguments have been * combined into an array last argument */ char aggkind; /* aggregate kind (see pg_aggregate.h) */ + Oid aggtransmultifn;/* pg_aggregate(aggtransmultifn) */ Index agglevelsup; /* > 0 if agg belongs to outer query */ AggSplit aggsplit; /* expected agg-splitting mode of parent Agg */ int location; /* token location, or -1 if unknown */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h new file mode 100644 index e1d31c7..1e8ef3b *** a/src/include/nodes/relation.h --- b/src/include/nodes/relation.h *************** typedef struct PlannerInfo *** 252,257 **** --- 252,262 ---- List *placeholder_list; /* list of PlaceHolderInfos */ + /* TODO Consider hashing. */ + List *grouped_var_list; /* List of GroupedVarInfos. */ + + bool all_baserels_grouped; /* Should grouped rels be joined? */ + List *fkey_list; /* list of ForeignKeyOptInfos */ List *query_pathkeys; /* desired pathkeys for query_planner() */ *************** typedef struct RelOptInfo *** 495,504 **** --- 500,516 ---- /* default result targetlist for Paths scanning this relation */ struct PathTarget *reltarget; /* list of Vars/Exprs, cost, width */ + /* result targetlist if the base relation is grouped */ + struct PathTarget *reltarget_grouped; + /* materialization information */ List *pathlist; /* Path structures */ + List *grouped_pathlist; /* paths with partial aggregation already + * done. */ List *ppilist; /* ParamPathInfos used in pathlist */ List *partial_pathlist; /* partial Paths */ + List *partial_grouped_pathlist; /* partial Paths with partial + * aggregation already done. */ struct Path *cheapest_startup_path; struct Path *cheapest_total_path; struct Path *cheapest_unique_path; *************** typedef struct PlaceHolderInfo *** 1905,1910 **** --- 1917,1951 ---- } PlaceHolderInfo; /* + * Variable of a "grouped relation" represents an aggregate or grouping + * expression that it evaluates. Input values of such expressions are not + * available above this relation, so the values of such expressions are passed + * as variables. Each variable-to-expression association is stored as an item + * of root->grouped_var_list and used by set_upper_references. + */ + typedef struct GroupedVarInfo + { + /* The variable added to base relation instead of aggregate. */ + Var *var; + + /* + * The expression whose value this "grouped var" represents in the + * target list of the (partially) grouped relation. + */ + Expr *expr; + + /* + * If aggtransmultifn function (see Aggref) must be applied before the + * final aggregation, expr_intermediate contains the function call. It'll + * replace the variable in targetlist of the Result node that prepares + * input for the final aggregation. + */ + Expr *expr_intermediate; + + Index sortgroupref; + } GroupedVarInfo; + + /* * This struct describes one potentially index-optimizable MIN/MAX aggregate * function. MinMaxAggPath contains a list of these, and if we accept that * path, the list is stored into root->minmax_aggs for use during setrefs.c. diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h new file mode 100644 index d16f879..54b84c7 *** a/src/include/optimizer/pathnode.h --- b/src/include/optimizer/pathnode.h *************** extern int compare_path_costs(Path *path *** 25,37 **** extern int compare_fractional_path_costs(Path *path1, Path *path2, double fraction); extern void set_cheapest(RelOptInfo *parent_rel); ! extern void add_path(RelOptInfo *parent_rel, Path *new_path); extern bool add_path_precheck(RelOptInfo *parent_rel, Cost startup_cost, Cost total_cost, ! List *pathkeys, Relids required_outer); ! extern void add_partial_path(RelOptInfo *parent_rel, Path *new_path); extern bool add_partial_path_precheck(RelOptInfo *parent_rel, ! Cost total_cost, List *pathkeys); extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer, int parallel_workers); --- 25,39 ---- extern int compare_fractional_path_costs(Path *path1, Path *path2, double fraction); extern void set_cheapest(RelOptInfo *parent_rel); ! extern void add_path(RelOptInfo *parent_rel, Path *new_path, bool grouped); extern bool add_path_precheck(RelOptInfo *parent_rel, Cost startup_cost, Cost total_cost, ! List *pathkeys, Relids required_outer, bool grouped); ! extern void add_partial_path(RelOptInfo *parent_rel, Path *new_path, ! bool grouped); extern bool add_partial_path_precheck(RelOptInfo *parent_rel, ! Cost total_cost, List *pathkeys, ! bool grouped); extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer, int parallel_workers); *************** extern HashPath *create_hashjoin_path(Pl *** 134,140 **** Path *inner_path, List *restrict_clauses, Relids required_outer, ! List *hashclauses); extern ProjectionPath *create_projection_path(PlannerInfo *root, RelOptInfo *rel, --- 136,143 ---- Path *inner_path, List *restrict_clauses, Relids required_outer, ! List *hashclauses, ! bool grouped); extern ProjectionPath *create_projection_path(PlannerInfo *root, RelOptInfo *rel, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h new file mode 100644 index 480f25f..0d29bec *** a/src/include/optimizer/paths.h --- b/src/include/optimizer/paths.h *************** extern void set_dummy_rel_pathlist(RelOp *** 52,58 **** extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels); ! extern void generate_gather_paths(PlannerInfo *root, RelOptInfo *rel); #ifdef OPTIMIZER_DEBUG extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel); --- 52,59 ---- extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels); ! extern void generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, ! bool grouped); #ifdef OPTIMIZER_DEBUG extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h new file mode 100644 index 8468b0c..dd2529b *** a/src/include/optimizer/planmain.h --- b/src/include/optimizer/planmain.h *************** extern void add_base_rels_to_query(Plann *** 75,80 **** --- 75,81 ---- extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist); extern void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed, bool create_new_ph); + extern void build_base_rel_tlists_grouped(PlannerInfo *root); extern void find_lateral_references(PlannerInfo *root); extern void create_lateral_join_info(PlannerInfo *root); extern List *deconstruct_jointree(PlannerInfo *root); diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h new file mode 100644 index f80b31a..cb9d0c0 *** a/src/include/optimizer/tlist.h --- b/src/include/optimizer/tlist.h *************** extern void add_column_to_pathtarget(Pat *** 61,67 **** extern void add_new_column_to_pathtarget(PathTarget *target, Expr *expr); extern void add_new_columns_to_pathtarget(PathTarget *target, List *exprs); extern void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target); ! /* Convenience macro to get a PathTarget with valid cost/width fields */ #define create_pathtarget(root, tlist) \ set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist)) --- 61,70 ---- extern void add_new_column_to_pathtarget(PathTarget *target, Expr *expr); extern void add_new_columns_to_pathtarget(PathTarget *target, List *exprs); extern void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target); ! extern PathTarget *create_intermediate_grouping_target(PlannerInfo *root, ! PathTarget *src); ! extern List *restore_grouping_expressions(PlannerInfo *root, List *src); ! extern Expr *find_grouped_var_expr(PlannerInfo *root, Var *var); /* Convenience macro to get a PathTarget with valid cost/width fields */ #define create_pathtarget(root, tlist) \ set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist)) diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h new file mode 100644 index e1bb344..628b142 *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** extern Datum float84le(PG_FUNCTION_ARGS) *** 475,480 **** --- 475,481 ---- extern Datum float84gt(PG_FUNCTION_ARGS); extern Datum float84ge(PG_FUNCTION_ARGS); extern Datum width_bucket_float8(PG_FUNCTION_ARGS); + extern Datum float4_sum_mul(PG_FUNCTION_ARGS); /* dbsize.c */ extern Datum pg_tablespace_size_oid(PG_FUNCTION_ARGS); diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h new file mode 100644 index b43dae7..39f5dee *** a/src/include/utils/selfuncs.h --- b/src/include/utils/selfuncs.h *************** extern double estimate_num_groups(Planne *** 232,237 **** --- 232,240 ---- extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets); + extern Size estimate_hashagg_tablesize(Path *path, + const AggClauseCosts *agg_costs, + double dNumGroups); extern List *deconstruct_indexquals(IndexPath *path); extern void genericcostestimate(PlannerInfo *root, IndexPath *path,