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:
@ -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,
|
||||
|
@ -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 */
|
||||
}
|
||||
|
Reference in New Issue
Block a user