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,