1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Reimplement planner's handling of MIN/MAX aggregate optimization (again).

Instead of playing cute games with pathkeys, just build a direct
representation of the intended sub-select, and feed it through
query_planner to get a Path for the index access.  This is a bit slower
than 9.1's previous method, since we'll duplicate most of the overhead of
query_planner; but since the whole optimization only applies to rather
simple single-table queries, that probably won't be much of a problem in
practice.  The advantage is that we get to do the right thing when there's
a partial index that needs the implicit IS NOT NULL clause to be usable.
Also, although this makes planagg.c be a bit more closely tied to the
ordering of operations in grouping_planner, we can get rid of some coupling
to lower-level parts of the planner.  Per complaint from Marti Raudsepp.
This commit is contained in:
Tom Lane
2011-03-22 00:34:31 -04:00
parent 6d8096e2f3
commit 8df08c8489
12 changed files with 267 additions and 549 deletions

View File

@ -41,6 +41,13 @@
#define IsBooleanOpfamily(opfamily) \
((opfamily) == BOOL_BTREE_FAM_OID || (opfamily) == BOOL_HASH_FAM_OID)
/* Whether to use ScalarArrayOpExpr to build index qualifications */
typedef enum
{
SAOP_FORBID, /* Do not use ScalarArrayOpExpr */
SAOP_ALLOW, /* OK to use ScalarArrayOpExpr */
SAOP_REQUIRE /* Require ScalarArrayOpExpr */
} SaOpControl;
/* Whether we are looking for plain indexscan, bitmap scan, or either */
typedef enum
@ -78,6 +85,11 @@ static PathClauseUsage *classify_index_clause_usage(Path *path,
List **clauselist);
static void find_indexpath_quals(Path *bitmapqual, List **quals, List **preds);
static int find_list_position(Node *node, List **nodelist);
static List *group_clauses_by_indexkey(IndexOptInfo *index,
List *clauses, List *outer_clauses,
Relids outer_relids,
SaOpControl saop_control,
bool *found_clause);
static bool match_clause_to_indexcol(IndexOptInfo *index,
int indexcol,
RestrictInfo *rinfo,
@ -1060,7 +1072,7 @@ find_list_position(Node *node, List **nodelist)
* from multiple places. Defend against redundant outputs by using
* list_append_unique_ptr (pointer equality should be good enough).
*/
List *
static List *
group_clauses_by_indexkey(IndexOptInfo *index,
List *clauses, List *outer_clauses,
Relids outer_relids,

View File

@ -905,39 +905,6 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
return pathkeys;
}
/****************************************************************************
* PATHKEYS AND AGGREGATES
****************************************************************************/
/*
* make_pathkeys_for_aggregate
* Generate a pathkeys list (always a 1-item list) that represents
* the sort order needed by a MIN/MAX aggregate
*
* This is only called before EquivalenceClass merging, so we can assume
* we are not supposed to canonicalize.
*/
List *
make_pathkeys_for_aggregate(PlannerInfo *root,
Expr *aggtarget,
Oid aggsortop)
{
PathKey *pathkey;
/*
* We arbitrarily set nulls_first to false. Actually, a MIN/MAX agg can
* use either nulls ordering option, but that is dealt with elsewhere.
*/
pathkey = make_pathkey_from_sortop(root,
aggtarget,
aggsortop,
false, /* nulls_first */
0,
true,
false);
return list_make1(pathkey);
}
/****************************************************************************
* PATHKEYS AND MERGECLAUSES
****************************************************************************/
@ -1407,11 +1374,10 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
* PATHKEY USEFULNESS CHECKS
*
* We only want to remember as many of the pathkeys of a path as have some
* potential use, which can include subsequent mergejoins, meeting the query's
* requested output ordering, or implementing MIN/MAX aggregates. This
* ensures that add_path() won't consider a path to have a usefully different
* ordering unless it really is useful. These routines check for usefulness
* of given pathkeys.
* potential use, either for subsequent mergejoins or for meeting the query's
* requested output ordering. This ensures that add_path() won't consider
* a path to have a usefully different ordering unless it really is useful.
* These routines check for usefulness of given pathkeys.
****************************************************************************/
/*
@ -1553,50 +1519,6 @@ pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys)
return 0; /* path ordering not useful */
}
/*
* pathkeys_useful_for_minmax
* Count the number of pathkeys that are useful for implementing
* some MIN/MAX aggregate.
*
* Like pathkeys_useful_for_ordering, this is a yes-or-no affair, but
* there could be several MIN/MAX aggregates and we can match to any one.
*
* We can't use pathkeys_contained_in() because we would like to match
* pathkeys regardless of the nulls_first setting. However, we know that
* MIN/MAX aggregates will have at most one item in their pathkeys, so it's
* not too complicated to match by brute force.
*/
static int
pathkeys_useful_for_minmax(PlannerInfo *root, List *pathkeys)
{
PathKey *pathkey;
ListCell *lc;
if (pathkeys == NIL)
return 0; /* unordered path */
pathkey = (PathKey *) linitial(pathkeys);
foreach(lc, root->minmax_aggs)
{
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
PathKey *mmpathkey;
/* Ignore minmax agg if its pathkey turned out to be redundant */
if (mminfo->pathkeys == NIL)
continue;
Assert(list_length(mminfo->pathkeys) == 1);
mmpathkey = (PathKey *) linitial(mminfo->pathkeys);
if (mmpathkey->pk_eclass == pathkey->pk_eclass &&
mmpathkey->pk_opfamily == pathkey->pk_opfamily &&
mmpathkey->pk_strategy == pathkey->pk_strategy)
return 1;
}
return 0; /* path ordering not useful */
}
/*
* truncate_useless_pathkeys
* Shorten the given pathkey list to just the useful pathkeys.
@ -1608,15 +1530,11 @@ truncate_useless_pathkeys(PlannerInfo *root,
{
int nuseful;
int nuseful2;
int nuseful3;
nuseful = pathkeys_useful_for_merging(root, rel, pathkeys);
nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
if (nuseful2 > nuseful)
nuseful = nuseful2;
nuseful3 = pathkeys_useful_for_minmax(root, pathkeys);
if (nuseful3 > nuseful)
nuseful = nuseful3;
/*
* Note: not safe to modify input list destructively, but we can avoid
@ -1642,8 +1560,8 @@ truncate_useless_pathkeys(PlannerInfo *root,
*
* We could make the test more complex, for example checking to see if any of
* the joinclauses are really mergejoinable, but that likely wouldn't win
* often enough to repay the extra cycles. Queries with no join, sort, or
* aggregate at all are reasonably common, so this much work seems worthwhile.
* often enough to repay the extra cycles. Queries with neither a join nor
* a sort are reasonably common, though, so this much work seems worthwhile.
*/
bool
has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel)
@ -1652,7 +1570,5 @@ has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel)
return true; /* might be able to use pathkeys for merging */
if (root->query_pathkeys != NIL)
return true; /* might be able to use them for ordering */
if (root->minmax_aggs != NIL)
return true; /* might be able to use them for MIN/MAX */
return false; /* definitely useless */
}