aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/planner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r--src/backend/optimizer/plan/planner.c657
1 files changed, 197 insertions, 460 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 17ad449839b..b328c40f226 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.62 1999/08/09 06:20:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.63 1999/08/21 03:49:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/internal.h"
+#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
@@ -35,11 +36,10 @@
#include "utils/syscache.h"
static List *make_subplanTargetList(Query *parse, List *tlist,
- AttrNumber **groupColIdx);
+ AttrNumber **groupColIdx);
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
- List *groupClause, AttrNumber *grpColIdx,
- Plan *subplan);
-static ScanDirection get_dir_to_omit_sortplan(List *sortcls, Plan *plan);
+ List *groupClause, AttrNumber *grpColIdx,
+ bool is_sorted, Plan *subplan);
static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
/*****************************************************************************
@@ -59,6 +59,7 @@ planner(Query *parse)
PlannerPlanId = 0;
transformKeySetQuery(parse);
+
result_plan = union_planner(parse);
Assert(PlannerQueryLevel == 1);
@@ -88,6 +89,7 @@ union_planner(Query *parse)
List *rangetable = parse->rtable;
Plan *result_plan = (Plan *) NULL;
AttrNumber *groupColIdx = NULL;
+ bool is_sorted = false;
Index rt_index;
if (parse->unionClause)
@@ -180,6 +182,34 @@ union_planner(Query *parse)
}
/*
+ * Figure out whether we need a sorted result from query_planner.
+ *
+ * If we have a GROUP BY clause, then we want a result sorted
+ * 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
+ * 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)
+ {
+ parse->query_pathkeys =
+ make_pathkeys_for_sortclauses(parse->groupClause, tlist);
+ }
+ else if (parse->sortClause && ! parse->hasAggs)
+ {
+ parse->query_pathkeys =
+ make_pathkeys_for_sortclauses(parse->sortClause, tlist);
+ }
+ else
+ {
+ parse->query_pathkeys = NIL;
+ }
+
+ /*
* Generate appropriate target list for subplan; may be different
* from tlist if grouping or aggregation is needed.
*/
@@ -190,6 +220,12 @@ union_planner(Query *parse)
parse->commandType,
sub_tlist,
(List *) parse->qual);
+
+ /* query_planner sets query_pathkeys to NIL if it didn't make
+ * a properly sorted plan
+ */
+ if (parse->query_pathkeys)
+ is_sorted = true;
}
/* query_planner returns NULL if it thinks plan is bogus */
@@ -197,8 +233,8 @@ union_planner(Query *parse)
elog(ERROR, "union_planner: failed to create plan");
/*
- * If we have a GROUP BY clause, insert a group node (with the
- * appropriate sort node.)
+ * If we have a GROUP BY clause, insert a group node (plus the
+ * appropriate sort node, if necessary).
*/
if (parse->groupClause)
{
@@ -215,17 +251,27 @@ union_planner(Query *parse)
/*
* If there are aggregates then the Group node should just return
- * the same (simplified) tlist as the subplan, which we indicate
- * to make_groupplan by passing NIL. If there are no aggregates
+ * the same set of vars as the subplan did (but we can exclude
+ * any GROUP BY expressions). If there are no aggregates
* then the Group node had better compute the final tlist.
*/
- group_tlist = parse->hasAggs ? NIL : tlist;
+ if (parse->hasAggs)
+ group_tlist = flatten_tlist(result_plan->targetlist);
+ else
+ group_tlist = tlist;
result_plan = make_groupplan(group_tlist,
tuplePerGroup,
parse->groupClause,
groupColIdx,
+ is_sorted,
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;
}
/*
@@ -269,66 +315,48 @@ union_planner(Query *parse)
result_plan->qual = (List *) parse->havingQual;
/*
- * Update vars to refer to subplan result tuples, find Aggrefs,
+ * Update vars to refer to subplan result tuples, and
* make sure there is an Aggref in every HAVING clause.
*/
if (!set_agg_tlist_references((Agg *) result_plan))
elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
/*
- * Check that we actually found some aggregates, else executor
- * will die unpleasantly. (This defends against possible bugs in
- * parser or rewrite that might cause hasAggs to be incorrectly
- * set 'true'. It's not easy to recover here, since we've already
- * made decisions assuming there will be an Agg node.)
+ * Assume result is not ordered suitably for ORDER BY.
+ * XXX it might be; improve this!
*/
- if (((Agg *) result_plan)->aggs == NIL)
- elog(ERROR, "union_planner: query is marked hasAggs, but I don't see any");
+ is_sorted = false;
}
/*
- * For now, before we hand back the plan, check to see if there is a
- * user-specified sort that needs to be done. Eventually, this will
- * be moved into the guts of the planner s.t. user specified sorts
- * will be considered as part of the planning process. Since we can
- * only make use of user-specified sorts in special cases, we can do
- * the optimization step later.
+ * If we were not able to make the plan come out in the right order,
+ * add an explicit sort step.
*/
-
- if (parse->uniqueFlag)
+ if (parse->sortClause && ! is_sorted)
{
- Plan *sortplan = make_sortplan(tlist, parse->sortClause, result_plan);
-
- return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag));
+ result_plan = make_sortplan(tlist, parse->sortClause, result_plan);
}
- else
+
+ /*
+ * Finally, if there is a UNIQUE clause, add the UNIQUE node.
+ */
+ if (parse->uniqueFlag)
{
- if (parse->sortClause)
- {
- ScanDirection dir = get_dir_to_omit_sortplan(parse->sortClause, result_plan);
- if (ScanDirectionIsNoMovement(dir))
- return (make_sortplan(tlist, parse->sortClause, result_plan));
- else
- {
- ((IndexScan *)result_plan)->indxorderdir = dir;
- return ((Plan *) result_plan);
- }
- }
- else
- return ((Plan *) result_plan);
+ result_plan = (Plan *) make_unique(tlist, result_plan,
+ parse->uniqueFlag);
}
+ return result_plan;
}
/*---------------
* make_subplanTargetList
- * Generate appropriate target lists when grouping is required.
+ * Generate appropriate target list when grouping is required.
*
- * When union_planner inserts Aggregate and/or Group/Sort plan nodes above
- * the result of query_planner, we typically need to pass a different
+ * When union_planner inserts Aggregate and/or Group plan nodes above
+ * the result of query_planner, we typically want to pass a different
* target list to query_planner than the outer plan nodes should have.
- * This routine generates the correct target list for the subplan, and
- * if necessary modifies the target list for the inserted nodes as well.
+ * This routine generates the correct target list for the subplan.
*
* The initial target list passed from the parser already contains entries
* for all ORDER BY and GROUP BY expressions, but it will not have entries
@@ -340,26 +368,18 @@ union_planner(Query *parse)
* given a query like
* SELECT a+b,SUM(c+d) FROM table GROUP BY a+b;
* we want to pass this targetlist to the subplan:
- * a+b,c,d
+ * a,b,c,d,a+b
* where the a+b target will be used by the Sort/Group steps, and the
- * c and d targets will be needed to compute the aggregate results.
+ * other targets will be used for computing the final results. (In the
+ * above example we could theoretically suppress the a and b targets and
+ * use only a+b, but it's not really worth the trouble.)
*
* 'parse' is the query being processed.
- * 'tlist' is the query's target list. CAUTION: list elements may be
- * modified by this routine!
+ * 'tlist' is the query's target list.
* 'groupColIdx' receives an array of column numbers for the GROUP BY
* expressions (if there are any) in the subplan's target list.
*
- * The result is the targetlist to be passed to the subplan. Also,
- * the parent tlist is modified so that any nontrivial targetlist items that
- * exactly match GROUP BY items are replaced by simple Var nodes referencing
- * those outputs of the subplan. This avoids redundant recalculations in
- * cases like
- * SELECT a+1, ... GROUP BY a+1
- * Note, however, that other varnodes in the parent's targetlist (and
- * havingQual, if any) will still need to be updated to refer to outputs
- * of the subplan. This routine is quite large enough already, so we do
- * that later.
+ * The result is the targetlist to be passed to the subplan.
*---------------
*/
static List *
@@ -368,14 +388,8 @@ make_subplanTargetList(Query *parse,
AttrNumber **groupColIdx)
{
List *sub_tlist;
- List *prnt_tlist;
- List *sl,
- *gl;
- List *glc = NIL;
- List *extravars = NIL;
+ List *extravars;
int numCols;
- AttrNumber *grpColIdx = NULL;
- int next_resno = 1;
*groupColIdx = NULL;
@@ -387,247 +401,151 @@ make_subplanTargetList(Query *parse,
return tlist;
/*
- * If grouping, make a working copy of groupClause list (which we use
- * just to verify that we found all the groupClause items in tlist).
- * Also allocate space to remember where the group columns are in the
- * subplan tlist.
+ * Otherwise, start with a "flattened" tlist (having just the vars
+ * mentioned in the targetlist and HAVING qual).
*/
- numCols = length(parse->groupClause);
- if (numCols > 0)
- {
- glc = listCopy(parse->groupClause);
- grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
- *groupColIdx = grpColIdx;
- }
-
- sub_tlist = new_unsorted_tlist(tlist); /* make a modifiable copy */
+ sub_tlist = flatten_tlist(tlist);
+ extravars = pull_var_clause(parse->havingQual);
+ sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
+ freeList(extravars);
/*
- * Step 1: build grpColIdx by finding targetlist items that match
- * GroupBy entries. If there are aggregates, remove non-GroupBy items
- * from sub_tlist, and reset its resnos accordingly. When we leave an
- * expression in the subplan tlist, modify the parent tlist to copy
- * the value from the subplan output rather than re-evaluating it.
+ * If grouping, create sub_tlist entries for all GROUP BY expressions
+ * (GROUP BY items that are simple Vars should be in the list already),
+ * and make an array showing where the group columns are in the sub_tlist.
*/
- prnt_tlist = tlist; /* scans parent tlist in sync with sl */
- foreach(sl, sub_tlist)
+ numCols = length(parse->groupClause);
+ if (numCols > 0)
{
- TargetEntry *te = (TargetEntry *) lfirst(sl);
- TargetEntry *parentte = (TargetEntry *) lfirst(prnt_tlist);
- Resdom *resdom = te->resdom;
- bool keepInSubPlan = true;
- bool foundGroupClause = false;
int keyno = 0;
+ AttrNumber *grpColIdx;
+ List *gl;
+
+ grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+ *groupColIdx = grpColIdx;
foreach(gl, parse->groupClause)
{
- GroupClause *grpcl = (GroupClause *) lfirst(gl);
+ GroupClause *grpcl = (GroupClause *) lfirst(gl);
+ Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist);
+ TargetEntry *te = NULL;
+ List *sl;
- keyno++; /* sort key # for this GroupClause */
- if (grpcl->tleGroupref == resdom->resgroupref)
+ /* Find or make a matching sub_tlist entry */
+ foreach(sl, sub_tlist)
{
- /* Found a matching groupclause; record info for sorting */
- foundGroupClause = true;
- resdom->reskey = keyno;
- resdom->reskeyop = get_opcode(grpcl->grpOpoid);
- grpColIdx[keyno - 1] = next_resno;
-
- /*
- * Remove groupclause from our list of unmatched
- * groupclauses. NB: this depends on having used a shallow
- * listCopy() above.
- */
- glc = lremove((void *) grpcl, glc);
- break;
+ te = (TargetEntry *) lfirst(sl);
+ if (equal(groupexpr, te->expr))
+ break;
}
- }
-
- if (!foundGroupClause)
- {
-
- /*
- * Non-GroupBy entry: remove it from subplan if there are
- * aggregates in query - it will be evaluated by Aggregate
- * plan. But do not remove simple-Var entries; we'd just have
- * to add them back anyway, and we risk confusing
- * INSERT/UPDATE.
- */
- if (parse->hasAggs && !IsA(te->expr, Var))
- keepInSubPlan = false;
- }
-
- if (keepInSubPlan)
- {
- /* Assign new sequential resnos to subplan tlist items */
- resdom->resno = next_resno++;
- if (!IsA(parentte->expr, Var))
+ if (! sl)
{
-
- /*
- * Since the item is being computed in the subplan, we can
- * just make a Var node to reference it in the outer plan,
- * rather than recomputing it there. Note we use varnoold
- * = -1 as a flag to let replace_vars_with_subplan_refs
- * know it needn't change this Var node. If it's only a
- * Var anyway, we leave it alone for now;
- * replace_vars_with_subplan_refs will fix it later.
- */
- parentte->expr = (Node *) makeVar(1, resdom->resno,
- resdom->restype,
- resdom->restypmod,
- 0, -1, resdom->resno);
+ te = makeTargetEntry(makeResdom(length(sub_tlist) + 1,
+ exprType(groupexpr),
+ exprTypmod(groupexpr),
+ NULL,
+ (Index) 0,
+ (Oid) 0,
+ false),
+ groupexpr);
+ sub_tlist = lappend(sub_tlist, te);
}
- }
- else
- {
-
- /*
- * Remove this tlist item from the subplan, but remember the
- * vars it needs. The outer tlist item probably needs
- * changes, but that will happen later.
- */
- sub_tlist = lremove(te, sub_tlist);
- extravars = nconc(extravars, pull_var_clause(te->expr));
- }
-
- prnt_tlist = lnext(prnt_tlist);
- }
-
- /* We should have found all the GROUP BY clauses in the tlist. */
- if (length(glc) != 0)
- elog(ERROR, "make_subplanTargetList: GROUP BY attribute not found in target list");
- /*
- * Add subplan targets for any variables needed by removed tlist
- * entries that aren't otherwise mentioned in the subplan target list.
- * We'll also need targets for any variables seen only in HAVING.
- */
- extravars = nconc(extravars, pull_var_clause(parse->havingQual));
-
- foreach(gl, extravars)
- {
- Var *v = (Var *) lfirst(gl);
-
- if (tlist_member(v, sub_tlist) == NULL)
- {
-
- /*
- * Make sure sub_tlist element is a fresh object not shared
- * with any other structure; not sure if anything will break
- * if it is shared, but better to be safe...
- */
- sub_tlist = lappend(sub_tlist,
- create_tl_element((Var *) copyObject(v),
- next_resno));
- next_resno++;
+ /* and save its resno */
+ grpColIdx[keyno++] = te->resdom->resno;
}
}
return sub_tlist;
}
+/*
+ * make_groupplan
+ * Add a Group node for GROUP BY processing.
+ * If we couldn't make the subplan produce presorted output for grouping,
+ * first add an explicit Sort node.
+ */
static Plan *
make_groupplan(List *group_tlist,
bool tuplePerGroup,
List *groupClause,
AttrNumber *grpColIdx,
+ bool is_sorted,
Plan *subplan)
{
- List *sort_tlist;
- List *sl;
- Sort *sortplan;
- Group *grpplan;
int numCols = length(groupClause);
+ Group *grpplan;
- /*
- * Make the targetlist for the Sort node; it always just references
- * each of the corresponding target items of the subplan. We need to
- * ensure that simple Vars in the subplan's target list are
- * recognizable by replace_vars_with_subplan_refs when it's applied to
- * the Sort/Group target list, so copy up their varnoold/varoattno.
- */
- sort_tlist = NIL;
- foreach(sl, subplan->targetlist)
+ if (! is_sorted)
{
- TargetEntry *te = (TargetEntry *) lfirst(sl);
- Resdom *resdom = te->resdom;
- Var *newvar;
+ /*
+ * The Sort node always just takes a copy of the subplan's tlist
+ * plus ordering information. (This might seem inefficient if the
+ * subplan contains complex GROUP BY expressions, but in fact Sort
+ * does not evaluate its targetlist --- it only outputs the same
+ * tuples in a new order. So the expressions we might be copying
+ * are just dummies with no extra execution cost.)
+ */
+ List *sort_tlist = new_unsorted_tlist(subplan->targetlist);
+ int keyno = 0;
+ List *gl;
- if (IsA(te->expr, Var))
+ foreach(gl, groupClause)
{
- Var *subvar = (Var *) te->expr;
+ GroupClause *grpcl = (GroupClause *) lfirst(gl);
+ TargetEntry *te = nth(grpColIdx[keyno]-1, sort_tlist);
+ Resdom *resdom = te->resdom;
- newvar = makeVar(1, resdom->resno,
- resdom->restype, resdom->restypmod,
- 0, subvar->varnoold, subvar->varoattno);
- }
- else
- {
- newvar = makeVar(1, resdom->resno,
- resdom->restype, resdom->restypmod,
- 0, -1, resdom->resno);
+ /*
+ * Check for the possibility of duplicate group-by clauses --- the
+ * parser should have removed 'em, but the Sort executor will get
+ * terribly confused if any get through!
+ */
+ if (resdom->reskey == 0)
+ {
+ /* OK, insert the ordering info needed by the executor. */
+ resdom->reskey = ++keyno;
+ resdom->reskeyop = get_opcode(grpcl->sortop);
+ }
}
- sort_tlist = lappend(sort_tlist,
- makeTargetEntry((Resdom *) copyObject(resdom),
- (Node *) newvar));
+ subplan = (Plan *) make_sort(sort_tlist,
+ _NONAME_RELATION_ID_,
+ subplan,
+ keyno);
}
/*
- * Make the Sort node
+ * Fix variables in tlist (should be done somewhere else?)
*/
- sortplan = make_sort(sort_tlist,
- _NONAME_RELATION_ID_,
- subplan,
- numCols);
- sortplan->plan.cost = subplan->cost; /* XXX assume no cost */
-
- /*
- * If the caller gave us a target list, use it after fixing the
- * variables. If not, we need the same sort of "repeater" tlist as for
- * the Sort node.
- */
- if (group_tlist)
- {
- group_tlist = copyObject(group_tlist); /* necessary?? */
- replace_tlist_with_subplan_refs(group_tlist,
- (Index) 0,
- subplan->targetlist);
- }
- else
- group_tlist = copyObject(sort_tlist);
+ group_tlist = copyObject(group_tlist); /* necessary?? */
+ replace_tlist_with_subplan_refs(group_tlist,
+ (Index) 0,
+ subplan->targetlist);
/*
* Make the Group node
*/
grpplan = make_group(group_tlist, tuplePerGroup, numCols,
- grpColIdx, sortplan);
+ grpColIdx, subplan);
return (Plan *) grpplan;
}
/*
* make_sortplan
- * Returns a sortplan which is basically a SORT node attached to the
- * top of the plan returned from the planner. It also adds the
- * cost of sorting into the plan.
- *
- * sortkeys: ( resdom1 resdom2 resdom3 ...)
- * sortops: (sortop1 sortop2 sortop3 ...)
+ * Add a Sort node to implement an explicit ORDER BY clause.
*/
static Plan *
make_sortplan(List *tlist, List *sortcls, Plan *plannode)
{
- Plan *sortplan = (Plan *) NULL;
- List *temp_tlist = NIL;
- List *i = NIL;
- Resdom *resnode = (Resdom *) NULL;
- Resdom *resdom = (Resdom *) NULL;
- int keyno = 1;
+ List *temp_tlist;
+ List *i;
+ int keyno = 0;
/*
- * First make a copy of the tlist so that we don't corrupt the the
- * original .
+ * First make a copy of the tlist so that we don't corrupt the
+ * original.
*/
temp_tlist = new_unsorted_tlist(tlist);
@@ -635,35 +553,38 @@ make_sortplan(List *tlist, List *sortcls, Plan *plannode)
foreach(i, sortcls)
{
SortClause *sortcl = (SortClause *) lfirst(i);
+ Index refnumber = sortcl->tleSortGroupRef;
+ TargetEntry *tle = NULL;
+ Resdom *resdom;
+ List *l;
- resnode = sortcl->resdom;
- resdom = tlist_resdom(temp_tlist, resnode);
+ foreach(l, temp_tlist)
+ {
+ tle = (TargetEntry *) lfirst(l);
+ if (tle->resdom->ressortgroupref == refnumber)
+ break;
+ }
+ if (l == NIL)
+ elog(ERROR, "make_sortplan: ORDER BY expression not found in targetlist");
+ resdom = tle->resdom;
/*
- * Order the resdom keys and replace the operator OID for each key
- * with the regproc OID.
+ * Check for the possibility of duplicate order-by clauses --- the
+ * parser should have removed 'em, but the executor will get terribly
+ * confused if any get through!
*/
- resdom->reskey = keyno;
- resdom->reskeyop = get_opcode(sortcl->opoid);
- keyno += 1;
+ if (resdom->reskey == 0)
+ {
+ /* OK, insert the ordering info needed by the executor. */
+ resdom->reskey = ++keyno;
+ resdom->reskeyop = get_opcode(sortcl->sortop);
+ }
}
- sortplan = (Plan *) make_sort(temp_tlist,
- _NONAME_RELATION_ID_,
- (Plan *) plannode,
- length(sortcls));
-
- /*
- * XXX Assuming that an internal sort has no. cost. This is wrong, but
- * given that at this point, we don't know the no. of tuples returned,
- * etc, we can't do better than to add a constant cost. This will be
- * fixed once we move the sort further into the planner, but for now
- * ... functionality....
- */
-
- sortplan->cost = plannode->cost;
-
- return sortplan;
+ return (Plan *) make_sort(temp_tlist,
+ _NONAME_RELATION_ID_,
+ plannode,
+ keyno);
}
/*
@@ -828,187 +749,3 @@ pg_checkretval(Oid rettype, List *queryTreeList)
/* success */
return;
}
-
-
-/* ----------
- * Support function for get scan direction to omit sortplan
- * ----------
- */
-static TargetEntry *
-get_matching_tle(Plan *plan, Resdom *resdom)
-{
- List *i;
- TargetEntry *tle;
-
- foreach(i, plan->targetlist)
- {
- tle = (TargetEntry *) lfirst(i);
- if (tle->resdom->resno == resdom->resno)
- return tle;
- }
- return NULL;
-}
-
-
-/* ----------
- * Check if a user requested ORDER BY is already satisfied by
- * the choosen index scan.
- *
- * Returns the direction of Index scan to omit sort,
- * if sort is required returns NoMovementScanDirection
- *
- * ----------
- */
-static ScanDirection
-get_dir_to_omit_sortplan(List *sortcls, Plan *plan)
-{
- Relation indexRel;
- IndexScan *indexScan;
- Oid indexId;
- List *i;
- HeapTuple htup;
- Form_pg_index index_tup;
- int key_no = 0;
- ScanDirection dir, nodir = NoMovementScanDirection;
-
- dir = nodir;
- /* ----------
- * Must be an IndexScan
- * ----------
- */
- if (nodeTag(plan) != T_IndexScan)
- return nodir;
-
- indexScan = (IndexScan *) plan;
-
- /* ----------
- * Should not have left- or righttree
- * ----------
- */
- if (plan->lefttree != NULL)
- return nodir;
- if (plan->righttree != NULL)
- return nodir;
-
- /* ----------
- * Must be a single index scan
- * ----------
- */
- if (length(indexScan->indxid) != 1)
- return nodir;
-
- /* ----------
- * Indices can only have up to 8 attributes. So an ORDER BY using
- * more that 8 attributes could never be satisfied by an index.
- * ----------
- */
- if (length(sortcls) > 8)
- return nodir;
-
- /* ----------
- * The choosen Index must be a btree
- * ----------
- */
- indexId = lfirsti(indexScan->indxid);
-
- indexRel = index_open(indexId);
- if (strcmp(nameout(&(indexRel->rd_am->amname)), "btree") != 0)
- {
- heap_close(indexRel);
- return nodir;
- }
- heap_close(indexRel);
-
- /* ----------
- * Fetch the index tuple
- * ----------
- */
- htup = SearchSysCacheTuple(INDEXRELID,
- ObjectIdGetDatum(indexId), 0, 0, 0);
- if (!HeapTupleIsValid(htup))
- elog(ERROR, "cache lookup for index %u failed", indexId);
- index_tup = (Form_pg_index) GETSTRUCT(htup);
-
- /* ----------
- * Check if all the sort clauses match the attributes in the index
- * ----------
- */
- foreach(i, sortcls)
- {
- SortClause *sortcl;
- Resdom *resdom;
- TargetEntry *tle;
- Var *var;
-
- sortcl = (SortClause *) lfirst(i);
-
- resdom = sortcl->resdom;
- tle = get_matching_tle(plan, resdom);
- if (tle == NULL)
- {
- /* ----------
- * Could this happen?
- * ----------
- */
- return nodir;
- }
- if (nodeTag(tle->expr) != T_Var)
- {
- /* ----------
- * The target list expression isn't a var, so it
- * cannot be the indexed attribute
- * ----------
- */
- return nodir;
- }
- var = (Var *) (tle->expr);
-
- if (var->varno != indexScan->scan.scanrelid)
- {
- /* ----------
- * This Var isn't from the scan relation. So it isn't
- * that of the index
- * ----------
- */
- return nodir;
- }
-
- if (var->varattno != index_tup->indkey[key_no])
- {
- /* ----------
- * It isn't the indexed attribute.
- * ----------
- */
- return nodir;
- }
-
- if (oprid(oper("<", resdom->restype, resdom->restype, FALSE)) != sortcl->opoid)
- {
- /* ----------
- * Sort order isn't in ascending order.
- * ----------
- */
- if (ScanDirectionIsForward(dir))
- return nodir;
- dir = BackwardScanDirection;
- }
- else
- {
- /* ----------
- * Sort order is in ascending order.
- * ----------
- */
- if (ScanDirectionIsBackward(dir))
- return nodir;
- dir = ForwardScanDirection;
- }
-
- key_no++;
- }
-
- /* ----------
- * Index matches ORDER BY - sort not required
- * ----------
- */
- return dir;
-}