mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
Further sort-order twiddling in optimizer: be smart about
case where ORDER BY and GROUP BY request the same sort order.
This commit is contained in:
parent
c9d040d85e
commit
e8140adb10
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.75 1999/08/22 20:14:47 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.76 1999/08/22 23:56:44 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -51,7 +51,6 @@ static List *fix_indxqual_sublist(List *indexqual, IndexPath *index_path,
|
|||||||
Form_pg_index index);
|
Form_pg_index index);
|
||||||
static Node *fix_indxqual_operand(Node *node, IndexPath *index_path,
|
static Node *fix_indxqual_operand(Node *node, IndexPath *index_path,
|
||||||
Form_pg_index index);
|
Form_pg_index index);
|
||||||
static Noname *make_noname(List *tlist, List *pathkeys, Plan *plan_node);
|
|
||||||
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||||
List *indxid, List *indxqual, List *indxqualorig);
|
List *indxid, List *indxqual, List *indxqualorig);
|
||||||
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
|
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
|
||||||
@ -926,12 +925,12 @@ copy_costsize(Plan *dest, Plan *src)
|
|||||||
* 'tlist' is the target list of the scan to be sorted or materialized
|
* 'tlist' is the target list of the scan to be sorted or materialized
|
||||||
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
|
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
|
||||||
* (NIL implies no sort needed, just materialize it)
|
* (NIL implies no sort needed, just materialize it)
|
||||||
* 'plan_node' is the node which yields input tuples
|
* 'subplan' is the node which yields input tuples
|
||||||
*/
|
*/
|
||||||
static Noname *
|
Noname *
|
||||||
make_noname(List *tlist,
|
make_noname(List *tlist,
|
||||||
List *pathkeys,
|
List *pathkeys,
|
||||||
Plan *plan_node)
|
Plan *subplan)
|
||||||
{
|
{
|
||||||
List *noname_tlist;
|
List *noname_tlist;
|
||||||
int numsortkeys;
|
int numsortkeys;
|
||||||
@ -946,7 +945,7 @@ make_noname(List *tlist,
|
|||||||
/* need to sort */
|
/* need to sort */
|
||||||
retval = (Plan *) make_sort(noname_tlist,
|
retval = (Plan *) make_sort(noname_tlist,
|
||||||
_NONAME_RELATION_ID_,
|
_NONAME_RELATION_ID_,
|
||||||
plan_node,
|
subplan,
|
||||||
numsortkeys);
|
numsortkeys);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -954,7 +953,7 @@ make_noname(List *tlist,
|
|||||||
/* no sort */
|
/* no sort */
|
||||||
retval = (Plan *) make_material(noname_tlist,
|
retval = (Plan *) make_material(noname_tlist,
|
||||||
_NONAME_RELATION_ID_,
|
_NONAME_RELATION_ID_,
|
||||||
plan_node,
|
subplan,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.42 1999/08/22 20:14:48 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.43 1999/08/22 23:56:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -44,11 +44,14 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
|
|||||||
* qual is the qualification of the query
|
* qual is the qualification of the query
|
||||||
*
|
*
|
||||||
* Note: the Query node now also includes a query_pathkeys field, which
|
* Note: the Query node now also includes a query_pathkeys field, which
|
||||||
|
* is both an input and an output of query_planner(). The input value
|
||||||
* signals query_planner that the indicated sort order is wanted in the
|
* signals query_planner that the indicated sort order is wanted in the
|
||||||
* final output plan. If, for some reason, query_planner is unable to
|
* final output plan. The output value is the actual pathkeys of the
|
||||||
* comply, it sets query_pathkeys to NIL before returning. (The reason
|
* selected path. This might not be the same as what the caller requested;
|
||||||
* query_pathkeys is a Query field and not a passed parameter is that
|
* the caller must do pathkeys_contained_in() to decide whether an
|
||||||
* the low-level routines in indxpath.c need to see it.)
|
* explicit sort is still needed. (The main reason query_pathkeys is a
|
||||||
|
* Query field and not a passed parameter is that the low-level routines
|
||||||
|
* in indxpath.c need to see it.)
|
||||||
*
|
*
|
||||||
* Returns a query plan.
|
* Returns a query plan.
|
||||||
*/
|
*/
|
||||||
@ -255,7 +258,11 @@ subplanner(Query *root,
|
|||||||
if (root->query_pathkeys == NIL ||
|
if (root->query_pathkeys == NIL ||
|
||||||
pathkeys_contained_in(root->query_pathkeys,
|
pathkeys_contained_in(root->query_pathkeys,
|
||||||
final_rel->cheapestpath->pathkeys))
|
final_rel->cheapestpath->pathkeys))
|
||||||
|
{
|
||||||
|
root->query_pathkeys = final_rel->cheapestpath->pathkeys;
|
||||||
return create_plan(final_rel->cheapestpath);
|
return create_plan(final_rel->cheapestpath);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Otherwise, look to see if we have an already-ordered path that is
|
* Otherwise, look to see if we have an already-ordered path that is
|
||||||
* cheaper than doing an explicit sort on cheapestpath.
|
* cheaper than doing an explicit sort on cheapestpath.
|
||||||
@ -271,6 +278,7 @@ subplanner(Query *root,
|
|||||||
if (sortedpath->path_cost <= cheapest_cost)
|
if (sortedpath->path_cost <= cheapest_cost)
|
||||||
{
|
{
|
||||||
/* Found a better presorted path, use it */
|
/* Found a better presorted path, use it */
|
||||||
|
root->query_pathkeys = sortedpath->pathkeys;
|
||||||
return create_plan(sortedpath);
|
return create_plan(sortedpath);
|
||||||
}
|
}
|
||||||
/* otherwise, doing it the hard way is still cheaper */
|
/* otherwise, doing it the hard way is still cheaper */
|
||||||
@ -300,21 +308,31 @@ subplanner(Query *root,
|
|||||||
* then poke the result.
|
* then poke the result.
|
||||||
*/
|
*/
|
||||||
Plan *sortedplan = create_plan(sortedpath);
|
Plan *sortedplan = create_plan(sortedpath);
|
||||||
|
List *sortedpathkeys;
|
||||||
|
|
||||||
Assert(IsA(sortedplan, IndexScan));
|
Assert(IsA(sortedplan, IndexScan));
|
||||||
((IndexScan *) sortedplan)->indxorderdir = BackwardScanDirection;
|
((IndexScan *) sortedplan)->indxorderdir = BackwardScanDirection;
|
||||||
|
/*
|
||||||
|
* Need to generate commuted keys representing the actual
|
||||||
|
* sort order. This should succeed, probably, but just in
|
||||||
|
* case it does not, use the original root->query_pathkeys
|
||||||
|
* as a conservative approximation.
|
||||||
|
*/
|
||||||
|
sortedpathkeys = copyObject(sortedpath->pathkeys);
|
||||||
|
if (commute_pathkeys(sortedpathkeys))
|
||||||
|
root->query_pathkeys = sortedpathkeys;
|
||||||
|
|
||||||
return sortedplan;
|
return sortedplan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nothing for it but to sort the cheapestpath...
|
/* Nothing for it but to sort the cheapestpath --- but we let the
|
||||||
*
|
* caller do that. union_planner has to be able to add a sort node
|
||||||
* We indicate we failed to sort the plan, and let the caller
|
* anyway, so no need for extra code here. (Furthermore, the given
|
||||||
* stick the appropriate sort node on top. union_planner has to be
|
* pathkeys might involve something we can't compute yet, such as
|
||||||
* able to add a sort node anyway, so no need for extra code here.
|
* an aggregate function...)
|
||||||
*/
|
*/
|
||||||
root->query_pathkeys = NIL; /* sorry, it ain't sorted */
|
root->query_pathkeys = final_rel->cheapestpath->pathkeys;
|
||||||
|
|
||||||
return create_plan(final_rel->cheapestpath);
|
return create_plan(final_rel->cheapestpath);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.64 1999/08/22 20:14:48 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.65 1999/08/22 23:56:45 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -39,7 +39,7 @@ static List *make_subplanTargetList(Query *parse, List *tlist,
|
|||||||
AttrNumber **groupColIdx);
|
AttrNumber **groupColIdx);
|
||||||
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
|
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
|
||||||
List *groupClause, AttrNumber *grpColIdx,
|
List *groupClause, AttrNumber *grpColIdx,
|
||||||
bool is_sorted, Plan *subplan);
|
bool is_presorted, Plan *subplan);
|
||||||
static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
|
static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -91,7 +91,7 @@ union_planner(Query *parse)
|
|||||||
List *rangetable = parse->rtable;
|
List *rangetable = parse->rtable;
|
||||||
Plan *result_plan = (Plan *) NULL;
|
Plan *result_plan = (Plan *) NULL;
|
||||||
AttrNumber *groupColIdx = NULL;
|
AttrNumber *groupColIdx = NULL;
|
||||||
bool is_sorted = false;
|
List *current_pathkeys = NIL;
|
||||||
Index rt_index;
|
Index rt_index;
|
||||||
|
|
||||||
if (parse->unionClause)
|
if (parse->unionClause)
|
||||||
@ -102,6 +102,11 @@ union_planner(Query *parse)
|
|||||||
parse->commandType,
|
parse->commandType,
|
||||||
parse->resultRelation,
|
parse->resultRelation,
|
||||||
parse->rtable);
|
parse->rtable);
|
||||||
|
/*
|
||||||
|
* We leave current_pathkeys NIL indicating we do not know sort order.
|
||||||
|
* Actually, for a normal UNION we have done an explicit sort; ought
|
||||||
|
* to change interface to plan_union_queries to pass that info back!
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
else if ((rt_index = first_inherit_rt_entry(rangetable)) != -1)
|
else if ((rt_index = first_inherit_rt_entry(rangetable)) != -1)
|
||||||
{
|
{
|
||||||
@ -135,6 +140,10 @@ union_planner(Query *parse)
|
|||||||
|
|
||||||
if (parse->rowMark != NULL)
|
if (parse->rowMark != NULL)
|
||||||
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
|
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
|
||||||
|
/*
|
||||||
|
* We leave current_pathkeys NIL indicating we do not know sort order
|
||||||
|
* of the Append-ed results.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -182,25 +191,25 @@ union_planner(Query *parse)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate appropriate target list for subplan; may be different
|
||||||
|
* from tlist if grouping or aggregation is needed.
|
||||||
|
*/
|
||||||
|
sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Figure out whether we need a sorted result from query_planner.
|
* Figure out whether we need a sorted result from query_planner.
|
||||||
*
|
*
|
||||||
* If we have a GROUP BY clause, then we want a result sorted
|
* If we have a GROUP BY clause, then we want a result sorted
|
||||||
* properly for grouping. Otherwise, if there is an ORDER BY clause
|
* properly for grouping. Otherwise, if there is an ORDER BY clause,
|
||||||
* and no need for an aggregate node, we want to sort by the ORDER BY
|
* we want to sort by the ORDER BY clause.
|
||||||
* clause. (XXX In some cases, we could presort even when there is
|
|
||||||
* an aggregate, but I'll leave that refinement for another day.)
|
|
||||||
*
|
|
||||||
* NOTE: the reason we put the target pathkeys into the Query node
|
|
||||||
* rather than passing them as an argument to query_planner is that
|
|
||||||
* the low-level routines in indxpath.c want to be able to see them.
|
|
||||||
*/
|
*/
|
||||||
if (parse->groupClause)
|
if (parse->groupClause)
|
||||||
{
|
{
|
||||||
parse->query_pathkeys =
|
parse->query_pathkeys =
|
||||||
make_pathkeys_for_sortclauses(parse->groupClause, tlist);
|
make_pathkeys_for_sortclauses(parse->groupClause, tlist);
|
||||||
}
|
}
|
||||||
else if (parse->sortClause && ! parse->hasAggs)
|
else if (parse->sortClause)
|
||||||
{
|
{
|
||||||
parse->query_pathkeys =
|
parse->query_pathkeys =
|
||||||
make_pathkeys_for_sortclauses(parse->sortClause, tlist);
|
make_pathkeys_for_sortclauses(parse->sortClause, tlist);
|
||||||
@ -210,23 +219,16 @@ union_planner(Query *parse)
|
|||||||
parse->query_pathkeys = NIL;
|
parse->query_pathkeys = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Generate appropriate target list for subplan; may be different
|
|
||||||
* from tlist if grouping or aggregation is needed.
|
|
||||||
*/
|
|
||||||
sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
|
|
||||||
|
|
||||||
/* Generate the (sub) plan */
|
/* Generate the (sub) plan */
|
||||||
result_plan = query_planner(parse,
|
result_plan = query_planner(parse,
|
||||||
parse->commandType,
|
parse->commandType,
|
||||||
sub_tlist,
|
sub_tlist,
|
||||||
(List *) parse->qual);
|
(List *) parse->qual);
|
||||||
|
|
||||||
/* query_planner sets query_pathkeys to NIL if it didn't make
|
/* query_planner returns actual sort order (which is not
|
||||||
* a properly sorted plan
|
* necessarily what we requested) in query_pathkeys.
|
||||||
*/
|
*/
|
||||||
if (parse->query_pathkeys)
|
current_pathkeys = parse->query_pathkeys;
|
||||||
is_sorted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* query_planner returns NULL if it thinks plan is bogus */
|
/* query_planner returns NULL if it thinks plan is bogus */
|
||||||
@ -241,6 +243,8 @@ union_planner(Query *parse)
|
|||||||
{
|
{
|
||||||
bool tuplePerGroup;
|
bool tuplePerGroup;
|
||||||
List *group_tlist;
|
List *group_tlist;
|
||||||
|
List *group_pathkeys;
|
||||||
|
bool is_sorted;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decide whether how many tuples per group the Group node needs
|
* Decide whether how many tuples per group the Group node needs
|
||||||
@ -261,18 +265,33 @@ union_planner(Query *parse)
|
|||||||
else
|
else
|
||||||
group_tlist = tlist;
|
group_tlist = tlist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Figure out whether the path result is already ordered the way we
|
||||||
|
* need it --- if so, no need for an explicit sort step.
|
||||||
|
*/
|
||||||
|
group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,
|
||||||
|
tlist);
|
||||||
|
if (pathkeys_contained_in(group_pathkeys, current_pathkeys))
|
||||||
|
{
|
||||||
|
is_sorted = true; /* no sort needed now */
|
||||||
|
/* current_pathkeys remains unchanged */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We will need to do an explicit sort by the GROUP BY clause.
|
||||||
|
* make_groupplan will do the work, but set current_pathkeys
|
||||||
|
* to indicate the resulting order.
|
||||||
|
*/
|
||||||
|
is_sorted = false;
|
||||||
|
current_pathkeys = group_pathkeys;
|
||||||
|
}
|
||||||
|
|
||||||
result_plan = make_groupplan(group_tlist,
|
result_plan = make_groupplan(group_tlist,
|
||||||
tuplePerGroup,
|
tuplePerGroup,
|
||||||
parse->groupClause,
|
parse->groupClause,
|
||||||
groupColIdx,
|
groupColIdx,
|
||||||
is_sorted,
|
is_sorted,
|
||||||
result_plan);
|
result_plan);
|
||||||
|
|
||||||
/*
|
|
||||||
* Assume the result of the group step is not ordered suitably
|
|
||||||
* for any ORDER BY that may exist. XXX it might be; improve this!
|
|
||||||
*/
|
|
||||||
is_sorted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -325,20 +344,23 @@ union_planner(Query *parse)
|
|||||||
/* HAVING clause, if any, becomes qual of the Agg node */
|
/* HAVING clause, if any, becomes qual of the Agg node */
|
||||||
result_plan->qual = (List *) parse->havingQual;
|
result_plan->qual = (List *) parse->havingQual;
|
||||||
|
|
||||||
/*
|
/* Note: Agg does not affect any existing sort order of the tuples */
|
||||||
* Assume result is not ordered suitably for ORDER BY.
|
|
||||||
* XXX it might be; improve this!
|
|
||||||
*/
|
|
||||||
is_sorted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we were not able to make the plan come out in the right order,
|
* If we were not able to make the plan come out in the right order,
|
||||||
* add an explicit sort step.
|
* add an explicit sort step.
|
||||||
*/
|
*/
|
||||||
if (parse->sortClause && ! is_sorted)
|
if (parse->sortClause)
|
||||||
{
|
{
|
||||||
result_plan = make_sortplan(tlist, parse->sortClause, result_plan);
|
List *sort_pathkeys;
|
||||||
|
|
||||||
|
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
|
||||||
|
tlist);
|
||||||
|
if (! pathkeys_contained_in(sort_pathkeys, current_pathkeys))
|
||||||
|
{
|
||||||
|
result_plan = make_sortplan(tlist, parse->sortClause, result_plan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -474,12 +496,12 @@ make_groupplan(List *group_tlist,
|
|||||||
bool tuplePerGroup,
|
bool tuplePerGroup,
|
||||||
List *groupClause,
|
List *groupClause,
|
||||||
AttrNumber *grpColIdx,
|
AttrNumber *grpColIdx,
|
||||||
bool is_sorted,
|
bool is_presorted,
|
||||||
Plan *subplan)
|
Plan *subplan)
|
||||||
{
|
{
|
||||||
int numCols = length(groupClause);
|
int numCols = length(groupClause);
|
||||||
|
|
||||||
if (! is_sorted)
|
if (! is_presorted)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The Sort node always just takes a copy of the subplan's tlist
|
* The Sort node always just takes a copy of the subplan's tlist
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: planmain.h,v 1.32 1999/08/22 20:14:56 tgl Exp $
|
* $Id: planmain.h,v 1.33 1999/08/22 23:56:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -33,6 +33,7 @@ extern Sort *make_sort(List *tlist, Oid nonameid, Plan *lefttree,
|
|||||||
extern Agg *make_agg(List *tlist, Plan *lefttree);
|
extern Agg *make_agg(List *tlist, Plan *lefttree);
|
||||||
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
|
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
|
||||||
AttrNumber *grpColIdx, Plan *lefttree);
|
AttrNumber *grpColIdx, Plan *lefttree);
|
||||||
|
extern Noname *make_noname(List *tlist, List *pathkeys, Plan *subplan);
|
||||||
extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
|
extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
|
||||||
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user