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.3 1997/04/05 06:37:37 vadim 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/relation.h"
22 #include "nodes/makefuncs.h"
24 #include "optimizer/planmain.h"
25 #include "optimizer/internal.h"
26 #include "optimizer/paths.h"
27 #include "optimizer/clauses.h"
28 #include "optimizer/keys.h"
29 #include "optimizer/tlist.h"
30 #include "optimizer/var.h"
31 #include "optimizer/xfunc.h"
32 #include "optimizer/cost.h"
34 #include "tcop/dest.h"
35 #include "utils/elog.h"
36 #include "utils/palloc.h"
37 #include "nodes/memnodes.h"
38 #include "utils/mcxt.h"
39 #include "utils/lsyscache.h"
41 static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
42 static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
44 static Plan *make_groupPlan(List **tlist, bool tuplePerGroup,
45 List *groupClause, Plan *subplan);
49 * Routine to create a query plan. It does so by first creating a
50 * subplan for the topmost level of attributes in the query. Then,
51 * it modifies all target list and qualifications to consider the next
52 * level of nesting and creates a plan for this modified query by
53 * recursively calling itself. The two pieces are then merged together
54 * by creating a result node that indicates which attributes should
55 * be placed where and any relation level qualifications to be
58 * command-type is the query command, e.g., retrieve, delete, etc.
59 * tlist is the target list of the query
60 * qual is the qualification of the query
62 * Returns a query plan.
65 query_planner(Query *root,
70 List *constant_qual = NIL;
71 List *flattened_tlist = NIL;
72 List *level_tlist = NIL;
73 Plan *subplan = (Plan*)NULL;
77 * A command without a target list or qualification is an error,
78 * except for "delete foo".
80 if (tlist==NIL && qual==NULL) {
81 if (command_type == CMD_DELETE ||
82 /* Total hack here. I don't know how to handle
83 statements like notify in action bodies.
84 Notify doesn't return anything but
85 scans a system table. */
86 command_type == CMD_NOTIFY) {
87 return ((Plan*)make_seqscan(NIL,
96 * Pull out any non-variable qualifications so these can be put in
97 * the topmost result node. The opids for the remaining
98 * qualifications will be changed to regprocs later.
100 qual = pull_constant_clauses(qual, &constant_qual);
101 fix_opids(constant_qual);
104 * Create a target list that consists solely of (resdom var) target
105 * list entries, i.e., contains no arbitrary expressions.
107 flattened_tlist = flatten_tlist(tlist);
108 if (flattened_tlist) {
109 level_tlist = flattened_tlist;
111 /* from old code. the logic is beyond me. - ay 2/95 */
116 * A query may have a non-variable target list and a non-variable
117 * qualification only under certain conditions:
118 * - the query creates all-new tuples, or
119 * - the query is a replace (a scan must still be done in this case).
121 if (flattened_tlist==NULL && qual==NULL) {
123 switch (command_type) {
126 return ((Plan*)make_result(tlist,
127 (Node*)constant_qual,
134 SeqScan *scan = make_seqscan(tlist,
136 root->resultRelation,
138 if (constant_qual!=NULL) {
139 return ((Plan*)make_result(tlist,
140 (Node*)constant_qual,
143 return ((Plan*)scan);
149 return ((Plan*)NULL);
154 * Find the subplan (access path) and destructively modify the
155 * target list of the newly created subplan to contain the appropriate
158 subplan = subplanner(root, level_tlist, qual);
160 set_tlist_references(subplan);
163 * If we have a GROUP BY clause, insert a group node (with the appropriate
166 if (root->groupClause != NULL) {
170 * decide whether how many tuples per group the Group node needs
171 * to return. (Needs only one tuple per group if no aggregate is
172 * present. Otherwise, need every tuple from the group to do the
175 tuplePerGroup = ( root->qry_aggs ) ? TRUE : FALSE;
178 make_groupPlan(&tlist, tuplePerGroup, root->groupClause, subplan);
183 * If aggregate is present, insert the agg node
185 if ( root->qry_aggs )
187 aggplan = make_agg(tlist, root->qry_numAgg, root->qry_aggs);
188 aggplan->plan.lefttree = subplan;
190 * set the varno/attno entries to the appropriate references to
191 * the result tuple of the subplans. (We need to set those in the
192 * array of aggreg's in the Agg node also. Even though they're
193 * pointers, after a few dozen's of copying, they're not the same as
194 * those in the target list.)
196 set_agg_tlist_references (aggplan);
197 set_agg_agglist_references (aggplan);
199 subplan = (Plan*)aggplan;
201 tlist = aggplan->plan.targetlist;
205 * Build a result node linking the plan if we have constant quals
210 plan = (Plan*)make_result(tlist,
211 (Node*)constant_qual,
214 * Change all varno's of the Result's node target list.
216 set_result_tlist_references((Result*)plan);
222 * fix up the flattened target list of the plan root node so that
223 * expressions are evaluated. this forces expression evaluations
224 * that may involve expensive function calls to be delayed to
225 * the very last stage of query execution. this could be bad.
226 * but it is joey's responsibility to optimally push these
227 * expressions down the plan tree. -- Wei
229 * But now nothing to do if there are GroupBy and/or Aggregates:
230 * 1. make_groupPlan fixes tlist; 2. flatten_tlist_vars does nothing
231 * with aggregates fixing only other entries (i.e. - GroupBy-ed and
232 * so fixed by make_groupPlan). - vadim 04/05/97
234 if ( root->groupClause == NULL && aggplan == NULL )
236 subplan->targetlist = flatten_tlist_vars(tlist,
237 subplan->targetlist);
241 * Destructively modify the query plan's targetlist to add fjoin
242 * lists to flatten functions that return sets of base types
244 subplan->targetlist = generate_fjoin(subplan->targetlist);
252 * Subplanner creates an entire plan consisting of joins and scans
253 * for processing a single level of attributes.
255 * flat-tlist is the flattened target list
256 * qual is the qualification to be satisfied
262 subplanner(Query *root,
267 List *final_relation_list;
269 /* Initialize the targetlist and qualification, adding entries to
270 * *query-relation-list* as relation references are found (e.g., in the
271 * qualification, the targetlist, etc.)
273 root->base_relation_list_ = NIL;
274 root->join_relation_list_ = NIL;
275 initialize_base_rels_list(root, flat_tlist);
276 initialize_base_rels_jinfo(root, qual);
277 add_missing_vars_to_base_rels(root, flat_tlist);
279 /* Find all possible scan and join paths.
280 * Mark all the clauses and relations that can be processed using special
281 * join methods, then do the exhaustive path search.
283 initialize_join_clause_info(root->base_relation_list_);
284 final_relation_list = find_paths(root,
285 root->base_relation_list_);
287 if (final_relation_list)
288 final_relation = (Rel*)lfirst (final_relation_list);
290 final_relation = (Rel*)NIL;
292 #if 0 /* fix xfunc */
294 * Perform Predicate Migration on each path, to optimize and correctly
295 * assess the cost of each before choosing the cheapest one.
298 * Needn't do so if the top rel is pruneable: that means there's no
299 * expensive functions left to pull up. -- JMH, 11/22/92
301 if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM &&
302 XfuncMode != XFUNC_NOPULL && !final_relation->pruneable)
305 foreach(pathnode, final_relation->pathlist)
307 if (xfunc_do_predmig((Path*)lfirst(pathnode)))
308 set_cheapest(final_relation, final_relation->pathlist);
314 * Determine the cheapest path and create a subplan corresponding to it.
316 if (final_relation) {
317 return (create_plan ((Path*)final_relation->cheapestpath));
319 elog(NOTICE, "final relation is nil");
320 return(create_plan ((Path*)NULL));
325 /*****************************************************************************
327 *****************************************************************************/
330 make_result(List *tlist,
331 Node *resconstantqual,
334 Result *node = makeNode(Result);
335 Plan *plan = &node->plan;
337 tlist = generate_fjoin(tlist);
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,
361 List *glc = listCopy (groupClause);
362 List *aggvals = NIL; /* list of vars of aggregates */
367 AttrNumber *grpColIdx;
371 numCols = length(groupClause);
372 grpColIdx = (AttrNumber *)palloc(sizeof(AttrNumber)*numCols);
374 sort_tlist = new_unsorted_tlist(*tlist); /* it's copy */
377 * Make template TL for subplan, Sort & Group:
378 * 1. Take away Aggregates and re-set resno-s accordantly.
381 * Note: we assume that TLEs in *tlist are ordered in accordance
382 * with their resdom->resno.
384 foreach (sl, sort_tlist)
386 Resdom *resdom = NULL;
387 TargetEntry *te = (TargetEntry *) lfirst (sl);
391 GroupClause *grpcl = (GroupClause*)lfirst(gl);
393 if ( grpcl->resdom->resno == te->resdom->resno )
397 resdom->reskey = keyno;
398 resdom->reskeyop = get_opcode (grpcl->grpOpoid);
399 resdom->resno = last_resno; /* re-set */
400 grpColIdx[keyno-1] = last_resno++;
402 glc = lremove (lfirst (gl), glc); /* TLE found for it */
406 if ( resdom == NULL ) /* Not GroupBy-ed entry: remove */
407 { /* aggregate(s) from Group/Sort TL */
408 if ( IsA (te->expr, Aggreg) )
409 { /* save Aggregate' Vars */
410 aggvals = nconc (aggvals, pull_var_clause (te->expr));
411 sort_tlist = lremove (lfirst (sl), sort_tlist);
414 resdom->resno = last_resno++; /* re-set */
418 if ( length (glc) != 0 )
420 elog(WARN, "group attribute disappeared from target list");
424 * Aggregates were removed from TL - we are to add Vars for them
425 * to the end of TL if there are no such Vars in TL already.
428 aggvcnt = length (aggvals);
429 foreach (gl, aggvals)
431 Var *v = (Var*)lfirst (gl);
433 if ( tlist_member (v, sort_tlist) == NULL )
435 sort_tlist = lappend (sort_tlist,
436 create_tl_element (v, last_resno));
439 else /* already in TL */
442 /* Now aggvcnt is number of Vars added in TL for Aggregates */
444 /* Make TL for subplan: substitute Vars from subplan TL into new TL */
445 sl = flatten_tlist_vars (sort_tlist, subplan->targetlist);
447 subplan->targetlist = new_unsorted_tlist (sl); /* there */
450 * Make Sort/Group TL :
451 * 1. make Var nodes (with varno = 1 and varnoold = -1) for all
452 * functions, 'couse they will be evaluated by subplan;
453 * 2. for real Vars: set varno = 1 and varattno to its resno in subplan
455 foreach (sl, sort_tlist)
457 TargetEntry *te = (TargetEntry *) lfirst (sl);
458 Resdom *resdom = te->resdom;
459 Node *expr = te->expr;
461 if ( IsA (expr, Var) )
463 #if 0 /* subplanVar->resdom->resno expected to be = te->resdom->resno */
464 TargetEntry *subplanVar;
466 subplanVar = match_varid ((Var*)expr, subplan->targetlist);
467 ((Var*)expr)->varattno = subplanVar->resdom->resno;
469 ((Var*)expr)->varattno = te->resdom->resno;
470 ((Var*)expr)->varno = 1;
473 te->expr = (Node*) makeVar (1, resdom->resno,
478 sortplan = make_sort(sort_tlist,
482 sortplan->plan.cost = subplan->cost; /* XXX assume no cost */
485 * make the Group node
487 sort_tlist = copyObject (sort_tlist);
488 grpplan = make_group(sort_tlist, tuplePerGroup, numCols,
489 grpColIdx, sortplan);
492 * Make TL for parent: "restore" Aggregates and
493 * resno-s of others accordantly.
496 sort_tlist = NIL; /* to be new parent TL */
499 TargetEntry *te = (TargetEntry *) lfirst (gl);
501 if ( !IsA (te->expr, Aggreg) ) /* It's "our" TLE - we're to return */
502 { /* it from Sort/Group plans */
503 TargetEntry *my = (TargetEntry *) lfirst (sl); /* get it */
505 sl = sl->next; /* prepare for the next "our" */
506 my = copyObject (my);
507 my->resdom->resno = te->resdom->resno; /* order of parent TL */
508 sort_tlist = lappend (sort_tlist, my);
511 /* TLE of an aggregate */
512 sort_tlist = lappend (sort_tlist, copyObject(te));
515 * Pure aggregates Vars were at the end of Group' TL.
516 * They shouldn't appear in parent TL, all others shouldn't
519 Assert ( aggvcnt == length (sl) );
523 return (Plan*)grpplan;