mirror of
https://github.com/postgres/postgres.git
synced 2025-07-15 19:21:59 +03:00
Restructure handling of inheritance queries so that they work with outer
joins, and clean things up a good deal at the same time. Append plan node no longer hacks on rangetable at runtime --- instead, all child tables are given their own RT entries during planning. Concept of multiple target tables pushed up into execMain, replacing bug-prone implementation within nodeAppend. Planner now supports generating Append plans for inheritance sets either at the top of the plan (the old way) or at the bottom. Expanding at the bottom is appropriate for tables used as sources, since they may appear inside an outer join; but we must still expand at the top when the target of an UPDATE or DELETE is an inheritance set, because we actually need a different targetlist and junkfilter for each target table in that case. Fortunately a target table can't be inside an outer join... Bizarre mutual recursion between union_planner and prepunion.c is gone --- in fact, union_planner doesn't really have much to do with union queries anymore, so I renamed it grouping_planner.
This commit is contained in:
@ -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
|
||||
|
@ -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);
|
||||
|
||||
/* Mark rel with estimated output rows, width, etc */
|
||||
set_baserel_size_estimates(root, 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 sequential scan */
|
||||
add_path(rel, create_seqscan_path(rel));
|
||||
|
||||
/* Consider TID scans */
|
||||
create_tidscan_paths(root, rel);
|
||||
|
||||
/* 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);
|
||||
set_plain_rel_pathlist(root, rel, rte);
|
||||
}
|
||||
|
||||
/* Now find the cheapest of the paths for this rel */
|
||||
set_cheapest(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);
|
||||
|
||||
/* Mark rel with estimated output rows, width, etc */
|
||||
set_baserel_size_estimates(root, 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 sequential scan */
|
||||
add_path(rel, create_seqscan_path(rel));
|
||||
|
||||
/* Consider TID scans */
|
||||
create_tidscan_paths(root, rel);
|
||||
|
||||
/* 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");
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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...)
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
@ -456,132 +458,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
|
||||
@ -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.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* If there is no entry that requires inheritance processing,
|
||||
* return FALSE.
|
||||
*
|
||||
* NOTE: We return the inheritors list so that plan_inherit_queries doesn't
|
||||
* have to compute it again.
|
||||
*
|
||||
* 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;
|
||||
RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
|
||||
Oid parentOID = rte->relid;
|
||||
List *inhOIDs;
|
||||
List *inhRTIs;
|
||||
List *l;
|
||||
|
||||
foreach(temp, rangetable)
|
||||
/* 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)
|
||||
/* parent will be in the list too, so ignore it */
|
||||
if (childOID == parentOID)
|
||||
continue;
|
||||
/* Fast path for common case of childless table */
|
||||
if (! has_subclass(rt_entry->relid))
|
||||
{
|
||||
rt_entry->inh = false;
|
||||
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);
|
||||
|
||||
inhRTIs = lappendi(inhRTIs, childRTindex);
|
||||
}
|
||||
return inhRTIs;
|
||||
}
|
||||
|
||||
/*
|
||||
* adjust_inherited_attrs
|
||||
* Copy the specified query or expression and translate Vars referring
|
||||
* to old_rt_index to refer to new_rt_index.
|
||||
*
|
||||
* 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...
|
||||
*/
|
||||
Node *
|
||||
adjust_inherited_attrs(Node *node,
|
||||
Index old_rt_index, Oid old_relid,
|
||||
Index new_rt_index, Oid new_relid)
|
||||
{
|
||||
adjust_inherited_attrs_context context;
|
||||
|
||||
/* Handle simple case simply... */
|
||||
if (old_rt_index == new_rt_index)
|
||||
{
|
||||
Assert(old_relid == new_relid);
|
||||
return copyObject(node);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* fix_parsetree_attnums
|
||||
* Replaces attribute numbers from the relation represented by
|
||||
* 'old_relid' in 'parsetree' with the attribute numbers from
|
||||
* 'new_relid'.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static void
|
||||
fix_parsetree_attnums(Index rt_index,
|
||||
Oid old_relid,
|
||||
Oid new_relid,
|
||||
Query *parsetree)
|
||||
{
|
||||
fix_parsetree_attnums_context context;
|
||||
|
||||
if (old_relid == new_relid)
|
||||
return; /* no work needed for parent rel itself */
|
||||
|
||||
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;
|
||||
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
|
||||
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node,
|
||||
fix_parsetree_attnums_walker,
|
||||
(void *) context,
|
||||
true);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
if (rtr->rtindex == context->old_rt_index)
|
||||
rtr->rtindex = context->new_rt_index;
|
||||
return (Node *) rtr;
|
||||
}
|
||||
return expression_tree_walker(node, fix_parsetree_attnums_walker,
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
node->plan.state = (EState *) NULL;
|
||||
node->plan.targetlist = tlist;
|
||||
node->plan.qual = NIL;
|
||||
node->plan.lefttree = (Plan *) NULL;
|
||||
node->plan.righttree = (Plan *) NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -390,6 +390,37 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user