mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Teach planner how to propagate pathkeys from sub-SELECTs in FROM up to
the outer query. (The implementation is a bit klugy, but it would take nontrivial restructuring to make it nicer, which this is probably not worth.) This avoids unnecessary sort steps in examples like SELECT foo,count(*) FROM (SELECT ... ORDER BY foo,bar) sub GROUP BY foo which means there is now a reasonable technique for controlling the order of inputs to custom aggregates, even in the grouping case.
This commit is contained in:
@ -11,7 +11,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.46 2003/02/08 20:20:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.47 2003/02/15 20:12:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -366,6 +366,31 @@ canonicalize_pathkeys(Query *root, List *pathkeys)
|
||||
return new_pathkeys;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* count_canonical_peers
|
||||
* Given a PathKeyItem, find the equi_key_list subset it is a member of,
|
||||
* if any. If so, return the number of other members of the set.
|
||||
* If not, return 0 (without actually adding it to our equi_key_list).
|
||||
*
|
||||
* This is a hack to support the rather bogus heuristics in
|
||||
* build_subquery_pathkeys.
|
||||
*/
|
||||
static int
|
||||
count_canonical_peers(Query *root, PathKeyItem *item)
|
||||
{
|
||||
List *cursetlink;
|
||||
|
||||
foreach(cursetlink, root->equi_key_list)
|
||||
{
|
||||
List *curset = lfirst(cursetlink);
|
||||
|
||||
if (member(item, curset))
|
||||
return length(curset) - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* PATHKEY COMPARISONS
|
||||
****************************************************************************/
|
||||
@ -597,6 +622,9 @@ get_cheapest_fractional_path_for_pathkeys(List *paths,
|
||||
*
|
||||
* If 'scandir' is BackwardScanDirection, attempt to build pathkeys
|
||||
* representing a backwards scan of the index. Return NIL if can't do it.
|
||||
*
|
||||
* We generate the full pathkeys list whether or not all are useful for the
|
||||
* current query. Caller should do truncate_useless_pathkeys().
|
||||
*/
|
||||
List *
|
||||
build_index_pathkeys(Query *root,
|
||||
@ -699,9 +727,10 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
|
||||
|
||||
foreach(temp, rel->targetlist)
|
||||
{
|
||||
Var *tle_var = get_expr(lfirst(temp));
|
||||
Var *tle_var = (Var *) ((TargetEntry *) lfirst(temp))->expr;
|
||||
|
||||
if (IsA(tle_var, Var) &&tle_var->varattno == varattno)
|
||||
if (IsA(tle_var, Var) &&
|
||||
tle_var->varattno == varattno)
|
||||
return tle_var;
|
||||
}
|
||||
|
||||
@ -714,6 +743,112 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
|
||||
return makeVar(relid, varattno, vartypeid, type_mod, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* build_subquery_pathkeys
|
||||
* Build a pathkeys list that describes the ordering of a subquery's
|
||||
* result (in the terms of the outer query). The subquery must already
|
||||
* have been planned, so that its query_pathkeys field has been set.
|
||||
*
|
||||
* It is not necessary for caller to do truncate_useless_pathkeys(),
|
||||
* because we select keys in a way that takes usefulness of the keys into
|
||||
* account.
|
||||
*/
|
||||
List *
|
||||
build_subquery_pathkeys(Query *root, RelOptInfo *rel, Query *subquery)
|
||||
{
|
||||
List *retval = NIL;
|
||||
int retvallen = 0;
|
||||
int outer_query_keys = length(root->query_pathkeys);
|
||||
List *l;
|
||||
|
||||
foreach(l, subquery->query_pathkeys)
|
||||
{
|
||||
List *sub_pathkey = (List *) lfirst(l);
|
||||
List *j;
|
||||
PathKeyItem *best_item = NULL;
|
||||
int best_score = 0;
|
||||
List *cpathkey;
|
||||
|
||||
/*
|
||||
* The sub_pathkey could contain multiple elements (representing
|
||||
* knowledge that multiple items are effectively equal). Each
|
||||
* element might match none, one, or more of the output columns
|
||||
* that are visible to the outer query. This means we may have
|
||||
* multiple possible representations of the sub_pathkey in the
|
||||
* context of the outer query. Ideally we would generate them all
|
||||
* and put them all into a pathkey list of the outer query, thereby
|
||||
* propagating equality knowledge up to the outer query. Right now
|
||||
* we cannot do so, because the outer query's canonical pathkey
|
||||
* sets are already frozen when this is called. Instead we prefer
|
||||
* the one that has the highest "score" (number of canonical pathkey
|
||||
* peers, plus one if it matches the outer query_pathkeys).
|
||||
* This is the most likely to be useful in the outer query.
|
||||
*/
|
||||
foreach(j, sub_pathkey)
|
||||
{
|
||||
PathKeyItem *sub_item = (PathKeyItem *) lfirst(j);
|
||||
Node *sub_key = sub_item->key;
|
||||
List *k;
|
||||
|
||||
foreach(k, subquery->targetList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(k);
|
||||
|
||||
if (!tle->resdom->resjunk &&
|
||||
equal(tle->expr, sub_key))
|
||||
{
|
||||
/* Found a representation for this sub_key */
|
||||
Var *outer_var;
|
||||
PathKeyItem *outer_item;
|
||||
int score;
|
||||
|
||||
outer_var = makeVar(rel->relid,
|
||||
tle->resdom->resno,
|
||||
tle->resdom->restype,
|
||||
tle->resdom->restypmod,
|
||||
0);
|
||||
outer_item = makePathKeyItem((Node *) outer_var,
|
||||
sub_item->sortop);
|
||||
/* score = # of mergejoin peers */
|
||||
score = count_canonical_peers(root, outer_item);
|
||||
/* +1 if it matches the proper query_pathkeys item */
|
||||
if (retvallen < outer_query_keys &&
|
||||
member(outer_item,
|
||||
nth(retvallen, root->query_pathkeys)))
|
||||
score++;
|
||||
if (score > best_score)
|
||||
{
|
||||
best_item = outer_item;
|
||||
best_score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we couldn't find a representation of this sub_pathkey,
|
||||
* we're done (we can't use the ones to its right, either).
|
||||
*/
|
||||
if (!best_item)
|
||||
break;
|
||||
|
||||
/* Canonicalize the chosen item (we did not before) */
|
||||
cpathkey = make_canonical_pathkey(root, best_item);
|
||||
|
||||
/*
|
||||
* Eliminate redundant ordering info; could happen if outer
|
||||
* query equijoins subquery keys...
|
||||
*/
|
||||
if (!ptrMember(cpathkey, retval))
|
||||
{
|
||||
retval = lappend(retval, cpathkey);
|
||||
retvallen++;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* build_join_pathkeys
|
||||
* Build the path keys for a join relation constructed by mergejoin or
|
||||
|
Reference in New Issue
Block a user