diff options
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 102 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 657 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 41 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 6 |
5 files changed, 303 insertions, 512 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 40923fe27da..d1f756fc7c1 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.73 1999/08/18 04:15:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.74 1999/08/21 03:49:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1131,7 +1131,6 @@ make_agg(List *tlist, Plan *lefttree) node->plan.targetlist = tlist; node->plan.lefttree = lefttree; node->plan.righttree = (Plan *) NULL; - node->aggs = NIL; return node; } @@ -1141,15 +1140,15 @@ make_group(List *tlist, bool tuplePerGroup, int ngrp, AttrNumber *grpColIdx, - Sort *lefttree) + Plan *lefttree) { Group *node = makeNode(Group); - copy_costsize(&node->plan, (Plan *) lefttree); + copy_costsize(&node->plan, lefttree); node->plan.state = (EState *) NULL; node->plan.qual = NULL; node->plan.targetlist = tlist; - node->plan.lefttree = (Plan *) lefttree; + node->plan.lefttree = lefttree; node->plan.righttree = (Plan *) NULL; node->tuplePerGroup = tuplePerGroup; node->numCols = ngrp; diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 76e010c9d22..f6f62abfe08 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.40 1999/07/16 04:59:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.41 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,11 +17,13 @@ #include "optimizer/clauses.h" +#include "optimizer/cost.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/subselect.h" #include "optimizer/tlist.h" +#include "utils/lsyscache.h" static Plan *subplanner(Query *root, List *flat_tlist, List *qual); @@ -42,6 +44,13 @@ static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); * tlist is the target list of the query * qual is the qualification of the query * + * Note: the Query node now also includes a query_pathkeys field, which + * signals query_planner that the indicated sort order is wanted in the + * final output plan. If, for some reason, query_planner is unable to + * comply, it sets query_pathkeys to NIL before returning. (The 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. */ Plan * @@ -100,6 +109,8 @@ query_planner(Query *root, */ if (var_only_tlist == NULL && qual == NULL) { + root->query_pathkeys = NIL; /* these plans make unordered results */ + switch (command_type) { case CMD_SELECT: @@ -152,6 +163,8 @@ query_planner(Query *root, */ set_tlist_references(subplan); + root->query_pathkeys = NIL; /* result is unordered, no? */ + return subplan; } @@ -163,15 +176,15 @@ query_planner(Query *root, * responsibility to optimally push these expressions down the plan * tree. -- Wei * - * Note: formerly there was a test here to skip the flatten call if we - * expected union_planner to insert a Group or Agg node above our + * Note: formerly there was a test here to skip the unflatten call if + * we expected union_planner to insert a Group or Agg node above our * result. However, now union_planner tells us exactly what it wants * returned, and we just do it. Much cleaner. */ else { - subplan->targetlist = flatten_tlist_vars(tlist, - subplan->targetlist); + subplan->targetlist = unflatten_tlist(tlist, + subplan->targetlist); return subplan; } @@ -204,6 +217,8 @@ subplanner(Query *root, List *qual) { RelOptInfo *final_rel; + Cost cheapest_cost; + Path *sortedpath; /* * Initialize the targetlist and qualification, adding entries to @@ -244,18 +259,83 @@ subplanner(Query *root, } #endif + if (! final_rel) + { + elog(NOTICE, "final relation is null"); + root->query_pathkeys = NIL; /* result is unordered, no? */ + return create_plan((Path *) NULL); + } + + /* + * Determine the cheapest path and create a subplan to execute it. + * + * If no special sort order is wanted, or if the cheapest path is + * already appropriately ordered, just use the cheapest path. + */ + if (root->query_pathkeys == NIL || + pathkeys_contained_in(root->query_pathkeys, + final_rel->cheapestpath->pathkeys)) + return create_plan(final_rel->cheapestpath); /* - * Determine the cheapest path and create a subplan corresponding to - * it. + * Otherwise, look to see if we have an already-ordered path that is + * cheaper than doing an explicit sort on cheapestpath. */ - if (final_rel) - return create_plan((Path *) final_rel->cheapestpath); + cheapest_cost = final_rel->cheapestpath->path_cost + + cost_sort(root->query_pathkeys, final_rel->size, final_rel->width); + + sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist, + root->query_pathkeys, + false); + if (sortedpath) + { + if (sortedpath->path_cost <= cheapest_cost) + { + /* Found a better presorted path, use it */ + return create_plan(sortedpath); + } + /* otherwise, doing it the hard way is still cheaper */ + } else { - elog(NOTICE, "final relation is null"); - return create_plan((Path *) NULL); + /* + * If we found no usable presorted path at all, it is possible + * that the user asked for descending sort order. Check to see + * if we can satisfy the pathkeys by using a backwards indexscan. + * To do this, we commute all the operators in the pathkeys and + * then look for a matching path that is an IndexPath. + */ + List *commuted_pathkeys = copyObject(root->query_pathkeys); + + if (commute_pathkeys(commuted_pathkeys)) + { + /* pass 'true' to force only IndexPaths to be considered */ + sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist, + commuted_pathkeys, + true); + if (sortedpath && sortedpath->path_cost <= cheapest_cost) + { + /* + * Kluge here: since IndexPath has no representation for + * backwards scan, we have to convert to Plan format and + * then poke the result. + */ + Plan *sortedplan = create_plan(sortedpath); + + Assert(IsA(sortedplan, IndexScan)); + ((IndexScan *) sortedplan)->indxorderdir = BackwardScanDirection; + return sortedplan; + } + } } + /* Nothing for it but to sort the cheapestpath... + * + * we indicate we failed to sort the plan, and let the caller + * stick the appropriate sortplan on top. + */ + root->query_pathkeys = NIL; /* sorry, it ain't sorted */ + + return create_plan(final_rel->cheapestpath); } /***************************************************************************** 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; -} diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 6471ad4e41b..1492df8b030 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.55 1999/08/18 04:15:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,7 +52,6 @@ static void replace_vars_with_subplan_refs(Node *clause, List *subplanTargetList); static bool replace_vars_with_subplan_refs_walker(Node *node, replace_vars_with_subplan_refs_context *context); -static List *pull_agg_clause(Node *clause); static bool pull_agg_clause_walker(Node *node, List **listptr); static bool check_having_for_ungrouped_vars_walker(Node *node, check_having_for_ungrouped_vars_context *context); @@ -416,23 +415,9 @@ replace_vars_with_subplan_refs_walker(Node *node, return false; if (IsA(node, Var)) { - /* - * It could be that this varnode has been created by make_groupplan - * and is already set up to reference the subplan target list. We - * recognize that case by varno = 1, varnoold = -1, varattno = - * varoattno, and varlevelsup = 0. (Probably ought to have an - * explicit flag, but this should do for now.) - */ Var *var = (Var *) node; TargetEntry *subplanVar; - if (var->varno == (Index) 1 && - var->varnoold == ((Index) -1) && - var->varattno == var->varoattno && - var->varlevelsup == 0) - return false; /* OK to leave it alone */ - - /* Otherwise it had better be in the subplan list. */ subplanVar = match_varid(var, context->subplanTargetList); if (!subplanVar) elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list"); @@ -461,7 +446,6 @@ replace_vars_with_subplan_refs_walker(Node *node, * the tuples returned by its left tree subplan. * * If there is a qual list (from a HAVING clause), similarly update * vars in it to point to the subplan target list. - * * Generate the aggNode->aggs list of Aggref nodes contained in the Agg. * * The return value is TRUE if all qual clauses include Aggrefs, or FALSE * if any do not (caller may choose to raise an error condition). @@ -475,7 +459,6 @@ set_agg_tlist_references(Agg *aggNode) bool all_quals_ok; subplanTargetList = aggNode->plan.lefttree->targetlist; - aggNode->aggs = NIL; foreach(tl, aggNode->plan.targetlist) { @@ -484,24 +467,19 @@ set_agg_tlist_references(Agg *aggNode) replace_vars_with_subplan_refs(tle->expr, (Index) 0, subplanTargetList); - aggNode->aggs = nconc(pull_agg_clause(tle->expr), aggNode->aggs); } all_quals_ok = true; foreach(ql, aggNode->plan.qual) { Node *qual = lfirst(ql); - List *qualaggs; replace_vars_with_subplan_refs(qual, (Index) 0, subplanTargetList); - qualaggs = pull_agg_clause(qual); - if (qualaggs == NIL) + if (pull_agg_clause(qual) == NIL) all_quals_ok = false; /* this qual clause has no agg * functions! */ - else - aggNode->aggs = nconc(qualaggs, aggNode->aggs); } return all_quals_ok; @@ -514,7 +492,7 @@ set_agg_tlist_references(Agg *aggNode) * Returns list of Aggref nodes found. Note the nodes themselves are not * copied, only referenced. */ -static List * +List * pull_agg_clause(Node *clause) { List *result = NIL; @@ -545,10 +523,6 @@ pull_agg_clause_walker(Node *node, List **listptr) * exprIsAggOrGroupCol()). But that routine currently does not check subplans, * because the necessary info is not computed until the planner runs. * This ought to be cleaned up someday. - * - * NOTE: the havingClause has been cnf-ified, so AND subclauses have been - * turned into a plain List. Thus, this routine has to cope with List nodes - * where the routine above does not... */ void @@ -588,10 +562,13 @@ check_having_for_ungrouped_vars_walker(Node *node, foreach(gl, context->groupClause) { - Var *groupexpr = get_groupclause_expr(lfirst(gl), - context->targetList); + GroupClause *gcl = lfirst(gl); + Node *groupexpr; - if (var_equal((Var *) thisarg, groupexpr)) + groupexpr = get_sortgroupclause_expr(gcl, + context->targetList); + /* XXX is var_equal correct, or should we use equal()? */ + if (var_equal((Var *) thisarg, (Var *) groupexpr)) { contained_in_group_clause = true; break; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index d0022b4cbc0..188379c9a2d 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -6,7 +6,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.21 1999/07/16 04:59:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.22 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -552,9 +552,7 @@ SS_finalize_plan(Plan *plan) break; case T_Agg: - finalize_primnode_walker((Node *) ((Agg *) plan)->aggs, - &results); - Assert(results.subplans == NIL); + /* XXX Code used to reject subplans in Aggref args; needed?? */ break; case T_SeqScan: |