1 /*-------------------------------------------------------------------------
4 * Routines to plan a single query
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.24 1998/08/07 05:02:19 momjian Exp $
12 *-------------------------------------------------------------------------
14 #include <sys/types.h>
18 #include "nodes/pg_list.h"
19 #include "nodes/plannodes.h"
20 #include "nodes/parsenodes.h"
21 #include "nodes/print.h"
22 #include "nodes/relation.h"
23 #include "nodes/makefuncs.h"
25 #include "optimizer/planmain.h"
26 #include "optimizer/subselect.h"
27 #include "optimizer/internal.h"
28 #include "optimizer/prep.h"
29 #include "optimizer/paths.h"
30 #include "optimizer/clauses.h"
31 #include "optimizer/keys.h"
32 #include "optimizer/tlist.h"
33 #include "optimizer/var.h"
34 #include "optimizer/xfunc.h"
35 #include "optimizer/cost.h"
37 #include "tcop/dest.h"
38 #include "utils/elog.h"
39 #include "utils/palloc.h"
40 #include "nodes/memnodes.h"
41 #include "utils/mcxt.h"
42 #include "utils/lsyscache.h"
44 static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
45 static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
48 make_groupPlan(List **tlist, bool tuplePerGroup,
49 List *groupClause, Plan *subplan);
53 * Routine to create a query plan. It does so by first creating a
54 * subplan for the topmost level of attributes in the query. Then,
55 * it modifies all target list and qualifications to consider the next
56 * level of nesting and creates a plan for this modified query by
57 * recursively calling itself. The two pieces are then merged together
58 * by creating a result node that indicates which attributes should
59 * be placed where and any relation level qualifications to be
62 * command-type is the query command, e.g., retrieve, delete, etc.
63 * tlist is the target list of the query
64 * qual is the qualification of the query
66 * Returns a query plan.
69 query_planner(Query *root,
74 List *constant_qual = NIL;
75 List *var_only_tlist = NIL;
76 List *level_tlist = NIL;
79 if (PlannerQueryLevel > 1)
81 /* should copy be made ? */
82 tlist = (List *) SS_replace_correlation_vars((Node *) tlist);
83 qual = (List *) SS_replace_correlation_vars((Node *) qual);
85 if (root->hasSubLinks)
86 qual = (List *) SS_process_sublinks((Node *) qual);
88 qual = cnfify((Expr *) qual, true);
89 #ifdef OPTIMIZER_DEBUG
90 printf("After cnfify()\n");
95 * A command without a target list or qualification is an error,
96 * except for "delete foo".
98 if (tlist == NIL && qual == NULL)
100 if (command_type == CMD_DELETE ||
103 * Total hack here. I don't know how to handle statements like
104 * notify in action bodies. Notify doesn't return anything but
105 * scans a system table.
107 command_type == CMD_NOTIFY)
109 return ((Plan *) make_seqscan(NIL,
111 root->resultRelation,
115 return ((Plan *) NULL);
119 * Pull out any non-variable qualifications so these can be put in the
120 * topmost result node. The opids for the remaining qualifications
121 * will be changed to regprocs later.
123 qual = pull_constant_clauses(qual, &constant_qual);
124 fix_opids(constant_qual);
127 * Create a target list that consists solely of (resdom var) target
128 * list entries, i.e., contains no arbitrary expressions.
130 var_only_tlist = flatten_tlist(tlist);
132 level_tlist = var_only_tlist;
134 /* from old code. the logic is beyond me. - ay 2/95 */
138 * A query may have a non-variable target list and a non-variable
139 * qualification only under certain conditions: - the query creates
140 * all-new tuples, or - the query is a replace (a scan must still be
141 * done in this case).
143 if (var_only_tlist == NULL && qual == NULL)
146 switch (command_type)
150 return ((Plan *) make_result(tlist,
151 (Node *) constant_qual,
158 SeqScan *scan = make_seqscan(tlist,
160 root->resultRelation,
163 if (constant_qual != NULL)
165 return ((Plan *) make_result(tlist,
166 (Node *) constant_qual,
170 return ((Plan *) scan);
175 return ((Plan *) NULL);
180 * Find the subplan (access path) and destructively modify the target
181 * list of the newly created subplan to contain the appropriate join
184 subplan = subplanner(root, level_tlist, qual);
186 set_tlist_references(subplan);
189 * Build a result node linking the plan if we have constant quals
193 subplan = (Plan *) make_result((!root->hasAggs &&
194 !root->groupClause &&
196 ? tlist : subplan->targetlist,
197 (Node *) constant_qual,
201 * Change all varno's of the Result's node target list.
203 if (!root->hasAggs && !root->groupClause && !root->havingQual)
204 set_tlist_references(subplan);
210 * fix up the flattened target list of the plan root node so that
211 * expressions are evaluated. this forces expression evaluations that
212 * may involve expensive function calls to be delayed to the very last
213 * stage of query execution. this could be bad. but it is joey's
214 * responsibility to optimally push these expressions down the plan
217 * But now nothing to do if there are GroupBy and/or Aggregates: 1.
218 * make_groupPlan fixes tlist; 2. flatten_tlist_vars does nothing with
219 * aggregates fixing only other entries (i.e. - GroupBy-ed and so
220 * fixed by make_groupPlan). - vadim 04/05/97
224 if (!root->hasAggs && !root->groupClause && !root->havingQual)
225 subplan->targetlist = flatten_tlist_vars(tlist,
226 subplan->targetlist);
233 * Destructively modify the query plan's targetlist to add fjoin lists
234 * to flatten functions that return sets of base types
236 subplan->targetlist = generate_fjoin(subplan->targetlist);
244 * Subplanner creates an entire plan consisting of joins and scans
245 * for processing a single level of attributes.
247 * flat-tlist is the flattened target list
248 * qual is the qualification to be satisfied
254 subplanner(Query *root,
258 RelOptInfo *final_relation;
259 List *final_relation_list;
262 * Initialize the targetlist and qualification, adding entries to
263 * *query-relation-list* as relation references are found (e.g., in
264 * the qualification, the targetlist, etc.)
266 root->base_relation_list_ = NIL;
267 root->join_relation_list_ = NIL;
268 initialize_base_rels_list(root, flat_tlist);
269 initialize_base_rels_jinfo(root, qual);
270 add_missing_vars_to_base_rels(root, flat_tlist);
273 * Find all possible scan and join paths. Mark all the clauses and
274 * relations that can be processed using special join methods, then do
275 * the exhaustive path search.
277 initialize_join_clause_info(root->base_relation_list_);
278 final_relation_list = find_paths(root,
279 root->base_relation_list_);
281 if (final_relation_list)
282 final_relation = (RelOptInfo *) lfirst(final_relation_list);
284 final_relation = (RelOptInfo *) NIL;
286 #if 0 /* fix xfunc */
289 * Perform Predicate Migration on each path, to optimize and correctly
290 * assess the cost of each before choosing the cheapest one. -- JMH,
293 * Needn't do so if the top rel is pruneable: that means there's no
294 * expensive functions left to pull up. -- JMH, 11/22/92
296 if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM &&
297 XfuncMode != XFUNC_NOPULL && !final_relation->pruneable)
301 foreach(pathnode, final_relation->pathlist)
303 if (xfunc_do_predmig((Path *) lfirst(pathnode)))
304 set_cheapest(final_relation, final_relation->pathlist);
310 * Determine the cheapest path and create a subplan corresponding to
314 return (create_plan((Path *) final_relation->cheapestpath));
317 elog(NOTICE, "final relation is nil");
318 return (create_plan((Path *) NULL));
323 /*****************************************************************************
325 *****************************************************************************/
328 make_result(List *tlist,
329 Node *resconstantqual,
332 Result *node = makeNode(Result);
333 Plan *plan = &node->plan;
336 tlist = generate_fjoin(tlist);
338 plan->cost = (subplan ? subplan->cost : 0);
339 plan->state = (EState *) NULL;
340 plan->targetlist = tlist;
341 plan->lefttree = subplan;
342 plan->righttree = NULL;
343 node->resconstantqual = resconstantqual;
344 node->resstate = NULL;
349 /*****************************************************************************
351 *****************************************************************************/
354 make_groupPlan(List **tlist,
362 List *glc = listCopy(groupClause);
363 List *otles = NIL; /* list of removed non-GroupBy entries */
364 List *otlvars = NIL; /* list of var in them */
369 AttrNumber *grpColIdx;
372 numCols = length(groupClause);
373 grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
375 sort_tlist = new_unsorted_tlist(*tlist); /* it's copy */
378 * Make template TL for subplan, Sort & Group: 1. If there are
379 * aggregates (tuplePerGroup is true) then take away non-GroupBy
380 * entries and re-set resno-s accordantly. 2. Make grpColIdx
382 * Note: we assume that TLEs in *tlist are ordered in accordance with
383 * their resdom->resno.
385 foreach(sl, sort_tlist)
387 Resdom *resdom = NULL;
388 TargetEntry *te = (TargetEntry *) lfirst(sl);
391 foreach(gl, groupClause)
393 GroupClause *grpcl = (GroupClause *) lfirst(gl);
396 if (grpcl->entry->resdom->resno == te->resdom->resno)
400 resdom->reskey = keyno;
401 resdom->reskeyop = get_opcode(grpcl->grpOpoid);
402 resdom->resno = last_resno; /* re-set */
403 grpColIdx[keyno - 1] = last_resno++;
404 glc = lremove(lfirst(gl), glc); /* TLE found for it */
410 * Non-GroupBy entry: remove it from Group/Sort TL if there are
411 * aggregates in query - it will be evaluated by Aggregate plan
417 otlvars = nconc(otlvars, pull_var_clause(te->expr));
418 otles = lcons(te, otles);
419 sort_tlist = lremove(te, sort_tlist);
422 te->resdom->resno = last_resno++;
426 if (length(glc) != 0)
427 elog(ERROR, "group attribute disappeared from target list");
430 * If non-GroupBy entries were removed from TL - we are to add Vars
431 * for them to the end of TL if there are no such Vars in TL already.
434 otlvcnt = length(otlvars);
437 Var *v = (Var *) lfirst(gl);
439 if (tlist_member(v, sort_tlist) == NULL)
441 sort_tlist = lappend(sort_tlist,
442 create_tl_element(v, last_resno));
449 /* Now otlvcnt is number of Vars added in TL for non-GroupBy entries */
451 /* Make TL for subplan: substitute Vars from subplan TL into new TL */
452 sl = flatten_tlist_vars(sort_tlist, subplan->targetlist);
454 subplan->targetlist = new_unsorted_tlist(sl); /* there */
457 * Make Sort/Group TL : 1. make Var nodes (with varno = 1 and varnoold
458 * = -1) for all functions, 'couse they will be evaluated by subplan;
459 * 2. for real Vars: set varno = 1 and varattno to its resno in
462 foreach(sl, sort_tlist)
464 TargetEntry *te = (TargetEntry *) lfirst(sl);
465 Resdom *resdom = te->resdom;
466 Node *expr = te->expr;
470 #if 0 /* subplanVar->resdom->resno expected to
471 * be = te->resdom->resno */
472 TargetEntry *subplanVar;
474 subplanVar = match_varid((Var *) expr, subplan->targetlist);
475 ((Var *) expr)->varattno = subplanVar->resdom->resno;
477 ((Var *) expr)->varattno = te->resdom->resno;
478 ((Var *) expr)->varno = 1;
481 te->expr = (Node *) makeVar(1, resdom->resno,
484 0, -1, resdom->resno);
487 sortplan = make_sort(sort_tlist,
491 sortplan->plan.cost = subplan->cost; /* XXX assume no cost */
494 * make the Group node
496 sort_tlist = copyObject(sort_tlist);
497 grpplan = make_group(sort_tlist, tuplePerGroup, numCols,
498 grpColIdx, sortplan);
501 * Make TL for parent: "restore" non-GroupBy entries (if they were
502 * removed) and set resno-s of others accordantly.
505 sort_tlist = NIL; /* to be new parent TL */
509 TargetEntry *te = (TargetEntry *) lfirst(gl);
511 foreach(temp, otles) /* Is it removed non-GroupBy entry ? */
513 TargetEntry *ote = (TargetEntry *) lfirst(temp);
515 if (ote->resdom->resno == te->resdom->resno)
517 otles = lremove(ote, otles);
521 if (temp == NIL) /* It's "our" TLE - we're to return */
522 { /* it from Sort/Group plans */
523 TargetEntry *my = (TargetEntry *) lfirst(sl); /* get it */
525 sl = sl->next; /* prepare for the next "our" */
527 my->resdom->resno = te->resdom->resno; /* order of parent TL */
528 sort_tlist = lappend(sort_tlist, my);
531 /* else - it's TLE of an non-GroupBy entry */
532 sort_tlist = lappend(sort_tlist, copyObject(te));
536 * Pure non-GroupBy entries Vars were at the end of Group' TL. They
537 * shouldn't appear in parent TL, all others shouldn't disappear.
539 Assert(otlvcnt == length(sl));
540 Assert(length(otles) == 0);
544 return (Plan *) grpplan;