aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/README21
-rw-r--r--src/backend/optimizer/path/allpaths.c191
-rw-r--r--src/backend/optimizer/path/pathkeys.c6
-rw-r--r--src/backend/optimizer/plan/createplan.c392
-rw-r--r--src/backend/optimizer/plan/planmain.c20
-rw-r--r--src/backend/optimizer/plan/planner.c169
-rw-r--r--src/backend/optimizer/prep/prepunion.c473
-rw-r--r--src/backend/optimizer/util/pathnode.c33
-rw-r--r--src/backend/optimizer/util/relnode.c30
9 files changed, 724 insertions, 611 deletions
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index f0113dfaf50..e60f1457208 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -4,11 +4,11 @@ Summary
These directories take the Query structure returned by the parser, and
generate a plan used by the executor. The /plan directory generates the
actual output plan, the /path code generates all possible ways to join the
-tables, and /prep handles special cases like inheritance. /util is utility
-stuff. /geqo is the separate "genetic optimization" planner --- it does
-a semi-random search through the join tree space, rather than exhaustively
-considering all possible join trees. (But each join considered by /geqo
-is given to /path to create paths for, so we consider all possible
+tables, and /prep handles various preprocessing steps for special cases.
+/util is utility stuff. /geqo is the separate "genetic optimization" planner
+--- it does a semi-random search through the join tree space, rather than
+exhaustively considering all possible join trees. (But each join considered
+by /geqo is given to /path to create paths for, so we consider all possible
implementation paths for each specific join pair even in GEQO mode.)
@@ -210,10 +210,10 @@ planner()
thereby reducing the accuracy of selectivity estimates.
process sublinks
convert Vars of outer query levels into Params
---union_planner()
- handle unions and inheritance by mutual recursion with prepunion.c routines
- preprocess target list
- handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT
+--grouping_planner()
+ preprocess target list for non-SELECT queries
+ handle UNION/INTERSECT/EXCEPT, GROUP BY, HAVING, aggregates,
+ ORDER BY, DISTINCT, LIMIT
--query_planner()
pull out constant quals, which can be used to gate execution of the
whole plan (if any are found, we make a top-level Result node
@@ -239,11 +239,12 @@ planner()
Loop back if this wasn't the top join level.
Back at query_planner:
put back constant quals and non-simplified target list
- Back at union_planner:
+ Back at grouping_planner:
do grouping(GROUP)
do aggregates
make unique(DISTINCT)
make sort(ORDER BY)
+ make limit(LIMIT/OFFSET)
Optimizer Data Structures
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 7e017a746f1..12fc576612f 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.66 2000/10/05 19:11:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.67 2000/11/12 00:36:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/planner.h"
+#include "optimizer/prep.h"
#include "parser/parsetree.h"
@@ -28,7 +29,12 @@ bool enable_geqo = true;
int geqo_rels = DEFAULT_GEQO_RELS;
-static void set_base_rel_pathlist(Query *root);
+static void set_base_rel_pathlists(Query *root);
+static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
+ RangeTblEntry *rte);
+static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
+ RangeTblEntry *rte,
+ List *inheritlist);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
@@ -51,7 +57,7 @@ make_one_rel(Query *root)
/*
* Generate access paths for the base rels.
*/
- set_base_rel_pathlist(root);
+ set_base_rel_pathlists(root);
/*
* Generate access paths for the entire join tree.
@@ -69,23 +75,26 @@ make_one_rel(Query *root)
}
/*
- * set_base_rel_pathlist
+ * set_base_rel_pathlists
* Finds all paths available for scanning each base-relation entry.
* Sequential scan and any available indices are considered.
* Each useful path is attached to its relation's 'pathlist' field.
*/
static void
-set_base_rel_pathlist(Query *root)
+set_base_rel_pathlists(Query *root)
{
List *rellist;
foreach(rellist, root->base_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
+ Index rti;
RangeTblEntry *rte;
+ List *inheritlist;
Assert(length(rel->relids) == 1); /* better be base rel */
- rte = rt_fetch(lfirsti(rel->relids), root->rtable);
+ rti = lfirsti(rel->relids);
+ rte = rt_fetch(rti, root->rtable);
if (rel->issubquery)
{
@@ -109,47 +118,163 @@ set_base_rel_pathlist(Query *root)
/* Generate appropriate path */
add_path(rel, create_subqueryscan_path(rel));
+
+ /* Select cheapest path (pretty easy in this case...) */
+ set_cheapest(rel);
+ }
+ else if ((inheritlist = expand_inherted_rtentry(root, rti)) != NIL)
+ {
+ /* Relation is root of an inheritance tree, process specially */
+ set_inherited_rel_pathlist(root, rel, rte, inheritlist);
}
else
{
/* Plain relation */
- List *indices = find_secondary_indexes(rte->relid);
+ set_plain_rel_pathlist(root, rel, rte);
+ }
+ }
+}
- /* Mark rel with estimated output rows, width, etc */
- set_baserel_size_estimates(root, rel);
+/*
+ * set_plain_rel_pathlist
+ * Build access paths for a plain relation (no subquery, no inheritance)
+ */
+static void
+set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ List *indices = find_secondary_indexes(rte->relid);
- /*
- * Generate paths and add them to the rel's pathlist.
- *
- * Note: add_path() will discard any paths that are dominated by
- * another available path, keeping only those paths that are
- * superior along at least one dimension of cost or sortedness.
- */
+ /* Mark rel with estimated output rows, width, etc */
+ set_baserel_size_estimates(root, rel);
- /* Consider sequential scan */
- add_path(rel, create_seqscan_path(rel));
+ /*
+ * Generate paths and add them to the rel's pathlist.
+ *
+ * Note: add_path() will discard any paths that are dominated by
+ * another available path, keeping only those paths that are
+ * superior along at least one dimension of cost or sortedness.
+ */
- /* Consider TID scans */
- create_tidscan_paths(root, rel);
+ /* Consider sequential scan */
+ add_path(rel, create_seqscan_path(rel));
- /* Consider index paths for both simple and OR index clauses */
- create_index_paths(root, rel, indices,
- rel->baserestrictinfo,
- rel->joininfo);
+ /* Consider TID scans */
+ create_tidscan_paths(root, rel);
- /*
- * Note: create_or_index_paths depends on create_index_paths to
- * have marked OR restriction clauses with relevant indices; this
- * is why it doesn't need to be given the list of indices.
- */
- create_or_index_paths(root, rel, rel->baserestrictinfo);
- }
+ /* Consider index paths for both simple and OR index clauses */
+ create_index_paths(root, rel, indices,
+ rel->baserestrictinfo,
+ rel->joininfo);
+
+ /*
+ * Note: create_or_index_paths depends on create_index_paths to
+ * have marked OR restriction clauses with relevant indices; this
+ * is why it doesn't need to be given the list of indices.
+ */
+ create_or_index_paths(root, rel, rel->baserestrictinfo);
+
+ /* Now find the cheapest of the paths for this rel */
+ set_cheapest(rel);
+}
+
+/*
+ * set_inherited_rel_pathlist
+ * Build access paths for a inheritance tree rooted at rel
+ *
+ * inheritlist is a list of RT indexes of all tables in the inheritance tree,
+ * including the parent itself. Note we will not come here unless there's
+ * at least one child in addition to the parent.
+ */
+static void
+set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
+ List *inheritlist)
+{
+ int parentRTindex = lfirsti(rel->relids);
+ Oid parentOID = rte->relid;
+ List *subpaths = NIL;
+ List *il;
+
+ /*
+ * XXX for now, can't handle inherited expansion of FOR UPDATE;
+ * can we do better?
+ */
+ if (intMember(parentRTindex, root->rowMarks))
+ elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
- /* Now find the cheapest of the paths for this rel */
- set_cheapest(rel);
+ /*
+ * Recompute size estimates for whole inheritance tree
+ */
+ rel->rows = 0;
+ rel->width = 0;
+
+ /*
+ * Generate access paths for each table in the tree (parent AND children),
+ * and pick the cheapest path for each table.
+ */
+ foreach(il, inheritlist)
+ {
+ int childRTindex = lfirsti(il);
+ RangeTblEntry *childrte;
+ Oid childOID;
+ RelOptInfo *childrel;
+
+ childrte = rt_fetch(childRTindex, root->rtable);
+ childOID = childrte->relid;
+
+ /*
+ * Make a RelOptInfo for the child so we can do planning. Do NOT
+ * attach the RelOptInfo to the query's base_rel_list, however.
+ *
+ * NOTE: when childRTindex == parentRTindex, we create a second
+ * RelOptInfo for the same relation. This RelOptInfo will represent
+ * the parent table alone, whereas the original RelOptInfo represents
+ * the union of the inheritance tree members.
+ */
+ childrel = make_base_rel(root, childRTindex);
+
+ /*
+ * Copy the parent's targetlist and restriction quals to the child,
+ * with attribute-number adjustment if needed. We don't bother
+ * to copy the join quals, since we can't do any joining here.
+ */
+ childrel->targetlist = (List *)
+ adjust_inherited_attrs((Node *) rel->targetlist,
+ parentRTindex,
+ parentOID,
+ childRTindex,
+ childOID);
+ childrel->baserestrictinfo = (List *)
+ adjust_inherited_attrs((Node *) rel->baserestrictinfo,
+ parentRTindex,
+ parentOID,
+ childRTindex,
+ childOID);
+ childrel->baserestrictcost = rel->baserestrictcost;
+
+ /*
+ * Now compute child access paths, and save the cheapest.
+ */
+ set_plain_rel_pathlist(root, childrel, childrte);
+
+ subpaths = lappend(subpaths, childrel->cheapest_total_path);
+
+ /* Also update total size estimates */
+ rel->rows += childrel->rows;
+ if (childrel->width > rel->width)
+ rel->width = childrel->width;
}
+
+ /*
+ * Finally, build Append path and install it as the only access
+ * path for the parent rel.
+ */
+ add_path(rel, (Path *) create_append_path(rel, subpaths));
+
+ /* Select cheapest path (pretty easy in this case...) */
+ set_cheapest(rel);
}
+
/*
* make_fromexpr_rel
* Build access paths for a FromExpr jointree node.
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 820492bd186..f94d2e4037b 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.26 2000/09/29 18:21:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.27 2000/11/12 00:36:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -566,8 +566,8 @@ build_join_pathkeys(List *outer_pathkeys,
* NB: the result is NOT in canonical form, but must be passed through
* canonicalize_pathkeys() before it can be used for comparisons or
* labeling relation sort orders. (We do things this way because
- * union_planner needs to be able to construct requested pathkeys before
- * the pathkey equivalence sets have been created for the query.)
+ * grouping_planner needs to be able to construct requested pathkeys
+ * before the pathkey equivalence sets have been created for the query.)
*
* 'sortclauses' is a list of SortClause or GroupClause nodes
* 'tlist' is the targetlist to find the referenced tlist entries in
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index a865da61b92..4069ed66e58 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.99 2000/10/26 21:36:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.100 2000/11/12 00:36:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,35 +32,38 @@
#include "utils/syscache.h"
-static List *switch_outer(List *clauses);
-static Scan *create_scan_node(Query *root, Path *best_path, List *tlist);
-static Join *create_join_node(Query *root, JoinPath *best_path, List *tlist);
-static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
+static Scan *create_scan_plan(Query *root, Path *best_path);
+static Join *create_join_plan(Query *root, JoinPath *best_path);
+static Append *create_append_plan(Query *root, AppendPath *best_path);
+static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
List *scan_clauses);
-static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path,
+static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
List *tlist, List *scan_clauses);
-static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
+static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses);
-static SubqueryScan *create_subqueryscan_node(Path *best_path,
+static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
-static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
+static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
- Plan *outer_node, List *outer_tlist,
- Plan *inner_node, List *inner_tlist);
-static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
+ Plan *outer_plan, List *outer_tlist,
+ Plan *inner_plan, List *inner_tlist);
+static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
- Plan *outer_node, List *outer_tlist,
- Plan *inner_node, List *inner_tlist);
-static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
+ Plan *outer_plan, List *outer_tlist,
+ Plan *inner_plan, List *inner_tlist);
+static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
- Plan *outer_node, List *outer_tlist,
- Plan *inner_node, List *inner_tlist);
+ Plan *outer_plan, List *outer_tlist,
+ Plan *inner_plan, List *inner_tlist);
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
Form_pg_index index);
static Node *fix_indxqual_operand(Node *node, int baserelid,
Form_pg_index index,
Oid *opclass);
+static List *switch_outer(List *clauses);
+static void copy_path_costsize(Plan *dest, Path *src);
+static void copy_plan_costsize(Plan *dest, Plan *src);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual,
@@ -83,7 +86,6 @@ static MergeJoin *make_mergejoin(List *tlist,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
-static void copy_path_costsize(Plan *dest, Path *src);
/*
* create_plan
@@ -98,13 +100,12 @@ static void copy_path_costsize(Plan *dest, Path *src);
*
* best_path is the best access path
*
- * Returns the access plan.
+ * Returns a Plan tree.
*/
Plan *
create_plan(Query *root, Path *best_path)
{
- List *tlist = best_path->parent->targetlist;
- Plan *plan_node = (Plan *) NULL;
+ Plan *plan;
switch (best_path->pathtype)
{
@@ -112,18 +113,22 @@ create_plan(Query *root, Path *best_path)
case T_SeqScan:
case T_TidScan:
case T_SubqueryScan:
- plan_node = (Plan *) create_scan_node(root, best_path, tlist);
+ plan = (Plan *) create_scan_plan(root, best_path);
break;
case T_HashJoin:
case T_MergeJoin:
case T_NestLoop:
- plan_node = (Plan *) create_join_node(root,
- (JoinPath *) best_path,
- tlist);
+ plan = (Plan *) create_join_plan(root,
+ (JoinPath *) best_path);
+ break;
+ case T_Append:
+ plan = (Plan *) create_append_plan(root,
+ (AppendPath *) best_path);
break;
default:
elog(ERROR, "create_plan: unknown pathtype %d",
best_path->pathtype);
+ plan = NULL; /* keep compiler quiet */
break;
}
@@ -131,30 +136,29 @@ create_plan(Query *root, Path *best_path)
/* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
if (XfuncMode != XFUNC_OFF)
{
- set_qpqual((Plan) plan_node,
- lisp_qsort(get_qpqual((Plan) plan_node),
+ set_qpqual((Plan) plan,
+ lisp_qsort(get_qpqual((Plan) plan),
xfunc_clause_compare));
if (XfuncMode != XFUNC_NOR)
/* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
- xfunc_disjunct_sort(plan_node->qpqual);
+ xfunc_disjunct_sort(plan->qpqual);
}
#endif
- return plan_node;
+ return plan;
}
/*
- * create_scan_node
- * Create a scan path for the parent relation of 'best_path'.
+ * create_scan_plan
+ * Create a scan plan for the parent relation of 'best_path'.
*
- * tlist is the targetlist for the base relation scanned by 'best_path'
- *
- * Returns the scan node.
+ * Returns a Plan node.
*/
static Scan *
-create_scan_node(Query *root, Path *best_path, List *tlist)
+create_scan_plan(Query *root, Path *best_path)
{
- Scan *node = NULL;
+ Scan *plan;
+ List *tlist = best_path->parent->targetlist;
List *scan_clauses;
/*
@@ -166,65 +170,64 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
switch (best_path->pathtype)
{
case T_SeqScan:
- node = (Scan *) create_seqscan_node(best_path,
+ plan = (Scan *) create_seqscan_plan(best_path,
tlist,
scan_clauses);
break;
case T_IndexScan:
- node = (Scan *) create_indexscan_node(root,
+ plan = (Scan *) create_indexscan_plan(root,
(IndexPath *) best_path,
tlist,
scan_clauses);
break;
case T_TidScan:
- node = (Scan *) create_tidscan_node((TidPath *) best_path,
+ plan = (Scan *) create_tidscan_plan((TidPath *) best_path,
tlist,
scan_clauses);
break;
case T_SubqueryScan:
- node = (Scan *) create_subqueryscan_node(best_path,
+ plan = (Scan *) create_subqueryscan_plan(best_path,
tlist,
scan_clauses);
break;
default:
- elog(ERROR, "create_scan_node: unknown node type: %d",
+ elog(ERROR, "create_scan_plan: unknown node type: %d",
best_path->pathtype);
+ plan = NULL; /* keep compiler quiet */
break;
}
- return node;
+ return plan;
}
/*
- * create_join_node
- * Create a join path for 'best_path' and(recursively) paths for its
+ * create_join_plan
+ * Create a join plan for 'best_path' and (recursively) plans for its
* inner and outer paths.
*
- * 'tlist' is the targetlist for the join relation corresponding to
- * 'best_path'
- *
- * Returns the join node.
+ * Returns a Plan node.
*/
static Join *
-create_join_node(Query *root, JoinPath *best_path, List *tlist)
+create_join_plan(Query *root, JoinPath *best_path)
{
- Plan *outer_node;
+ List *join_tlist = best_path->path.parent->targetlist;
+ Plan *outer_plan;
List *outer_tlist;
- Plan *inner_node;
+ Plan *inner_plan;
List *inner_tlist;
List *joinclauses;
List *otherclauses;
- Join *retval = NULL;
+ Join *plan;
- outer_node = create_plan(root, best_path->outerjoinpath);
- outer_tlist = outer_node->targetlist;
+ outer_plan = create_plan(root, best_path->outerjoinpath);
+ outer_tlist = outer_plan->targetlist;
- inner_node = create_plan(root, best_path->innerjoinpath);
- inner_tlist = inner_node->targetlist;
+ inner_plan = create_plan(root, best_path->innerjoinpath);
+ inner_tlist = inner_plan->targetlist;
if (IS_OUTER_JOIN(best_path->jointype))
{
@@ -241,38 +244,40 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
switch (best_path->path.pathtype)
{
case T_MergeJoin:
- retval = (Join *) create_mergejoin_node((MergePath *) best_path,
- tlist,
- joinclauses,
- otherclauses,
- outer_node,
- outer_tlist,
- inner_node,
- inner_tlist);
+ plan = (Join *) create_mergejoin_plan((MergePath *) best_path,
+ join_tlist,
+ joinclauses,
+ otherclauses,
+ outer_plan,
+ outer_tlist,
+ inner_plan,
+ inner_tlist);
break;
case T_HashJoin:
- retval = (Join *) create_hashjoin_node((HashPath *) best_path,
- tlist,
- joinclauses,
- otherclauses,
- outer_node,
- outer_tlist,
- inner_node,
- inner_tlist);
+ plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
+ join_tlist,
+ joinclauses,
+ otherclauses,
+ outer_plan,
+ outer_tlist,
+ inner_plan,
+ inner_tlist);
break;
case T_NestLoop:
- retval = (Join *) create_nestloop_node((NestPath *) best_path,
- tlist,
- joinclauses,
- otherclauses,
- outer_node,
- outer_tlist,
- inner_node,
- inner_tlist);
+ plan = (Join *) create_nestloop_plan((NestPath *) best_path,
+ join_tlist,
+ joinclauses,
+ otherclauses,
+ outer_plan,
+ outer_tlist,
+ inner_plan,
+ inner_tlist);
break;
default:
- elog(ERROR, "create_join_node: unknown node type: %d",
+ elog(ERROR, "create_join_plan: unknown node type: %d",
best_path->path.pathtype);
+ plan = NULL; /* keep compiler quiet */
+ break;
}
#ifdef NOT_USED
@@ -283,14 +288,42 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
* JMH, 6/15/92
*/
if (get_loc_restrictinfo(best_path) != NIL)
- set_qpqual((Plan) retval,
- nconc(get_qpqual((Plan) retval),
+ set_qpqual((Plan) plan,
+ nconc(get_qpqual((Plan) plan),
get_actual_clauses(get_loc_restrictinfo(best_path))));
#endif
- return retval;
+ return plan;
}
+/*
+ * create_append_plan
+ * Create an Append plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ *
+ * Returns a Plan node.
+ */
+static Append *
+create_append_plan(Query *root, AppendPath *best_path)
+{
+ Append *plan;
+ List *tlist = best_path->path.parent->targetlist;
+ List *subplans = NIL;
+ List *subpaths;
+
+ foreach(subpaths, best_path->subpaths)
+ {
+ Path *subpath = (Path *) lfirst(subpaths);
+
+ subplans = lappend(subplans, create_plan(root, subpath));
+ }
+
+ plan = make_append(subplans, false, tlist);
+
+ return plan;
+}
+
+
/*****************************************************************************
*
* BASE-RELATION SCAN METHODS
@@ -299,14 +332,14 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
/*
- * create_seqscan_node
- * Returns a seqscan node for the base relation scanned by 'best_path'
+ * create_seqscan_plan
+ * Returns a seqscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static SeqScan *
-create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
+create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{
- SeqScan *scan_node;
+ SeqScan *scan_plan;
Index scan_relid;
/* there should be exactly one base rel involved... */
@@ -315,18 +348,18 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->parent->relids);
- scan_node = make_seqscan(tlist,
+ scan_plan = make_seqscan(tlist,
scan_clauses,
scan_relid);
- copy_path_costsize(&scan_node->plan, best_path);
+ copy_path_costsize(&scan_plan->plan, best_path);
- return scan_node;
+ return scan_plan;
}
/*
- * create_indexscan_node
- * Returns a indexscan node for the base relation scanned by 'best_path'
+ * create_indexscan_plan
+ * Returns a indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*
* The indexqual of the path contains a sublist of implicitly-ANDed qual
@@ -338,7 +371,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
* scan.
*/
static IndexScan *
-create_indexscan_node(Query *root,
+create_indexscan_plan(Query *root,
IndexPath *best_path,
List *tlist,
List *scan_clauses)
@@ -348,7 +381,7 @@ create_indexscan_node(Query *root,
List *qpqual;
List *fixed_indxqual;
List *ixid;
- IndexScan *scan_node;
+ IndexScan *scan_plan;
bool lossy = false;
/* there should be exactly one base rel involved... */
@@ -433,7 +466,7 @@ create_indexscan_node(Query *root,
*/
fixed_indxqual = fix_indxqual_references(indxqual, best_path);
- scan_node = make_indexscan(tlist,
+ scan_plan = make_indexscan(tlist,
qpqual,
baserelid,
best_path->indexid,
@@ -441,22 +474,22 @@ create_indexscan_node(Query *root,
indxqual,
best_path->indexscandir);
- copy_path_costsize(&scan_node->scan.plan, &best_path->path);
+ copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
/* use the indexscan-specific rows estimate, not the parent rel's */
- scan_node->scan.plan.plan_rows = best_path->rows;
+ scan_plan->scan.plan.plan_rows = best_path->rows;
- return scan_node;
+ return scan_plan;
}
/*
- * create_tidscan_node
- * Returns a tidscan node for the base relation scanned by 'best_path'
+ * create_tidscan_plan
+ * Returns a tidscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static TidScan *
-create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
+create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
{
- TidScan *scan_node;
+ TidScan *scan_plan;
Index scan_relid;
/* there should be exactly one base rel involved... */
@@ -465,28 +498,28 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->path.parent->relids);
- scan_node = make_tidscan(tlist,
+ scan_plan = make_tidscan(tlist,
scan_clauses,
scan_relid,
best_path->tideval);
if (best_path->unjoined_relids)
- scan_node->needRescan = true;
+ scan_plan->needRescan = true;
- copy_path_costsize(&scan_node->scan.plan, &best_path->path);
+ copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
- return scan_node;
+ return scan_plan;
}
/*
- * create_subqueryscan_node
- * Returns a subqueryscan node for the base relation scanned by 'best_path'
+ * create_subqueryscan_plan
+ * Returns a subqueryscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static SubqueryScan *
-create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
+create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{
- SubqueryScan *scan_node;
+ SubqueryScan *scan_plan;
Index scan_relid;
/* there should be exactly one base rel involved... */
@@ -496,14 +529,12 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->parent->relids);
- scan_node = make_subqueryscan(tlist,
+ scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
best_path->parent->subplan);
- copy_path_costsize(&scan_node->scan.plan, best_path);
-
- return scan_node;
+ return scan_plan;
}
/*****************************************************************************
@@ -528,18 +559,18 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
*****************************************************************************/
static NestLoop *
-create_nestloop_node(NestPath *best_path,
+create_nestloop_plan(NestPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
- Plan *outer_node,
+ Plan *outer_plan,
List *outer_tlist,
- Plan *inner_node,
+ Plan *inner_plan,
List *inner_tlist)
{
- NestLoop *join_node;
+ NestLoop *join_plan;
- if (IsA(inner_node, IndexScan))
+ if (IsA(inner_plan, IndexScan))
{
/*
@@ -563,7 +594,7 @@ create_nestloop_node(NestPath *best_path,
* and therefore has not itself done join_references renumbering
* of the vars in its quals.
*/
- IndexScan *innerscan = (IndexScan *) inner_node;
+ IndexScan *innerscan = (IndexScan *) inner_plan;
List *indxqualorig = innerscan->indxqualorig;
/* No work needed if indxqual refers only to its own relation... */
@@ -591,23 +622,23 @@ create_nestloop_node(NestPath *best_path,
NIL,
innerrel);
/* fix the inner qpqual too, if it has join clauses */
- if (NumRelids((Node *) inner_node->qual) > 1)
- inner_node->qual = join_references(inner_node->qual,
+ if (NumRelids((Node *) inner_plan->qual) > 1)
+ inner_plan->qual = join_references(inner_plan->qual,
outer_tlist,
NIL,
innerrel);
}
}
- else if (IsA(inner_node, TidScan))
+ else if (IsA(inner_plan, TidScan))
{
- TidScan *innerscan = (TidScan *) inner_node;
+ TidScan *innerscan = (TidScan *) inner_plan;
innerscan->tideval = join_references(innerscan->tideval,
outer_tlist,
inner_tlist,
innerscan->scan.scanrelid);
}
- else if (IsA_Join(inner_node))
+ else if (IsA_Join(inner_plan))
{
/*
@@ -617,8 +648,8 @@ create_nestloop_node(NestPath *best_path,
* join --- how can we estimate whether this is a good thing to
* do?
*/
- inner_node = (Plan *) make_material(inner_tlist,
- inner_node);
+ inner_plan = (Plan *) make_material(inner_tlist,
+ inner_plan);
}
/*
@@ -633,30 +664,30 @@ create_nestloop_node(NestPath *best_path,
inner_tlist,
(Index) 0);
- join_node = make_nestloop(tlist,
+ join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
- outer_node,
- inner_node,
+ outer_plan,
+ inner_plan,
best_path->jointype);
- copy_path_costsize(&join_node->join.plan, &best_path->path);
+ copy_path_costsize(&join_plan->join.plan, &best_path->path);
- return join_node;
+ return join_plan;
}
static MergeJoin *
-create_mergejoin_node(MergePath *best_path,
+create_mergejoin_plan(MergePath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
- Plan *outer_node,
+ Plan *outer_plan,
List *outer_tlist,
- Plan *inner_node,
+ Plan *inner_plan,
List *inner_tlist)
{
List *mergeclauses;
- MergeJoin *join_node;
+ MergeJoin *join_plan;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
@@ -692,15 +723,15 @@ create_mergejoin_node(MergePath *best_path,
* necessary. The sort cost was already accounted for in the path.
*/
if (best_path->outersortkeys)
- outer_node = (Plan *)
+ outer_plan = (Plan *)
make_sort_from_pathkeys(outer_tlist,
- outer_node,
+ outer_plan,
best_path->outersortkeys);
if (best_path->innersortkeys)
- inner_node = (Plan *)
+ inner_plan = (Plan *)
make_sort_from_pathkeys(inner_tlist,
- inner_node,
+ inner_plan,
best_path->innersortkeys);
/*
@@ -723,7 +754,7 @@ create_mergejoin_node(MergePath *best_path,
* This check must agree with ExecMarkPos/ExecRestrPos in
* executor/execAmi.c!
*/
- switch (nodeTag(inner_node))
+ switch (nodeTag(inner_plan))
{
case T_SeqScan:
case T_IndexScan:
@@ -734,40 +765,40 @@ create_mergejoin_node(MergePath *best_path,
default:
/* Ooops, need to materialize the inner plan */
- inner_node = (Plan *) make_material(inner_tlist,
- inner_node);
+ inner_plan = (Plan *) make_material(inner_tlist,
+ inner_plan);
break;
}
/*
* Now we can build the mergejoin node.
*/
- join_node = make_mergejoin(tlist,
+ join_plan = make_mergejoin(tlist,
joinclauses,
otherclauses,
mergeclauses,
- outer_node,
- inner_node,
+ outer_plan,
+ inner_plan,
best_path->jpath.jointype);
- copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
+ copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
- return join_node;
+ return join_plan;
}
static HashJoin *
-create_hashjoin_node(HashPath *best_path,
+create_hashjoin_plan(HashPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
- Plan *outer_node,
+ Plan *outer_plan,
List *outer_tlist,
- Plan *inner_node,
+ Plan *inner_plan,
List *inner_tlist)
{
List *hashclauses;
- HashJoin *join_node;
- Hash *hash_node;
+ HashJoin *join_plan;
+ Hash *hash_plan;
Node *innerhashkey;
/*
@@ -811,18 +842,18 @@ create_hashjoin_node(HashPath *best_path,
/*
* Build the hash node and hash join node.
*/
- hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
- join_node = make_hashjoin(tlist,
+ hash_plan = make_hash(inner_tlist, innerhashkey, inner_plan);
+ join_plan = make_hashjoin(tlist,
joinclauses,
otherclauses,
hashclauses,
- outer_node,
- (Plan *) hash_node,
+ outer_plan,
+ (Plan *) hash_plan,
best_path->jpath.jointype);
- copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
+ copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
- return join_node;
+ return join_plan;
}
@@ -1106,7 +1137,7 @@ copy_path_costsize(Plan *dest, Path *src)
* but it helps produce more reasonable-looking EXPLAIN output.
* (Some callers alter the info after copying it.)
*/
-void
+static void
copy_plan_costsize(Plan *dest, Plan *src)
{
if (src)
@@ -1128,6 +1159,10 @@ copy_plan_costsize(Plan *dest, Plan *src)
/*****************************************************************************
*
+ * PLAN NODE BUILDING ROUTINES
+ *
+ * Some of these are exported because they are called to build plan nodes
+ * in contexts where we're not deriving the plan node from a path node.
*
*****************************************************************************/
@@ -1212,7 +1247,7 @@ make_subqueryscan(List *qptlist,
SubqueryScan *node = makeNode(SubqueryScan);
Plan *plan = &node->scan.plan;
- /* cost should be inserted by caller */
+ copy_plan_costsize(plan, subplan);
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
@@ -1225,6 +1260,39 @@ make_subqueryscan(List *qptlist,
return node;
}
+Append *
+make_append(List *appendplans, bool isTarget, List *tlist)
+{
+ Append *node = makeNode(Append);
+ Plan *plan = &node->plan;
+ List *subnode;
+
+ /* compute costs from subplan costs */
+ plan->startup_cost = 0;
+ plan->total_cost = 0;
+ plan->plan_rows = 0;
+ plan->plan_width = 0;
+ foreach(subnode, appendplans)
+ {
+ Plan *subplan = (Plan *) lfirst(subnode);
+
+ if (subnode == appendplans) /* first node? */
+ plan->startup_cost = subplan->startup_cost;
+ plan->total_cost += subplan->total_cost;
+ plan->plan_rows += subplan->plan_rows;
+ if (plan->plan_width < subplan->plan_width)
+ plan->plan_width = subplan->plan_width;
+ }
+ plan->state = (EState *) NULL;
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->appendplans = appendplans;
+ node->isTarget = isTarget;
+
+ return node;
+}
static NestLoop *
make_nestloop(List *tlist,
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index a9747b32799..1a923a506ff 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.61 2000/10/05 19:11:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.62 2000/11/12 00:36:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -65,7 +65,8 @@ static Plan *subplanner(Query *root, List *flat_tlist,
* tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
* expected to be retrieved (ie, a LIMIT specification)
* Note that while this routine and its subroutines treat a negative
- * tuple_fraction the same as 0, union_planner has a different interpretation.
+ * tuple_fraction the same as 0, grouping_planner has a different
+ * interpretation.
*
* Returns a query plan.
*--------------------
@@ -125,9 +126,16 @@ query_planner(Query *root,
subplan = subplanner(root, var_only_tlist, tuple_fraction);
/*
- * Build a result node to control the plan if we have constant quals.
+ * Build a result node to control the plan if we have constant quals,
+ * or if the top-level plan node is one that cannot do expression
+ * evaluation (it won't be able to evaluate the requested tlist).
+ * Currently, the only plan node we might see here that falls into
+ * that category is Append.
+ *
+ * XXX future improvement: if the given tlist is flat anyway, we don't
+ * really need a Result node.
*/
- if (constant_quals)
+ if (constant_quals || IsA(subplan, Append))
{
/*
@@ -325,8 +333,8 @@ subplanner(Query *root,
/*
* Nothing for it but to sort the cheapest-total-cost path --- but we
- * let the caller do that. union_planner has to be able to add a sort
- * node anyway, so no need for extra code here. (Furthermore, the
+ * let the caller do that. grouping_planner has to be able to add a
+ * sort node anyway, so no need for extra code here. (Furthermore, the
* given pathkeys might involve something we can't compute here, such
* as an aggregate function...)
*/
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 67089e68d29..7a1151f0c9a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.95 2000/11/09 02:46:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.96 2000/11/12 00:36:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,6 +43,8 @@ static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
static Node *preprocess_jointree(Query *parse, Node *jtnode);
static Node *preprocess_expression(Query *parse, Node *expr, int kind);
static void preprocess_qual_conditions(Query *parse, Node *jtnode);
+static Plan *inheritance_planner(Query *parse, List *inheritlist);
+static Plan *grouping_planner(Query *parse, double tuple_fraction);
static List *make_subplanTargetList(Query *parse, List *tlist,
AttrNumber **groupColIdx);
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
@@ -65,7 +67,7 @@ planner(Query *parse)
/*
* The planner can be called recursively (an example is when
- * eval_const_expressions tries to simplify an SQL function).
+ * eval_const_expressions tries to pre-evaluate an SQL function).
* So, these global state variables must be saved and restored.
*
* These vars cannot be moved into the Query structure since their
@@ -109,11 +111,14 @@ planner(Query *parse)
*
* parse is the querytree produced by the parser & rewriter.
* tuple_fraction is the fraction of tuples we expect will be retrieved.
- * tuple_fraction is interpreted as explained for union_planner, below.
+ * tuple_fraction is interpreted as explained for grouping_planner, below.
*
* Basically, this routine does the stuff that should only be done once
- * per Query object. It then calls union_planner, which may be called
- * recursively on the same Query node in order to handle inheritance.
+ * per Query object. It then calls grouping_planner. At one time,
+ * grouping_planner could be invoked recursively on the same Query object;
+ * that's not currently true, but we keep the separation between the two
+ * routines anyway, in case we need it again someday.
+ *
* subquery_planner will be called recursively to handle sub-Query nodes
* found within the query's expressions and rangetable.
*
@@ -164,7 +169,7 @@ subquery_planner(Query *parse, double tuple_fraction)
}
/*
- * Do preprocessing on targetlist and quals.
+ * Do expression preprocessing on targetlist and quals.
*/
parse->targetList = (List *)
preprocess_expression(parse, (Node *) parse->targetList,
@@ -176,17 +181,14 @@ subquery_planner(Query *parse, double tuple_fraction)
EXPRKIND_HAVING);
/*
- * Do the main planning (potentially recursive for inheritance)
- */
- plan = union_planner(parse, tuple_fraction);
-
- /*
- * XXX should any more of union_planner's activity be moved here?
- *
- * That would take careful study of the interactions with prepunion.c,
- * but I suspect it would pay off in simplicity and avoidance of
- * wasted cycles.
+ * Do the main planning. If we have an inherited target relation,
+ * that needs special processing, else go straight to grouping_planner.
*/
+ if (parse->resultRelation &&
+ (lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
+ plan = inheritance_planner(parse, lst);
+ else
+ plan = grouping_planner(parse, tuple_fraction);
/*
* If any subplans were generated, or if we're inside a subplan,
@@ -600,10 +602,65 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
}
/*--------------------
- * union_planner
- * Invokes the planner on union-type queries (both set operations and
- * appends produced by inheritance), recursing if necessary to get them
- * all, then processes normal plans.
+ * inheritance_planner
+ * Generate a plan in the case where the result relation is an
+ * inheritance set.
+ *
+ * We have to handle this case differently from cases where a source
+ * relation is an inheritance set. Source inheritance is expanded at
+ * the bottom of the plan tree (see allpaths.c), but target inheritance
+ * has to be expanded at the top. The reason is that for UPDATE, each
+ * target relation needs a different targetlist matching its own column
+ * set. (This is not so critical for DELETE, but for simplicity we treat
+ * inherited DELETE the same way.) Fortunately, the UPDATE/DELETE target
+ * can never be the nullable side of an outer join, so it's OK to generate
+ * the plan this way.
+ *
+ * parse is the querytree produced by the parser & rewriter.
+ * inheritlist is an integer list of RT indexes for the result relation set.
+ *
+ * Returns a query plan.
+ *--------------------
+ */
+static Plan *
+inheritance_planner(Query *parse, List *inheritlist)
+{
+ int parentRTindex = parse->resultRelation;
+ Oid parentOID = getrelid(parentRTindex, parse->rtable);
+ List *subplans = NIL;
+ List *tlist = NIL;
+ List *l;
+
+ foreach(l, inheritlist)
+ {
+ int childRTindex = lfirsti(l);
+ Oid childOID = getrelid(childRTindex, parse->rtable);
+ Query *subquery;
+ Plan *subplan;
+
+ /* Generate modified query with this rel as target */
+ subquery = (Query *) adjust_inherited_attrs((Node *) parse,
+ parentRTindex, parentOID,
+ childRTindex, childOID);
+ /* Generate plan */
+ subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */);
+ subplans = lappend(subplans, subplan);
+ /* Save preprocessed tlist from first rel for use in Append */
+ if (tlist == NIL)
+ tlist = subplan->targetlist;
+ }
+
+ /* Save the target-relations list for the executor, too */
+ parse->resultRelations = inheritlist;
+
+ return (Plan *) make_append(subplans, true, tlist);
+}
+
+/*--------------------
+ * grouping_planner
+ * Perform planning steps related to grouping, aggregation, etc.
+ * This primarily means adding top-level processing to the basic
+ * query plan produced by query_planner.
*
* parse is the querytree produced by the parser & rewriter.
* tuple_fraction is the fraction of tuples we expect will be retrieved
@@ -621,18 +678,15 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
* Returns a query plan.
*--------------------
*/
-Plan *
-union_planner(Query *parse,
- double tuple_fraction)
+static Plan *
+grouping_planner(Query *parse, double tuple_fraction)
{
List *tlist = parse->targetList;
- Plan *result_plan = (Plan *) NULL;
- AttrNumber *groupColIdx = NULL;
- List *current_pathkeys = NIL;
+ Plan *result_plan;
+ List *current_pathkeys;
List *group_pathkeys;
List *sort_pathkeys;
- Index rt_index;
- List *inheritors;
+ AttrNumber *groupColIdx = NULL;
if (parse->setOperations)
{
@@ -654,12 +708,13 @@ union_planner(Query *parse,
tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
/*
- * We leave current_pathkeys NIL indicating we do not know sort
+ * We set current_pathkeys NIL indicating we do not know sort
* order. This is correct when the top set operation is UNION ALL,
* since the appended-together results are unsorted even if the
* subplans were sorted. For other set operations we could be
- * smarter --- future improvement!
+ * smarter --- room for future improvement!
*/
+ current_pathkeys = NIL;
/*
* Calculate pathkeys that represent grouping/ordering
@@ -670,54 +725,6 @@ union_planner(Query *parse,
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
tlist);
}
- else if (find_inheritable_rt_entry(parse->rtable,
- &rt_index, &inheritors))
- {
- List *sub_tlist;
-
- /*
- * Generate appropriate target list for subplan; may be different
- * from tlist if grouping or aggregation is needed.
- */
- sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
-
- /*
- * Recursively plan the subqueries needed for inheritance
- */
- result_plan = plan_inherit_queries(parse, sub_tlist,
- rt_index, inheritors);
-
- /*
- * Fix up outer target list. NOTE: unlike the case for
- * non-inherited query, we pass the unfixed tlist to subplans,
- * which do their own fixing. But we still want to fix the outer
- * target list afterwards. I *think* this is correct --- doing the
- * fix before recursing is definitely wrong, because
- * preprocess_targetlist() will do the wrong thing if invoked
- * twice on the same list. Maybe that is a bug? tgl 6/6/99
- */
- tlist = preprocess_targetlist(tlist,
- parse->commandType,
- parse->resultRelation,
- parse->rtable);
-
- if (parse->rowMarks)
- 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.
- */
-
- /*
- * Calculate pathkeys that represent grouping/ordering
- * requirements
- */
- group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,
- tlist);
- sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
- tlist);
- }
else
{
List *sub_tlist;
@@ -938,10 +945,6 @@ union_planner(Query *parse,
current_pathkeys = parse->query_pathkeys;
}
- /* query_planner returns NULL if it thinks plan is bogus */
- if (!result_plan)
- elog(ERROR, "union_planner: failed to create plan");
-
/*
* We couldn't canonicalize group_pathkeys and sort_pathkeys before
* running query_planner(), so do it now.
@@ -1057,7 +1060,7 @@ union_planner(Query *parse,
* make_subplanTargetList
* Generate appropriate target list when grouping is required.
*
- * When union_planner inserts Aggregate and/or Group plan nodes above
+ * When grouping_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.
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ea9b902bc5b..ebb09f59393 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1,15 +1,20 @@
/*-------------------------------------------------------------------------
*
* prepunion.c
- * Routines to plan set-operation and inheritance queries. The filename
- * is a leftover from a time when only UNIONs were handled.
+ * Routines to plan set-operation queries. The filename is a leftover
+ * from a time when only UNIONs were implemented.
+ *
+ * There is also some code here to support planning of queries that use
+ * inheritance (SELECT FROM foo*). This no longer has much connection
+ * to the processing of UNION queries, but it's still here.
+ *
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.55 2000/11/09 02:46:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.56 2000/11/12 00:36:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,13 +35,19 @@
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+/* macros borrowed from expression_tree_mutator */
+
+#define FLATCOPY(newnode, node, nodetype) \
+ ( (newnode) = makeNode(nodetype), \
+ memcpy((newnode), (node), sizeof(nodetype)) )
+
typedef struct
{
- Index rt_index;
- int sublevels_up;
+ Index old_rt_index;
+ Index new_rt_index;
Oid old_relid;
Oid new_relid;
-} fix_parsetree_attnums_context;
+} adjust_inherited_attrs_context;
static Plan *recurse_set_operations(Node *setOp, Query *parse,
List *colTypes, bool junkOK,
@@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag,
List *input_tlist,
List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
-static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
- Oid new_relid, Query *parsetree);
-static bool fix_parsetree_attnums_walker(Node *node,
- fix_parsetree_attnums_context *context);
-static RangeTblEntry *new_rangetable_entry(Oid new_relid,
- RangeTblEntry *old_entry);
-static Append *make_append(List *appendplans, Index rt_index,
- List *inheritrtable, List *tlist);
+static Node *adjust_inherited_attrs_mutator(Node *node,
+ adjust_inherited_attrs_context *context);
/*
@@ -69,8 +74,8 @@ static Append *make_append(List *appendplans, Index rt_index,
* Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
*
* This routine only deals with the setOperations tree of the given query.
- * Any top-level ORDER BY requested in parse->sortClause will be added on
- * back in union_planner.
+ * Any top-level ORDER BY requested in parse->sortClause will be added
+ * when we return to grouping_planner.
*/
Plan *
plan_set_operations(Query *parse)
@@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse,
NIL,
rtr->rtindex,
subplan);
- copy_plan_costsize(plan, subplan);
return plan;
}
else if (IsA(setOp, SetOperationStmt))
@@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
*/
plan = (Plan *)
make_append(planlist,
- 0,
- NIL,
+ false,
generate_setop_tlist(op->colTypes, -1, false,
((Plan *) lfirst(planlist))->targetlist,
refnames_tlist));
@@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
*/
plan = (Plan *)
make_append(makeList2(lplan, rplan),
- 0,
- NIL,
+ false,
generate_setop_tlist(op->colTypes, 0, false,
lplan->targetlist,
refnames_tlist));
@@ -457,132 +459,6 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
/*
- * plan_inherit_queries
- * Plans the queries for an inheritance tree rooted at a parent relation.
- *
- * Inputs:
- * root = parent parse tree
- * tlist = target list for inheritance subqueries (not same as parent's!)
- * rt_index = rangetable index for current inheritance item
- * inheritors = list of OIDs of the target rel plus all its descendants
- *
- * Returns an APPEND node that forms the result of performing the given
- * query for each member relation of the inheritance group.
- *
- * If grouping, aggregation, or sorting is specified in the parent plan,
- * the subplans should not do any of those steps --- we must do those
- * operations just once above the APPEND node. The given tlist has been
- * modified appropriately to remove group/aggregate expressions, but the
- * Query node still has the relevant fields set. We remove them in the
- * copies used for subplans.
- *
- * NOTE: this can be invoked recursively if more than one inheritance wildcard
- * is present. At each level of recursion, the first wildcard remaining in
- * the rangetable is expanded.
- *
- * NOTE: don't bother optimizing this routine for the case that the target
- * rel has no children. We won't get here unless find_inheritable_rt_entry
- * found at least two members in the inheritance group, so an APPEND is
- * certainly necessary.
- */
-Plan *
-plan_inherit_queries(Query *root, List *tlist,
- Index rt_index, List *inheritors)
-{
- RangeTblEntry *rt_entry = rt_fetch(rt_index, root->rtable);
- List *union_plans = NIL;
- List *union_rtentries = NIL;
- List *save_tlist = root->targetList;
- double tuple_fraction;
- List *i;
-
- /*
- * Avoid making copies of the root's tlist, which we aren't going to
- * use anyway (we are going to make copies of the passed tlist,
- * instead). This is purely a space-saving hack. Note we restore
- * the root's tlist before exiting.
- */
- root->targetList = NIL;
-
- /*
- * If we are going to need sorting or grouping at the top level, force
- * lower-level planners to assume that all tuples will be retrieved.
- */
- if (root->distinctClause || root->sortClause ||
- root->groupClause || root->hasAggs)
- tuple_fraction = 0.0; /* will need all tuples from each subplan */
- else
- tuple_fraction = -1.0; /* default behavior is OK (I think) */
-
- foreach(i, inheritors)
- {
- Oid relid = lfirsti(i);
-
- /*
- * Make a modifiable copy of the original query, and replace the
- * target rangetable entry in it with a new one identifying this
- * child table. The new rtentry is marked inh = false --- this
- * is essential to prevent infinite recursion when the subquery
- * is rescanned by find_inheritable_rt_entry!
- */
- Query *new_root = copyObject(root);
- RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
- rt_entry);
-
- new_rt_entry->inh = false;
- rt_store(rt_index, new_root->rtable, new_rt_entry);
-
- /*
- * Insert (a modifiable copy of) the desired simplified tlist into
- * the subquery
- */
- new_root->targetList = copyObject(tlist);
-
- /*
- * Clear the sorting and grouping qualifications in the subquery,
- * so that sorting will only be done once after append
- */
- new_root->distinctClause = NIL;
- new_root->sortClause = NIL;
- new_root->groupClause = NIL;
- new_root->havingQual = NULL;
- new_root->limitOffset = NULL; /* LIMIT's probably unsafe too */
- new_root->limitCount = NULL;
- new_root->hasAggs = false; /* shouldn't be any left ... */
-
- /*
- * Update attribute numbers in case child has different ordering
- * of columns than parent (as can happen after ALTER TABLE).
- *
- * XXX This is a crock, and it doesn't really work. It'd be better
- * to fix ALTER TABLE to preserve consistency of attribute
- * numbering.
- */
- fix_parsetree_attnums(rt_index,
- rt_entry->relid,
- relid,
- new_root);
-
- /*
- * Plan the subquery by recursively calling union_planner().
- * Add plan and child rtentry to lists for APPEND.
- */
- union_plans = lappend(union_plans,
- union_planner(new_root, tuple_fraction));
- union_rtentries = lappend(union_rtentries, new_rt_entry);
- }
-
- /* Restore root's tlist */
- root->targetList = save_tlist;
-
- /* Construct the finished Append plan. */
- return (Plan *) make_append(union_plans,
- rt_index,
- union_rtentries,
- ((Plan *) lfirst(union_plans))->targetlist);
-}
-
-/*
* find_all_inheritors -
* Returns an integer list of relids including the given rel plus
* all relations that inherit from it, directly or indirectly.
@@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel)
}
/*
- * find_inheritable_rt_entry -
- * Given a rangetable, find the first rangetable entry that represents
- * an inheritance set.
- *
- * If successful, set *rt_index to the index (1..n) of the entry,
- * set *inheritors to a list of the relation OIDs of the set,
- * and return TRUE.
- *
- * If there is no entry that requires inheritance processing,
- * return FALSE.
+ * expand_inherted_rtentry
+ * Check whether a rangetable entry represents an inheritance set.
+ * If so, add entries for all the child tables to the query's
+ * rangetable, and return an integer list of RT indexes for the
+ * whole inheritance set (parent and children).
+ * If not, return NIL.
*
- * NOTE: We return the inheritors list so that plan_inherit_queries doesn't
- * have to compute it again.
+ * A childless table is never considered to be an inheritance set; therefore
+ * the result will never be a one-element list. It'll be either empty
+ * or have two or more elements.
*
- * NOTE: We clear the inh flag in any entries that have it set but turn
- * out not to have any actual inheritance children. This is an efficiency
- * hack to avoid having to repeat the inheritance checks if the list is
- * scanned again (as will happen during expansion of any subsequent entry
- * that does have inheritance children). Although modifying the input
- * rangetable in-place may seem uncool, there's no reason not to do it,
- * since any re-examination of the entry would just come to the same
- * conclusion that the table has no children.
+ * NOTE: after this routine executes, the specified RTE will always have
+ * its inh flag cleared, whether or not there were any children. This
+ * ensures we won't expand the same RTE twice, which would otherwise occur
+ * for the case of an inherited UPDATE/DELETE target relation.
*/
-bool
-find_inheritable_rt_entry(List *rangetable,
- Index *rt_index,
- List **inheritors)
+List *
+expand_inherted_rtentry(Query *parse, Index rti)
{
- Index count = 0;
- List *temp;
-
- foreach(temp, rangetable)
+ RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
+ Oid parentOID = rte->relid;
+ List *inhOIDs;
+ List *inhRTIs;
+ List *l;
+
+ /* Does RT entry allow inheritance? */
+ if (! rte->inh)
+ return NIL;
+ Assert(parentOID != InvalidOid && rte->subquery == NULL);
+ /* Always clear the parent's inh flag, see above comments */
+ rte->inh = false;
+ /* Fast path for common case of childless table */
+ if (! has_subclass(parentOID))
+ return NIL;
+ /* Scan for all members of inheritance set */
+ inhOIDs = find_all_inheritors(parentOID);
+ /*
+ * Check that there's at least one descendant, else treat as
+ * no-child case. This could happen despite above has_subclass()
+ * check, if table once had a child but no longer does.
+ */
+ if (lnext(inhOIDs) == NIL)
+ return NIL;
+ /* OK, it's an inheritance set; expand it */
+ inhRTIs = makeListi1(rti);
+ foreach(l, inhOIDs)
{
- RangeTblEntry *rt_entry = (RangeTblEntry *) lfirst(temp);
- List *inhs;
+ Oid childOID = (Oid) lfirsti(l);
+ RangeTblEntry *childrte;
+ Index childRTindex;
- count++;
- /* Ignore non-inheritable RT entries */
- if (! rt_entry->inh)
- continue;
- /* Fast path for common case of childless table */
- if (! has_subclass(rt_entry->relid))
- {
- rt_entry->inh = false;
+ /* parent will be in the list too, so ignore it */
+ if (childOID == parentOID)
continue;
- }
- /* Scan for all members of inheritance set */
- inhs = find_all_inheritors(rt_entry->relid);
+
/*
- * Check that there's at least one descendant, else treat as
- * no-child case. This could happen despite above has_subclass()
- * check, if table once had a child but no longer does.
+ * Build an RTE for the child, and attach to query's rangetable list.
+ * We copy most fields of the parent's RTE, but replace relation
+ * real name and OID. Note that inh will be false at this point.
*/
- if (lnext(inhs) == NIL)
- {
- rt_entry->inh = false;
- continue;
- }
- /* OK, found our boy */
- *rt_index = count;
- *inheritors = inhs;
- return true;
- }
+ childrte = copyObject(rte);
+ childrte->relname = get_rel_name(childOID);
+ childrte->relid = childOID;
+ parse->rtable = lappend(parse->rtable, childrte);
+ childRTindex = length(parse->rtable);
- return false;
-}
-
-/*
- * new_rangetable_entry -
- * Replaces the name and relid of 'old_entry' with the values for
- * 'new_relid'.
- *
- * Returns a copy of 'old_entry' with the parameters substituted.
- */
-static RangeTblEntry *
-new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
-{
- RangeTblEntry *new_entry = copyObject(old_entry);
-
- /* Replace relation real name and OID, but not the reference name */
- new_entry->relname = get_rel_name(new_relid);
- new_entry->relid = new_relid;
- return new_entry;
+ inhRTIs = lappendi(inhRTIs, childRTindex);
+ }
+ return inhRTIs;
}
/*
- * fix_parsetree_attnums
- * Replaces attribute numbers from the relation represented by
- * 'old_relid' in 'parsetree' with the attribute numbers from
- * 'new_relid'.
+ * adjust_inherited_attrs
+ * Copy the specified query or expression and translate Vars referring
+ * to old_rt_index to refer to new_rt_index.
*
- * The parsetree is MODIFIED IN PLACE. This is OK only because
- * plan_inherit_queries made a copy of the tree for us to hack upon.
+ * We also adjust varattno to match the new table by column name, rather
+ * than column number. This hack makes it possible for child tables to have
+ * different column positions for the "same" attribute as a parent, which
+ * helps ALTER TABLE ADD COLUMN. Unfortunately this isn't nearly enough to
+ * make it work transparently; there are other places where things fall down
+ * if children and parents don't have the same column numbers for inherited
+ * attributes. It'd be better to rip this code out and fix ALTER TABLE...
*/
-static void
-fix_parsetree_attnums(Index rt_index,
- Oid old_relid,
- Oid new_relid,
- Query *parsetree)
+Node *
+adjust_inherited_attrs(Node *node,
+ Index old_rt_index, Oid old_relid,
+ Index new_rt_index, Oid new_relid)
{
- fix_parsetree_attnums_context context;
+ adjust_inherited_attrs_context context;
- if (old_relid == new_relid)
- return; /* no work needed for parent rel itself */
+ /* Handle simple case simply... */
+ if (old_rt_index == new_rt_index)
+ {
+ Assert(old_relid == new_relid);
+ return copyObject(node);
+ }
- context.rt_index = rt_index;
+ context.old_rt_index = old_rt_index;
+ context.new_rt_index = new_rt_index;
context.old_relid = old_relid;
context.new_relid = new_relid;
- context.sublevels_up = 0;
- query_tree_walker(parsetree,
- fix_parsetree_attnums_walker,
- (void *) &context,
- true);
+ /*
+ * Must be prepared to start with a Query or a bare expression tree.
+ */
+ if (node && IsA(node, Query))
+ {
+ Query *query = (Query *) node;
+ Query *newnode;
+
+ FLATCOPY(newnode, query, Query);
+ if (newnode->resultRelation == old_rt_index)
+ newnode->resultRelation = new_rt_index;
+ query_tree_mutator(newnode, adjust_inherited_attrs_mutator,
+ (void *) &context, false);
+ return (Node *) newnode;
+ }
+ else
+ return adjust_inherited_attrs_mutator(node, &context);
}
-/*
- * Adjust varnos for child tables. This routine makes it possible for
- * child tables to have different column positions for the "same" attribute
- * as a parent, which helps ALTER TABLE ADD COLUMN. Unfortunately this isn't
- * nearly enough to make it work transparently; there are other places where
- * things fall down if children and parents don't have the same column numbers
- * for inherited attributes. It'd be better to rip this code out and fix
- * ALTER TABLE...
- */
-static bool
-fix_parsetree_attnums_walker(Node *node,
- fix_parsetree_attnums_context *context)
+static Node *
+adjust_inherited_attrs_mutator(Node *node,
+ adjust_inherited_attrs_context *context)
{
if (node == NULL)
- return false;
+ return NULL;
if (IsA(node, Var))
{
- Var *var = (Var *) node;
+ Var *var = (Var *) copyObject(node);
- if (var->varlevelsup == context->sublevels_up &&
- var->varno == context->rt_index &&
- var->varattno > 0)
+ if (var->varlevelsup == 0 &&
+ var->varno == context->old_rt_index)
{
- var->varattno = get_attnum(context->new_relid,
- get_attname(context->old_relid,
- var->varattno));
+ var->varno = context->new_rt_index;
+ if (var->varattno > 0)
+ var->varattno = get_attnum(context->new_relid,
+ get_attname(context->old_relid,
+ var->varattno));
}
- return false;
+ return (Node *) var;
}
- if (IsA(node, Query))
+ if (IsA(node, RangeTblRef))
{
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- result = query_tree_walker((Query *) node,
- fix_parsetree_attnums_walker,
- (void *) context,
- true);
- context->sublevels_up--;
- return result;
- }
- return expression_tree_walker(node, fix_parsetree_attnums_walker,
- (void *) context);
-}
+ RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
-static Append *
-make_append(List *appendplans,
- Index rt_index,
- List *inheritrtable,
- List *tlist)
-{
- Append *node = makeNode(Append);
- List *subnode;
-
- node->appendplans = appendplans;
- node->inheritrelid = rt_index;
- node->inheritrtable = inheritrtable;
- node->plan.startup_cost = 0;
- node->plan.total_cost = 0;
- node->plan.plan_rows = 0;
- node->plan.plan_width = 0;
- foreach(subnode, appendplans)
- {
- Plan *subplan = (Plan *) lfirst(subnode);
-
- if (subnode == appendplans) /* first node? */
- node->plan.startup_cost = subplan->startup_cost;
- node->plan.total_cost += subplan->total_cost;
- node->plan.plan_rows += subplan->plan_rows;
- if (node->plan.plan_width < subplan->plan_width)
- node->plan.plan_width = subplan->plan_width;
+ if (rtr->rtindex == context->old_rt_index)
+ rtr->rtindex = context->new_rt_index;
+ return (Node *) rtr;
}
- node->plan.state = (EState *) NULL;
- node->plan.targetlist = tlist;
- node->plan.qual = NIL;
- node->plan.lefttree = (Plan *) NULL;
- node->plan.righttree = (Plan *) NULL;
+ /*
+ * We have to process RestrictInfo nodes specially: we do NOT want to
+ * copy the original subclauseindices list, since the new rel may have
+ * different indices. The list will be rebuilt during planning anyway.
+ */
+ if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *oldinfo = (RestrictInfo *) node;
+ RestrictInfo *newinfo = makeNode(RestrictInfo);
+
+ /* Copy all flat-copiable fields */
+ memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
+
+ newinfo->clause = (Expr *)
+ adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
- return node;
+ newinfo->subclauseindices = NIL;
+
+ return (Node *) newinfo;
+ }
+ /*
+ * NOTE: we do not need to recurse into sublinks, because they should
+ * already have been converted to subplans before we see them.
+ */
+ return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ (void *) context);
}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 36843f834da..b51ec089347 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.67 2000/10/05 19:48:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.68 2000/11/12 00:36:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -391,6 +391,37 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
}
/*
+ * create_append_path
+ * Creates a path corresponding to an Append plan, returning the
+ * pathnode.
+ *
+ */
+AppendPath *
+create_append_path(RelOptInfo *rel, List *subpaths)
+{
+ AppendPath *pathnode = makeNode(AppendPath);
+ List *l;
+
+ pathnode->path.pathtype = T_Append;
+ pathnode->path.parent = rel;
+ pathnode->path.pathkeys = NIL; /* result is always considered unsorted */
+ pathnode->subpaths = subpaths;
+
+ pathnode->path.startup_cost = 0;
+ pathnode->path.total_cost = 0;
+ foreach(l, subpaths)
+ {
+ Path *subpath = (Path *) lfirst(l);
+
+ if (l == subpaths) /* first node? */
+ pathnode->path.startup_cost = subpath->startup_cost;
+ pathnode->path.total_cost += subpath->total_cost;
+ }
+
+ return pathnode;
+}
+
+/*
* create_subqueryscan_path
* Creates a path corresponding to a sequential scan of a subquery,
* returning the pathnode.
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 86258b0f644..64720944ce3 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.29 2000/09/29 18:21:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.30 2000/11/12 00:36:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -45,7 +45,6 @@ get_base_rel(Query *root, int relid)
{
List *baserels;
RelOptInfo *rel;
- Oid relationObjectId;
foreach(baserels, root->base_rel_list)
{
@@ -60,7 +59,30 @@ get_base_rel(Query *root, int relid)
}
/* No existing RelOptInfo for this base rel, so make a new one */
- rel = makeNode(RelOptInfo);
+ rel = make_base_rel(root, relid);
+
+ /* and add it to the list */
+ root->base_rel_list = lcons(rel, root->base_rel_list);
+
+ return rel;
+}
+
+/*
+ * make_base_rel
+ * Construct a base-relation RelOptInfo for the specified rangetable index.
+ *
+ * This is split out of get_base_rel so that inheritance-tree processing can
+ * construct baserel nodes for child tables. We need a RelOptInfo so we can
+ * plan a suitable access path for each child table, but we do NOT want to
+ * enter the child nodes into base_rel_list. In most contexts, get_base_rel
+ * should be called instead.
+ */
+RelOptInfo *
+make_base_rel(Query *root, int relid)
+{
+ RelOptInfo *rel = makeNode(RelOptInfo);
+ Oid relationObjectId;
+
rel->relids = makeListi1(relid);
rel->rows = 0;
rel->width = 0;
@@ -95,8 +117,6 @@ get_base_rel(Query *root, int relid)
rel->issubquery = true;
}
- root->base_rel_list = lcons(rel, root->base_rel_list);
-
return rel;
}