diff options
| author | Andres Freund | 2015-05-16 01:40:59 +0000 |
|---|---|---|
| committer | Andres Freund | 2015-05-16 01:46:31 +0000 |
| commit | f3d3118532175541a9a96ed78881a3b04a057128 (patch) | |
| tree | d06e7177843c563491f3a132d29fec0f60f69bd3 /src/include | |
| parent | 6e4415c6aa428132dd41c8bf23a0885fca0f2271 (diff) | |
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
Diffstat (limited to 'src/include')
| -rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
| -rw-r--r-- | src/include/commands/explain.h | 2 | ||||
| -rw-r--r-- | src/include/lib/bipartite_match.h | 44 | ||||
| -rw-r--r-- | src/include/nodes/execnodes.h | 35 | ||||
| -rw-r--r-- | src/include/nodes/makefuncs.h | 2 | ||||
| -rw-r--r-- | src/include/nodes/nodes.h | 3 | ||||
| -rw-r--r-- | src/include/nodes/parsenodes.h | 69 | ||||
| -rw-r--r-- | src/include/nodes/pg_list.h | 3 | ||||
| -rw-r--r-- | src/include/nodes/plannodes.h | 2 | ||||
| -rw-r--r-- | src/include/nodes/primnodes.h | 35 | ||||
| -rw-r--r-- | src/include/nodes/relation.h | 3 | ||||
| -rw-r--r-- | src/include/optimizer/planmain.h | 1 | ||||
| -rw-r--r-- | src/include/optimizer/tlist.h | 3 | ||||
| -rw-r--r-- | src/include/parser/kwlist.h | 4 | ||||
| -rw-r--r-- | src/include/parser/parse_agg.h | 5 | ||||
| -rw-r--r-- | src/include/parser/parse_clause.h | 1 | ||||
| -rw-r--r-- | src/include/utils/selfuncs.h | 2 |
17 files changed, 211 insertions, 5 deletions
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index bb8a9b08e8..9a10fadfb5 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201505152 +#define CATALOG_VERSION_NO 201505153 #endif diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index c9f722361a..4df44d0242 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -83,6 +83,8 @@ extern void ExplainSeparatePlans(ExplainState *es); extern void ExplainPropertyList(const char *qlabel, List *data, ExplainState *es); +extern void ExplainPropertyListNested(const char *qlabel, List *data, + ExplainState *es); extern void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es); extern void ExplainPropertyInteger(const char *qlabel, int value, diff --git a/src/include/lib/bipartite_match.h b/src/include/lib/bipartite_match.h new file mode 100644 index 0000000000..c80f9bfdd0 --- /dev/null +++ b/src/include/lib/bipartite_match.h @@ -0,0 +1,44 @@ +/* + * bipartite_match.h + * + * Copyright (c) 2015, PostgreSQL Global Development Group + * + * src/include/lib/bipartite_match.h + */ +#ifndef BIPARTITE_MATCH_H +#define BIPARTITE_MATCH_H + +/* + * Given a bipartite graph consisting of nodes U numbered 1..nU, nodes V + * numbered 1..nV, and an adjacency map of undirected edges in the form + * adjacency[u] = [n, v1, v2, v3, ... vn], we wish to find a "maximum + * cardinality matching", which is defined as follows: a matching is a subset + * of the original edges such that no node has more than one edge, and a + * matching has maximum cardinality if there exists no other matching with a + * greater number of edges. + * + * This matching has various applications in graph theory, but the motivating + * example here is Dilworth's theorem: a partially-ordered set can be divided + * into the minimum number of chains (i.e. subsets X where x1 < x2 < x3 ...) by + * a bipartite graph construction. This gives us a polynomial-time solution to + * the problem of planning a collection of grouping sets with the provably + * minimal number of sort operations. + */ +typedef struct bipartite_match_state +{ + int u_size; /* size of U */ + int v_size; /* size of V */ + int matching; /* number of edges in matching */ + short **adjacency; /* adjacency[u] = [n, v1,v2,v3,...,vn] */ + short *pair_uv; /* pair_uv[u] -> v */ + short *pair_vu; /* pair_vu[v] -> u */ + + float *distance; /* distance[u], float so we can have +inf */ + short *queue; /* queue storage for breadth search */ +} BipartiteMatchState; + +BipartiteMatchState *BipartiteMatch(int u_size, int v_size, short **adjacency); + +void BipartiteMatchFree(BipartiteMatchState *state); + +#endif /* BIPARTITE_MATCH_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 972368019a..0a92cc4efc 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -23,6 +23,7 @@ #include "utils/reltrigger.h" #include "utils/sortsupport.h" #include "utils/tuplestore.h" +#include "utils/tuplesort.h" /* ---------------- @@ -615,6 +616,22 @@ typedef struct AggrefExprState } AggrefExprState; /* ---------------- + * GroupingFuncExprState node + * + * The list of column numbers refers to the input tuples of the Agg node to + * which the GroupingFunc belongs, and may contain 0 for references to columns + * that are only present in grouping sets processed by different Agg nodes (and + * which are therefore always considered "grouping" here). + * ---------------- + */ +typedef struct GroupingFuncExprState +{ + ExprState xprstate; + struct AggState *aggstate; + List *clauses; /* integer list of column numbers */ +} GroupingFuncExprState; + +/* ---------------- * WindowFuncExprState node * ---------------- */ @@ -1796,19 +1813,33 @@ typedef struct GroupState /* these structs are private in nodeAgg.c: */ typedef struct AggStatePerAggData *AggStatePerAgg; typedef struct AggStatePerGroupData *AggStatePerGroup; +typedef struct AggStatePerPhaseData *AggStatePerPhase; typedef struct AggState { ScanState ss; /* its first field is NodeTag */ List *aggs; /* all Aggref nodes in targetlist & quals */ int numaggs; /* length of list (could be zero!) */ - FmgrInfo *eqfunctions; /* per-grouping-field equality fns */ + AggStatePerPhase phase; /* pointer to current phase data */ + int numphases; /* number of phases */ + int current_phase; /* current phase number */ FmgrInfo *hashfunctions; /* per-grouping-field hash fns */ AggStatePerAgg peragg; /* per-Aggref information */ - MemoryContext aggcontext; /* memory context for long-lived data */ + ExprContext **aggcontexts; /* econtexts for long-lived data (per GS) */ ExprContext *tmpcontext; /* econtext for input expressions */ AggStatePerAgg curperagg; /* identifies currently active aggregate */ + bool input_done; /* indicates end of input */ bool agg_done; /* indicates completion of Agg scan */ + int projected_set; /* The last projected grouping set */ + int current_set; /* The current grouping set being evaluated */ + Bitmapset *grouped_cols; /* grouped cols in current projection */ + List *all_grouped_cols; /* list of all grouped cols in DESC order */ + /* These fields are for grouping set phase data */ + int maxsets; /* The max number of sets in any phase */ + AggStatePerPhase phases; /* array of all phases */ + Tuplesortstate *sort_in; /* sorted input to phases > 0 */ + Tuplesortstate *sort_out; /* input is copied here for next phase */ + TupleTableSlot *sort_slot; /* slot for sort results */ /* these fields are used in AGG_PLAIN and AGG_SORTED modes: */ AggStatePerGroup pergroup; /* per-Aggref-per-group working state */ HeapTuple grp_firstTuple; /* copy of first tuple of current group */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 4dff6a0901..01d9fedb54 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -81,4 +81,6 @@ extern DefElem *makeDefElem(char *name, Node *arg); extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg, DefElemAction defaction); +extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int location); + #endif /* MAKEFUNC_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 8b275f6e26..669a0afa09 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -136,6 +136,7 @@ typedef enum NodeTag T_Const, T_Param, T_Aggref, + T_GroupingFunc, T_WindowFunc, T_ArrayRef, T_FuncExpr, @@ -188,6 +189,7 @@ typedef enum NodeTag T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, + T_GroupingFuncExprState, T_WindowFuncExprState, T_ArrayRefExprState, T_FuncExprState, @@ -406,6 +408,7 @@ typedef enum NodeTag T_RangeTblFunction, T_WithCheckOption, T_SortGroupClause, + T_GroupingSet, T_WindowClause, T_PrivGrantee, T_FuncWithArgs, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6723f46f3f..23190e1af0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -138,6 +138,8 @@ typedef struct Query List *groupClause; /* a list of SortGroupClause's */ + List *groupingSets; /* a list of GroupingSet's if present */ + Node *havingQual; /* qualifications applied to groups */ List *windowClause; /* a list of WindowClause's */ @@ -1002,6 +1004,73 @@ typedef struct SortGroupClause } SortGroupClause; /* + * GroupingSet - + * representation of CUBE, ROLLUP and GROUPING SETS clauses + * + * In a Query with grouping sets, the groupClause contains a flat list of + * SortGroupClause nodes for each distinct expression used. The actual + * structure of the GROUP BY clause is given by the groupingSets tree. + * + * In the raw parser output, GroupingSet nodes (of all types except SIMPLE + * which is not used) are potentially mixed in with the expressions in the + * groupClause of the SelectStmt. (An expression can't contain a GroupingSet, + * but a list may mix GroupingSet and expression nodes.) At this stage, the + * content of each node is a list of expressions, some of which may be RowExprs + * which represent sublists rather than actual row constructors, and nested + * GroupingSet nodes where legal in the grammar. The structure directly + * reflects the query syntax. + * + * In parse analysis, the transformed expressions are used to build the tlist + * and groupClause list (of SortGroupClause nodes), and the groupingSets tree + * is eventually reduced to a fixed format: + * + * EMPTY nodes represent (), and obviously have no content + * + * SIMPLE nodes represent a list of one or more expressions to be treated as an + * atom by the enclosing structure; the content is an integer list of + * ressortgroupref values (see SortGroupClause) + * + * CUBE and ROLLUP nodes contain a list of one or more SIMPLE nodes. + * + * SETS nodes contain a list of EMPTY, SIMPLE, CUBE or ROLLUP nodes, but after + * parse analysis they cannot contain more SETS nodes; enough of the syntactic + * transforms of the spec have been applied that we no longer have arbitrarily + * deep nesting (though we still preserve the use of cube/rollup). + * + * Note that if the groupingSets tree contains no SIMPLE nodes (only EMPTY + * nodes at the leaves), then the groupClause will be empty, but this is still + * an aggregation query (similar to using aggs or HAVING without GROUP BY). + * + * As an example, the following clause: + * + * GROUP BY GROUPING SETS ((a,b), CUBE(c,(d,e))) + * + * looks like this after raw parsing: + * + * SETS( RowExpr(a,b) , CUBE( c, RowExpr(d,e) ) ) + * + * and parse analysis converts it to: + * + * SETS( SIMPLE(1,2), CUBE( SIMPLE(3), SIMPLE(4,5) ) ) + */ +typedef enum +{ + GROUPING_SET_EMPTY, + GROUPING_SET_SIMPLE, + GROUPING_SET_ROLLUP, + GROUPING_SET_CUBE, + GROUPING_SET_SETS +} GroupingSetKind; + +typedef struct GroupingSet +{ + NodeTag type; + GroupingSetKind kind; + List *content; + int location; +} GroupingSet; + +/* * WindowClause - * transformed representation of WINDOW and OVER clauses * diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index a17500082b..729456d6e5 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -229,8 +229,9 @@ extern List *list_union_int(const List *list1, const List *list2); extern List *list_union_oid(const List *list1, const List *list2); extern List *list_intersection(const List *list1, const List *list2); +extern List *list_intersection_int(const List *list1, const List *list2); -/* currently, there's no need for list_intersection_int etc */ +/* currently, there's no need for list_intersection_ptr etc */ extern List *list_difference(const List *list1, const List *list2); extern List *list_difference_ptr(const List *list1, const List *list2); diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 4e655b0e6c..51906d6898 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -712,6 +712,8 @@ typedef struct Agg AttrNumber *grpColIdx; /* their indexes in the target list */ Oid *grpOperators; /* equality operators to compare with */ long numGroups; /* estimated number of groups in input */ + List *groupingSets; /* grouping sets to use */ + List *chain; /* chained Agg/Sort nodes */ } Agg; /* ---------------- diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 4a4dd7e9ef..a5467c5379 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -272,6 +272,41 @@ typedef struct Aggref } Aggref; /* + * GroupingFunc + * + * A GroupingFunc is a GROUPING(...) expression, which behaves in many ways + * like an aggregate function (e.g. it "belongs" to a specific query level, + * which might not be the one immediately containing it), but also differs in + * an important respect: it never evaluates its arguments, they merely + * designate expressions from the GROUP BY clause of the query level to which + * it belongs. + * + * The spec defines the evaluation of GROUPING() purely by syntactic + * replacement, but we make it a real expression for optimization purposes so + * that one Agg node can handle multiple grouping sets at once. Evaluating the + * result only needs the column positions to check against the grouping set + * being projected. However, for EXPLAIN to produce meaningful output, we have + * to keep the original expressions around, since expression deparse does not + * give us any feasible way to get at the GROUP BY clause. + * + * Also, we treat two GroupingFunc nodes as equal if they have equal arguments + * lists and agglevelsup, without comparing the refs and cols annotations. + * + * In raw parse output we have only the args list; parse analysis fills in the + * refs list, and the planner fills in the cols list. + */ +typedef struct GroupingFunc +{ + Expr xpr; + List *args; /* arguments, not evaluated but kept for + * benefit of EXPLAIN etc. */ + List *refs; /* ressortgrouprefs of arguments */ + List *cols; /* actual column positions set by planner */ + Index agglevelsup; /* same as Aggref.agglevelsup */ + int location; /* token location */ +} GroupingFunc; + +/* * WindowFunc */ typedef struct WindowFunc diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index d3ee61c4d0..279051ed18 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -260,6 +260,9 @@ typedef struct PlannerInfo /* optional private data for join_search_hook, e.g., GEQO */ void *join_search_private; + + /* for GroupingFunc fixup in setrefs */ + AttrNumber *grouping_map; } PlannerInfo; diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index da15fca1f6..52b077a1b4 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -59,6 +59,7 @@ extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls, extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual, AggStrategy aggstrategy, const AggClauseCosts *aggcosts, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, + List *groupingSets, long numGroups, Plan *lefttree); extern WindowAgg *make_windowagg(PlannerInfo *root, List *tlist, diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index 3dc8babacb..b0f0f19683 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -43,6 +43,9 @@ extern Node *get_sortgroupclause_expr(SortGroupClause *sgClause, extern List *get_sortgrouplist_exprs(List *sgClauses, List *targetList); +extern SortGroupClause *get_sortgroupref_clause(Index sortref, + List *clauses); + extern Oid *extract_grouping_ops(List *groupClause); extern AttrNumber *extract_grouping_cols(List *groupClause, List *tlist); extern bool grouping_is_sortable(List *groupClause); diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 7d5f857ae5..2414069077 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -99,6 +99,7 @@ PG_KEYWORD("cost", COST, UNRESERVED_KEYWORD) PG_KEYWORD("create", CREATE, RESERVED_KEYWORD) PG_KEYWORD("cross", CROSS, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("csv", CSV, UNRESERVED_KEYWORD) +PG_KEYWORD("cube", CUBE, UNRESERVED_KEYWORD) PG_KEYWORD("current", CURRENT_P, UNRESERVED_KEYWORD) PG_KEYWORD("current_catalog", CURRENT_CATALOG, RESERVED_KEYWORD) PG_KEYWORD("current_date", CURRENT_DATE, RESERVED_KEYWORD) @@ -174,6 +175,7 @@ PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD) PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD) PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD) PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD) +PG_KEYWORD("grouping", GROUPING, COL_NAME_KEYWORD) PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD) PG_KEYWORD("having", HAVING, RESERVED_KEYWORD) PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD) @@ -325,6 +327,7 @@ PG_KEYWORD("revoke", REVOKE, UNRESERVED_KEYWORD) PG_KEYWORD("right", RIGHT, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("role", ROLE, UNRESERVED_KEYWORD) PG_KEYWORD("rollback", ROLLBACK, UNRESERVED_KEYWORD) +PG_KEYWORD("rollup", ROLLUP, UNRESERVED_KEYWORD) PG_KEYWORD("row", ROW, COL_NAME_KEYWORD) PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD) PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD) @@ -343,6 +346,7 @@ PG_KEYWORD("session", SESSION, UNRESERVED_KEYWORD) PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD) PG_KEYWORD("set", SET, UNRESERVED_KEYWORD) PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD) +PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD) PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD) PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD) PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD) diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h index 91a0706f45..6a5f9bbdf1 100644 --- a/src/include/parser/parse_agg.h +++ b/src/include/parser/parse_agg.h @@ -18,11 +18,16 @@ extern void transformAggregateCall(ParseState *pstate, Aggref *agg, List *args, List *aggorder, bool agg_distinct); + +extern Node *transformGroupingFunc(ParseState *pstate, GroupingFunc *g); + extern void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, WindowDef *windef); extern void parseCheckAggregates(ParseState *pstate, Query *qry); +extern List *expand_grouping_sets(List *groupingSets, int limit); + extern int get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes); extern Oid resolve_aggregate_transtype(Oid aggfuncid, diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index f1b7d3d896..cbe5e76bb8 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -27,6 +27,7 @@ extern Node *transformWhereClause(ParseState *pstate, Node *clause, extern Node *transformLimitClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName); extern List *transformGroupClause(ParseState *pstate, List *grouplist, + List **groupingSets, List **targetlist, List *sortClause, ParseExprKind exprKind, bool useSQL99); extern List *transformSortClause(ParseState *pstate, List *orderlist, diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index bf69f2a629..fdca7130bb 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -185,7 +185,7 @@ extern void mergejoinscansel(PlannerInfo *root, Node *clause, Selectivity *rightstart, Selectivity *rightend); extern double estimate_num_groups(PlannerInfo *root, List *groupExprs, - double input_rows); + double input_rows, List **pgset); extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets); |
