Commit 8a1468af authored by Tom Lane's avatar Tom Lane

Restructure planner's handling of inheritance. Rather than processing

inheritance trees on-the-fly, which pretty well constrained us to considering
only one way of planning inheritance, expand inheritance sets during the
planner prep phase, and build a side data structure that can be consulted
later to find which RTEs are members of which inheritance sets.  As proof of
concept, use the data structure to plan joins against inheritance sets more
efficiently: we can now use indexes on the set members in inner-indexscan
joins.  (The generated plans could be improved further, but it'll take some
executor changes.)  This data structure will also support handling UNION ALL
subqueries in the same way as inheritance sets, but that aspect of it isn't
finished yet.
parent 097df388
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.324 2005/12/28 01:29:59 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.325 2006/01/31 21:39:23 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1322,6 +1322,25 @@ _copyInClauseInfo(InClauseInfo *from) ...@@ -1322,6 +1322,25 @@ _copyInClauseInfo(InClauseInfo *from)
return newnode; return newnode;
} }
/*
* _copyAppendRelInfo
*/
static AppendRelInfo *
_copyAppendRelInfo(AppendRelInfo *from)
{
AppendRelInfo *newnode = makeNode(AppendRelInfo);
COPY_SCALAR_FIELD(parent_relid);
COPY_SCALAR_FIELD(child_relid);
COPY_SCALAR_FIELD(parent_reltype);
COPY_SCALAR_FIELD(child_reltype);
COPY_NODE_FIELD(col_mappings);
COPY_NODE_FIELD(translated_vars);
COPY_SCALAR_FIELD(parent_reloid);
return newnode;
}
/* **************************************************************** /* ****************************************************************
* parsenodes.h copy functions * parsenodes.h copy functions
* **************************************************************** * ****************************************************************
...@@ -2945,6 +2964,9 @@ copyObject(void *from) ...@@ -2945,6 +2964,9 @@ copyObject(void *from)
case T_InClauseInfo: case T_InClauseInfo:
retval = _copyInClauseInfo(from); retval = _copyInClauseInfo(from);
break; break;
case T_AppendRelInfo:
retval = _copyAppendRelInfo(from);
break;
/* /*
* VALUE NODES * VALUE NODES
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.260 2005/12/28 01:29:59 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.261 2006/01/31 21:39:23 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -644,6 +644,20 @@ _equalInClauseInfo(InClauseInfo *a, InClauseInfo *b) ...@@ -644,6 +644,20 @@ _equalInClauseInfo(InClauseInfo *a, InClauseInfo *b)
return true; return true;
} }
static bool
_equalAppendRelInfo(AppendRelInfo *a, AppendRelInfo *b)
{
COMPARE_SCALAR_FIELD(parent_relid);
COMPARE_SCALAR_FIELD(child_relid);
COMPARE_SCALAR_FIELD(parent_reltype);
COMPARE_SCALAR_FIELD(child_reltype);
COMPARE_NODE_FIELD(col_mappings);
COMPARE_NODE_FIELD(translated_vars);
COMPARE_SCALAR_FIELD(parent_reloid);
return true;
}
/* /*
* Stuff from parsenodes.h * Stuff from parsenodes.h
...@@ -1984,6 +1998,9 @@ equal(void *a, void *b) ...@@ -1984,6 +1998,9 @@ equal(void *a, void *b)
case T_InClauseInfo: case T_InClauseInfo:
retval = _equalInClauseInfo(a, b); retval = _equalInClauseInfo(a, b);
break; break;
case T_AppendRelInfo:
retval = _equalAppendRelInfo(a, b);
break;
case T_List: case T_List:
case T_IntList: case T_IntList:
case T_OidList: case T_OidList:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.266 2005/12/28 01:29:59 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.267 2006/01/31 21:39:23 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1178,6 +1178,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node) ...@@ -1178,6 +1178,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
WRITE_NODE_FIELD(full_join_clauses); WRITE_NODE_FIELD(full_join_clauses);
WRITE_NODE_FIELD(oj_info_list); WRITE_NODE_FIELD(oj_info_list);
WRITE_NODE_FIELD(in_info_list); WRITE_NODE_FIELD(in_info_list);
WRITE_NODE_FIELD(append_rel_list);
WRITE_NODE_FIELD(query_pathkeys); WRITE_NODE_FIELD(query_pathkeys);
WRITE_NODE_FIELD(group_pathkeys); WRITE_NODE_FIELD(group_pathkeys);
WRITE_NODE_FIELD(sort_pathkeys); WRITE_NODE_FIELD(sort_pathkeys);
...@@ -1204,8 +1205,8 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) ...@@ -1204,8 +1205,8 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
WRITE_NODE_FIELD(cheapest_unique_path); WRITE_NODE_FIELD(cheapest_unique_path);
WRITE_UINT_FIELD(relid); WRITE_UINT_FIELD(relid);
WRITE_ENUM_FIELD(rtekind, RTEKind); WRITE_ENUM_FIELD(rtekind, RTEKind);
WRITE_UINT_FIELD(min_attr); WRITE_INT_FIELD(min_attr);
WRITE_UINT_FIELD(max_attr); WRITE_INT_FIELD(max_attr);
WRITE_NODE_FIELD(indexlist); WRITE_NODE_FIELD(indexlist);
WRITE_UINT_FIELD(pages); WRITE_UINT_FIELD(pages);
WRITE_FLOAT_FIELD(tuples, "%.0f"); WRITE_FLOAT_FIELD(tuples, "%.0f");
...@@ -1295,6 +1296,20 @@ _outInClauseInfo(StringInfo str, InClauseInfo *node) ...@@ -1295,6 +1296,20 @@ _outInClauseInfo(StringInfo str, InClauseInfo *node)
WRITE_NODE_FIELD(sub_targetlist); WRITE_NODE_FIELD(sub_targetlist);
} }
static void
_outAppendRelInfo(StringInfo str, AppendRelInfo *node)
{
WRITE_NODE_TYPE("APPENDRELINFO");
WRITE_UINT_FIELD(parent_relid);
WRITE_UINT_FIELD(child_relid);
WRITE_OID_FIELD(parent_reltype);
WRITE_OID_FIELD(child_reltype);
WRITE_NODE_FIELD(col_mappings);
WRITE_NODE_FIELD(translated_vars);
WRITE_OID_FIELD(parent_reloid);
}
/***************************************************************************** /*****************************************************************************
* *
* Stuff from parsenodes.h. * Stuff from parsenodes.h.
...@@ -2048,6 +2063,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2048,6 +2063,9 @@ _outNode(StringInfo str, void *obj)
case T_InClauseInfo: case T_InClauseInfo:
_outInClauseInfo(str, obj); _outInClauseInfo(str, obj);
break; break;
case T_AppendRelInfo:
_outAppendRelInfo(str, obj);
break;
case T_CreateStmt: case T_CreateStmt:
_outCreateStmt(str, obj); _outCreateStmt(str, obj);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.139 2005/12/20 02:30:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.140 2006/01/31 21:39:23 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -44,9 +44,8 @@ int geqo_threshold; ...@@ -44,9 +44,8 @@ int geqo_threshold;
static void set_base_rel_pathlists(PlannerInfo *root); static void set_base_rel_pathlists(PlannerInfo *root);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte); RangeTblEntry *rte);
static void set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte, Index rti, RangeTblEntry *rte);
List *inheritlist);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte); Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
...@@ -96,9 +95,9 @@ make_one_rel(PlannerInfo *root, List *joinlist) ...@@ -96,9 +95,9 @@ make_one_rel(PlannerInfo *root, List *joinlist)
int num_base_rels = 0; int num_base_rels = 0;
Index rti; Index rti;
for (rti = 1; rti < root->base_rel_array_size; rti++) for (rti = 1; rti < root->simple_rel_array_size; rti++)
{ {
RelOptInfo *brel = root->base_rel_array[rti]; RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL) if (brel == NULL)
continue; continue;
...@@ -131,16 +130,10 @@ set_base_rel_pathlists(PlannerInfo *root) ...@@ -131,16 +130,10 @@ set_base_rel_pathlists(PlannerInfo *root)
{ {
Index rti; Index rti;
/* for (rti = 1; rti < root->simple_rel_array_size; rti++)
* Note: because we call expand_inherited_rtentry inside the loop, it's
* quite possible for the base_rel_array to be enlarged while the loop
* runs. Hence don't try to optimize the loop.
*/
for (rti = 1; rti < root->base_rel_array_size; rti++)
{ {
RelOptInfo *rel = root->base_rel_array[rti]; RelOptInfo *rel = root->simple_rel_array[rti];
RangeTblEntry *rte; RangeTblEntry *rte;
List *inheritlist;
/* there may be empty slots corresponding to non-baserel RTEs */ /* there may be empty slots corresponding to non-baserel RTEs */
if (rel == NULL) if (rel == NULL)
...@@ -154,7 +147,12 @@ set_base_rel_pathlists(PlannerInfo *root) ...@@ -154,7 +147,12 @@ set_base_rel_pathlists(PlannerInfo *root)
rte = rt_fetch(rti, root->parse->rtable); rte = rt_fetch(rti, root->parse->rtable);
if (rel->rtekind == RTE_SUBQUERY) if (rte->inh)
{
/* It's an "append relation", process accordingly */
set_append_rel_pathlist(root, rel, rti, rte);
}
else if (rel->rtekind == RTE_SUBQUERY)
{ {
/* Subquery --- generate a separate plan for it */ /* Subquery --- generate a separate plan for it */
set_subquery_pathlist(root, rel, rti, rte); set_subquery_pathlist(root, rel, rti, rte);
...@@ -164,11 +162,6 @@ set_base_rel_pathlists(PlannerInfo *root) ...@@ -164,11 +162,6 @@ set_base_rel_pathlists(PlannerInfo *root)
/* RangeFunction --- generate a separate plan for it */ /* RangeFunction --- generate a separate plan for it */
set_function_pathlist(root, rel, rte); set_function_pathlist(root, rel, rte);
} }
else if ((inheritlist = expand_inherited_rtentry(root, rti)) != NIL)
{
/* Relation is root of an inheritance tree, process specially */
set_inherited_rel_pathlist(root, rel, rti, rte, inheritlist);
}
else else
{ {
/* Plain relation */ /* Plain relation */
...@@ -188,6 +181,9 @@ set_base_rel_pathlists(PlannerInfo *root) ...@@ -188,6 +181,9 @@ set_base_rel_pathlists(PlannerInfo *root)
static void static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{ {
Assert(rel->rtekind == RTE_RELATION);
Assert(!rte->inh);
/* Mark rel with estimated output rows, width, etc */ /* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel); set_baserel_size_estimates(root, rel);
...@@ -224,37 +220,29 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) ...@@ -224,37 +220,29 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
} }
/* /*
* set_inherited_rel_pathlist * set_append_rel_pathlist
* Build access paths for a inheritance tree rooted at rel * Build access paths for an "append relation"
*
* inheritlist is a list of RT indexes of all tables in the inheritance tree,
* including a duplicate of the parent itself. Note we will not come here
* unless there's at least one child in addition to the parent.
* *
* NOTE: the passed-in rel and RTE will henceforth represent the appended * The passed-in rel and RTE represent the entire append relation. The
* result of the whole inheritance tree. The members of inheritlist represent * relation's contents are computed by appending together the output of
* the individual tables --- in particular, the inheritlist member that is a * the individual member relations. Note that in the inheritance case,
* duplicate of the parent RTE represents the parent table alone. * the first member relation is actually the same table as is mentioned in
* We will generate plans to scan the individual tables that refer to * the parent RTE ... but it has a different RTE and RelOptInfo. This is
* the inheritlist RTEs, whereas Vars elsewhere in the plan tree that * a good thing because their outputs are not the same size.
* refer to the original RTE are taken to refer to the append output.
* In particular, this means we have separate RelOptInfos for the parent
* table and for the append output, which is a good thing because they're
* not the same size.
*/ */
static void static void
set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte, Index rti, RangeTblEntry *rte)
List *inheritlist)
{ {
int parentRTindex = rti; int parentRTindex = rti;
Oid parentOID = rte->relid;
List *subpaths = NIL; List *subpaths = NIL;
ListCell *il; ListCell *l;
/* /*
* XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can * XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can
* we do better? * we do better? (This will take some redesign because the executor
* currently supposes that every rowMark relation is involved in every
* row returned by the query.)
*/ */
if (list_member_int(root->parse->rowMarks, parentRTindex)) if (list_member_int(root->parse->rowMarks, parentRTindex))
ereport(ERROR, ereport(ERROR,
...@@ -262,64 +250,79 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, ...@@ -262,64 +250,79 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries"))); errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
/* /*
* Initialize to compute size estimates for whole inheritance tree * Initialize to compute size estimates for whole append relation
*/ */
rel->rows = 0; rel->rows = 0;
rel->width = 0; rel->width = 0;
/* /*
* Generate access paths for each table in the tree (parent AND children), * Generate access paths for each member relation, and pick the cheapest
* and pick the cheapest path for each table. * path for each one.
*/ */
foreach(il, inheritlist) foreach(l, root->append_rel_list)
{ {
int childRTindex = lfirst_int(il); AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
RangeTblEntry *childrte; int childRTindex;
Oid childOID;
RelOptInfo *childrel; RelOptInfo *childrel;
RangeTblEntry *childrte;
ListCell *parentvars; ListCell *parentvars;
ListCell *childvars; ListCell *childvars;
childrte = rt_fetch(childRTindex, root->parse->rtable); /* append_rel_list contains all append rels; ignore others */
childOID = childrte->relid; if (appinfo->parent_relid != parentRTindex)
continue;
childRTindex = appinfo->child_relid;
/* /*
* Make a RelOptInfo for the child so we can do planning. Mark it as * Make a RelOptInfo for the child so we can do planning. Mark it as
* an "other rel" since it will not be part of the main join tree. * an "other rel" since it will not be part of the main join tree.
*/ */
childrel = build_other_rel(root, childRTindex); childrel = build_simple_rel(root, childRTindex,
RELOPT_OTHER_MEMBER_REL);
/* /*
* Copy the parent's targetlist and restriction quals to the child, * Copy the parent's targetlist and quals to the child, with
* with attribute-number adjustment as needed. We don't bother to * appropriate substitution of variables.
* copy the join quals, since we can't do any joining of the
* individual tables. Also, we just zap attr_needed rather than
* trying to adjust it; it won't be looked at in the child.
*/ */
childrel->reltargetlist = (List *) childrel->reltargetlist = (List *)
adjust_inherited_attrs((Node *) rel->reltargetlist, adjust_appendrel_attrs((Node *) rel->reltargetlist,
parentRTindex, appinfo);
parentOID,
childRTindex,
childOID);
childrel->attr_needed = NULL;
childrel->baserestrictinfo = (List *) childrel->baserestrictinfo = (List *)
adjust_inherited_attrs((Node *) rel->baserestrictinfo, adjust_appendrel_attrs((Node *) rel->baserestrictinfo,
parentRTindex, appinfo);
parentOID, childrel->joininfo = (List *)
childRTindex, adjust_appendrel_attrs((Node *) rel->joininfo,
childOID); appinfo);
/*
* Copy the parent's attr_needed data as well, with appropriate
* adjustment of relids and attribute numbers.
*/
pfree(childrel->attr_needed);
childrel->attr_needed =
adjust_appendrel_attr_needed(rel, appinfo,
childrel->min_attr,
childrel->max_attr);
/* /*
* If we can prove we don't need to scan this child via constraint * If we can prove we don't need to scan this child via constraint
* exclusion, just ignore it. (We have to have converted the * exclusion, just ignore it. (We have to have converted the
* baserestrictinfo Vars before we can make the test.) * baserestrictinfo Vars before we can make the test.)
*
* XXX it'd probably be better to give the child some kind of dummy
* cheapest path, or otherwise explicitly mark it as ignorable.
* Currently there is an ugly check in join_before_append() to handle
* excluded children.
*/ */
if (constraint_exclusion) childrte = rt_fetch(childRTindex, root->parse->rtable);
if (constraint_exclusion &&
childrte->rtekind == RTE_RELATION)
{ {
List *constraint_pred; List *constraint_pred;
constraint_pred = get_relation_constraints(childOID, childrel); constraint_pred = get_relation_constraints(childrte->relid,
childrel);
/* /*
* We do not currently enforce that CHECK constraints contain only * We do not currently enforce that CHECK constraints contain only
...@@ -363,7 +366,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, ...@@ -363,7 +366,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Var *parentvar = (Var *) lfirst(parentvars); Var *parentvar = (Var *) lfirst(parentvars);
Var *childvar = (Var *) lfirst(childvars); Var *childvar = (Var *) lfirst(childvars);
if (IsA(parentvar, Var) &&IsA(childvar, Var)) if (IsA(parentvar, Var) &&
IsA(childvar, Var))
{ {
int pndx = parentvar->varattno - rel->min_attr; int pndx = parentvar->varattno - rel->min_attr;
int cndx = childvar->varattno - childrel->min_attr; int cndx = childvar->varattno - childrel->min_attr;
...@@ -392,9 +396,9 @@ has_multiple_baserels(PlannerInfo *root) ...@@ -392,9 +396,9 @@ has_multiple_baserels(PlannerInfo *root)
int num_base_rels = 0; int num_base_rels = 0;
Index rti; Index rti;
for (rti = 1; rti < root->base_rel_array_size; rti++) for (rti = 1; rti < root->simple_rel_array_size; rti++)
{ {
RelOptInfo *brel = root->base_rel_array[rti]; RelOptInfo *brel = root->simple_rel_array[rti];
if (brel == NULL) if (brel == NULL)
continue; continue;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.98 2005/11/22 18:17:12 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.99 2006/01/31 21:39:23 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -35,6 +36,9 @@ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, ...@@ -35,6 +36,9 @@ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, JoinType jointype); List *restrictlist, JoinType jointype);
static void join_before_append(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinType jointype);
static List *select_mergejoin_clauses(RelOptInfo *joinrel, static List *select_mergejoin_clauses(RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *outerrel,
RelOptInfo *innerrel, RelOptInfo *innerrel,
...@@ -115,6 +119,13 @@ add_paths_to_joinrel(PlannerInfo *root, ...@@ -115,6 +119,13 @@ add_paths_to_joinrel(PlannerInfo *root,
if (enable_hashjoin) if (enable_hashjoin)
hash_inner_and_outer(root, joinrel, outerrel, innerrel, hash_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, jointype); restrictlist, jointype);
/*
* 5. If the inner relation is an append relation, consider joining
* the outer rel to each append member and then appending the results.
*/
if (innerrel->cheapest_total_path->pathtype == T_Append)
join_before_append(root, joinrel, outerrel, innerrel, jointype);
} }
/* /*
...@@ -777,6 +788,154 @@ hash_inner_and_outer(PlannerInfo *root, ...@@ -777,6 +788,154 @@ hash_inner_and_outer(PlannerInfo *root,
} }
} }
/*
* join_before_append
* Creates possible join paths for processing a single join relation
* 'joinrel' when the inner input is an append relation.
*
* The idea here is to swap the order of the APPEND and JOIN operators.
* This is only really helpful if it allows us to reduce the cost of
* scanning the members of the append relation, and so we only consider
* plans involving nestloops with inner indexscans. Also, since the APPEND
* will certainly yield an unsorted result, there's no point in considering
* any but the cheapest-total outer path.
*
* XXX this is a bit of a kluge, because the resulting plan has to evaluate
* the outer relation multiple times. Would be better to allow
* best_inner_indexscan to generate an AppendPath and not have this routine
* at all. But we can't do that without some executor changes (need a way
* to pass outer keys down through Append). FIXME later.
*
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'jointype' is the type of join to do
*/
static void
join_before_append(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype)
{
Path *outer_cheapest_total = outerrel->cheapest_total_path;
int parentRTindex = innerrel->relid;
List *append_paths = NIL;
ListCell *l;
/*
* Swapping JOIN with APPEND only works for inner joins, not outer joins.
* However, we can also handle a unique-ified outer path.
*/
switch (jointype)
{
case JOIN_INNER:
break;
case JOIN_UNIQUE_OUTER:
outer_cheapest_total = (Path *)
create_unique_path(root, outerrel, outer_cheapest_total);
break;
case JOIN_LEFT:
case JOIN_RIGHT:
case JOIN_FULL:
case JOIN_IN:
case JOIN_UNIQUE_INNER:
return; /* can't join this way */
default:
elog(ERROR, "unrecognized join type: %d",
(int) jointype);
break;
}
/*
* Generate suitable access paths for each member relation.
*/
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RelOptInfo *childrel;
Path *bestinnerjoin;
Relids joinrelids;
Relids *save_attr_needed;
RelOptInfo *this_joinrel;
List *this_restrictlist;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
childRTindex = appinfo->child_relid;
childrel = find_base_rel(root, childRTindex);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
/*
* If the child has no cheapest_total_path, assume it was deemed
* excludable by constraint exclusion (see set_append_rel_pathlist).
*/
if (childrel->cheapest_total_path == NULL)
{
Assert(constraint_exclusion);
continue;
}
/*
* Get the best innerjoin indexpath (if any) for this outer rel.
*/
bestinnerjoin = best_inner_indexscan(root, childrel,
outerrel->relids, JOIN_INNER);
/*
* If no luck on an indexpath for this rel, we'll still consider
* an Append substituting the cheapest-total inner path. This
* is only likely to win if there's at least one member rel for
* which an indexscan path does exist.
*/
if (!bestinnerjoin)
bestinnerjoin = childrel->cheapest_total_path;
/*
* We need a joinrel that describes this join accurately. Although
* the joinrel won't ever be used by the join path search algorithm
* in joinrels.c, it provides necessary context for the Path,
* such as properly-translated target and quals lists.
*/
joinrelids = bms_copy(joinrel->relids);
joinrelids = bms_del_member(joinrelids, parentRTindex);
joinrelids = bms_add_member(joinrelids, childRTindex);
/*
* Kluge: temporarily adjust the outer rel's attr_needed info so
* that it references the member rel instead of the appendrel.
* This is needed to build the correct targetlist for the joinrel.
*/
save_attr_needed = outerrel->attr_needed;
outerrel->attr_needed =
adjust_other_rel_attr_needed(outerrel, appinfo,
outerrel->min_attr,
outerrel->max_attr);
this_joinrel = build_join_rel(root, joinrelids, outerrel, childrel,
jointype, &this_restrictlist);
/* Now we can undo the hack on attr_needed */
pfree(outerrel->attr_needed);
outerrel->attr_needed = save_attr_needed;
/* Build Path for join and add to result list */
append_paths = lappend(append_paths,
create_nestloop_path(root,
this_joinrel,
JOIN_INNER,
outer_cheapest_total,
bestinnerjoin,
this_restrictlist,
NIL));
}
/* Form the completed Append path and add it to the join relation. */
add_path(joinrel, (Path *) create_append_path(joinrel, append_paths));
}
/* /*
* select_mergejoin_clauses * select_mergejoin_clauses
* Select mergejoin clauses that are usable for a particular join. * Select mergejoin clauses that are usable for a particular join.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.113 2005/12/20 02:30:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.114 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -74,8 +74,9 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); ...@@ -74,8 +74,9 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
* *
* At the end of this process, there should be one baserel RelOptInfo for * At the end of this process, there should be one baserel RelOptInfo for
* every non-join RTE that is used in the query. Therefore, this routine * every non-join RTE that is used in the query. Therefore, this routine
* is the only place that should call build_base_rel. But build_other_rel * is the only place that should call build_simple_rel with reloptkind
* will be used later to build rels for inheritance children. * RELOPT_BASEREL. However, otherrels will be built later for append relation
* members.
*/ */
void void
add_base_rels_to_query(PlannerInfo *root, Node *jtnode) add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
...@@ -86,7 +87,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) ...@@ -86,7 +87,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
{ {
int varno = ((RangeTblRef *) jtnode)->rtindex; int varno = ((RangeTblRef *) jtnode)->rtindex;
build_base_rel(root, varno); (void) build_simple_rel(root, varno, RELOPT_BASEREL);
} }
else if (IsA(jtnode, FromExpr)) else if (IsA(jtnode, FromExpr))
{ {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.91 2005/12/20 02:30:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.92 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -120,15 +120,15 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction, ...@@ -120,15 +120,15 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
&constant_quals); &constant_quals);
/* /*
* Init planner lists to empty. We create the base_rel_array with a size * Init planner lists to empty, and set up the array to hold RelOptInfos
* that will be sufficient if no pullups or inheritance additions happen * for "simple" rels.
* ... otherwise it will be enlarged as needed.
* *
* NOTE: in_info_list was set up by subquery_planner, do not touch here * NOTE: in_info_list and append_rel_list were set up by subquery_planner,
* do not touch here
*/ */
root->base_rel_array_size = list_length(parse->rtable) + 1; root->simple_rel_array_size = list_length(parse->rtable) + 1;
root->base_rel_array = (RelOptInfo **) root->simple_rel_array = (RelOptInfo **)
palloc0(root->base_rel_array_size * sizeof(RelOptInfo *)); palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *));
root->join_rel_list = NIL; root->join_rel_list = NIL;
root->join_rel_hash = NULL; root->join_rel_hash = NULL;
root->equi_key_list = NIL; root->equi_key_list = NIL;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.196 2005/12/20 02:30:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.197 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,16 +47,17 @@ ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */ ...@@ -47,16 +47,17 @@ ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */
/* Expression kind codes for preprocess_expression */ /* Expression kind codes for preprocess_expression */
#define EXPRKIND_QUAL 0 #define EXPRKIND_QUAL 0
#define EXPRKIND_TARGET 1 #define EXPRKIND_TARGET 1
#define EXPRKIND_RTFUNC 2 #define EXPRKIND_RTFUNC 2
#define EXPRKIND_LIMIT 3 #define EXPRKIND_LIMIT 3
#define EXPRKIND_ININFO 4 #define EXPRKIND_ININFO 4
#define EXPRKIND_APPINFO 5
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode); static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
static Plan *inheritance_planner(PlannerInfo *root, List *inheritlist); static Plan *inheritance_planner(PlannerInfo *root);
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction); static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
static double preprocess_limit(PlannerInfo *root, static double preprocess_limit(PlannerInfo *root,
double tuple_fraction, double tuple_fraction,
...@@ -194,7 +195,6 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -194,7 +195,6 @@ subquery_planner(Query *parse, double tuple_fraction,
PlannerInfo *root; PlannerInfo *root;
Plan *plan; Plan *plan;
List *newHaving; List *newHaving;
List *lst;
ListCell *l; ListCell *l;
/* Set up for a new level of subquery */ /* Set up for a new level of subquery */
...@@ -204,6 +204,8 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -204,6 +204,8 @@ subquery_planner(Query *parse, double tuple_fraction,
/* Create a PlannerInfo data structure for this subquery */ /* Create a PlannerInfo data structure for this subquery */
root = makeNode(PlannerInfo); root = makeNode(PlannerInfo);
root->parse = parse; root->parse = parse;
root->in_info_list = NIL;
root->append_rel_list = NIL;
/* /*
* Look for IN clauses at the top level of WHERE, and transform them into * Look for IN clauses at the top level of WHERE, and transform them into
...@@ -211,7 +213,6 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -211,7 +213,6 @@ subquery_planner(Query *parse, double tuple_fraction,
* level of WHERE; if we pull up any subqueries in the next step, their * level of WHERE; if we pull up any subqueries in the next step, their
* INs are processed just before pulling them up. * INs are processed just before pulling them up.
*/ */
root->in_info_list = NIL;
if (parse->hasSubLinks) if (parse->hasSubLinks)
parse->jointree->quals = pull_up_IN_clauses(root, parse->jointree->quals = pull_up_IN_clauses(root,
parse->jointree->quals); parse->jointree->quals);
...@@ -252,6 +253,16 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -252,6 +253,16 @@ subquery_planner(Query *parse, double tuple_fraction,
} }
} }
/*
* Expand any rangetable entries that are inheritance sets into "append
* relations". This can add entries to the rangetable, but they must be
* plain base relations not joins, so it's OK (and marginally more
* efficient) to do it after checking for join RTEs. We must do it after
* pulling up subqueries, else we'd fail to handle inherited tables in
* subqueries.
*/
expand_inherited_tables(root);
/* /*
* Set hasHavingQual to remember if HAVING clause is present. Needed * Set hasHavingQual to remember if HAVING clause is present. Needed
* because preprocess_expression will reduce a constant-true condition to * because preprocess_expression will reduce a constant-true condition to
...@@ -279,6 +290,9 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -279,6 +290,9 @@ subquery_planner(Query *parse, double tuple_fraction,
root->in_info_list = (List *) root->in_info_list = (List *)
preprocess_expression(root, (Node *) root->in_info_list, preprocess_expression(root, (Node *) root->in_info_list,
EXPRKIND_ININFO); EXPRKIND_ININFO);
root->append_rel_list = (List *)
preprocess_expression(root, (Node *) root->append_rel_list,
EXPRKIND_APPINFO);
/* Also need to preprocess expressions for function RTEs */ /* Also need to preprocess expressions for function RTEs */
foreach(l, parse->rtable) foreach(l, parse->rtable)
...@@ -357,8 +371,8 @@ subquery_planner(Query *parse, double tuple_fraction, ...@@ -357,8 +371,8 @@ subquery_planner(Query *parse, double tuple_fraction,
* needs special processing, else go straight to grouping_planner. * needs special processing, else go straight to grouping_planner.
*/ */
if (parse->resultRelation && if (parse->resultRelation &&
(lst = expand_inherited_rtentry(root, parse->resultRelation)) != NIL) rt_fetch(parse->resultRelation, parse->rtable)->inh)
plan = inheritance_planner(root, lst); plan = inheritance_planner(root);
else else
plan = grouping_planner(root, tuple_fraction); plan = grouping_planner(root, tuple_fraction);
...@@ -504,44 +518,50 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode) ...@@ -504,44 +518,50 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode)
(int) nodeTag(jtnode)); (int) nodeTag(jtnode));
} }
/*-------------------- /*
* inheritance_planner * inheritance_planner
* Generate a plan in the case where the result relation is an * Generate a plan in the case where the result relation is an
* inheritance set. * inheritance set.
* *
* We have to handle this case differently from cases where a source * We have to handle this case differently from cases where a source relation
* relation is an inheritance set. Source inheritance is expanded at * is an inheritance set. Source inheritance is expanded at the bottom of the
* the bottom of the plan tree (see allpaths.c), but target inheritance * plan tree (see allpaths.c), but target inheritance has to be expanded at
* has to be expanded at the top. The reason is that for UPDATE, each * the top. The reason is that for UPDATE, each target relation needs a
* target relation needs a different targetlist matching its own column * different targetlist matching its own column set. Also, for both UPDATE
* set. (This is not so critical for DELETE, but for simplicity we treat * and DELETE, the executor needs the Append plan node at the top, else it
* inherited DELETE the same way.) Fortunately, the UPDATE/DELETE target * can't keep track of which table is the current target table. Fortunately,
* can never be the nullable side of an outer join, so it's OK to generate * the UPDATE/DELETE target can never be the nullable side of an outer join,
* the plan this way. * so it's OK to generate the plan this way.
*
* inheritlist is an integer list of RT indexes for the result relation set.
* *
* Returns a query plan. * Returns a query plan.
*--------------------
*/ */
static Plan * static Plan *
inheritance_planner(PlannerInfo *root, List *inheritlist) inheritance_planner(PlannerInfo *root)
{ {
Query *parse = root->parse; Query *parse = root->parse;
int parentRTindex = parse->resultRelation; int parentRTindex = parse->resultRelation;
Oid parentOID = getrelid(parentRTindex, parse->rtable);
int mainrtlength = list_length(parse->rtable);
List *subplans = NIL; List *subplans = NIL;
List *tlist = NIL; List *tlist = NIL;
PlannerInfo subroot;
ListCell *l; ListCell *l;
foreach(l, inheritlist) subroot.parse = NULL; /* catch it if no matches in loop */
parse->resultRelations = NIL;
foreach(l, root->append_rel_list)
{ {
int childRTindex = lfirst_int(l); AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
Oid childOID = getrelid(childRTindex, parse->rtable);
PlannerInfo subroot;
Plan *subplan; Plan *subplan;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
/* Build target-relations list for the executor */
parse->resultRelations = lappend_int(parse->resultRelations,
appinfo->child_relid);
/* /*
* Generate modified query with this rel as target. We have to be * Generate modified query with this rel as target. We have to be
* prepared to translate varnos in in_info_list as well as in the * prepared to translate varnos in in_info_list as well as in the
...@@ -549,14 +569,12 @@ inheritance_planner(PlannerInfo *root, List *inheritlist) ...@@ -549,14 +569,12 @@ inheritance_planner(PlannerInfo *root, List *inheritlist)
*/ */
memcpy(&subroot, root, sizeof(PlannerInfo)); memcpy(&subroot, root, sizeof(PlannerInfo));
subroot.parse = (Query *) subroot.parse = (Query *)
adjust_inherited_attrs((Node *) parse, adjust_appendrel_attrs((Node *) parse,
parentRTindex, parentOID, appinfo);
childRTindex, childOID);
subroot.in_info_list = (List *) subroot.in_info_list = (List *)
adjust_inherited_attrs((Node *) root->in_info_list, adjust_appendrel_attrs((Node *) root->in_info_list,
parentRTindex, parentOID, appinfo);
childRTindex, childOID); /* There shouldn't be any OJ info to translate, as yet */
/* There shouldn't be any OJ info to translate, though */
Assert(subroot.oj_info_list == NIL); Assert(subroot.oj_info_list == NIL);
/* Generate plan */ /* Generate plan */
...@@ -564,48 +582,23 @@ inheritance_planner(PlannerInfo *root, List *inheritlist) ...@@ -564,48 +582,23 @@ inheritance_planner(PlannerInfo *root, List *inheritlist)
subplans = lappend(subplans, subplan); subplans = lappend(subplans, subplan);
/*
* XXX my goodness this next bit is ugly. Really need to think about
* ways to rein in planner's habit of scribbling on its input.
*
* Planning of the subquery might have modified the rangetable, either
* by addition of RTEs due to expansion of inherited source tables, or
* by changes of the Query structures inside subquery RTEs. We have
* to ensure that this gets propagated back to the master copy.
* However, if we aren't done planning yet, we also need to ensure
* that subsequent calls to grouping_planner have virgin sub-Queries
* to work from. So, if we are at the last list entry, just copy the
* subquery rangetable back to the master copy; if we are not, then
* extend the master copy by adding whatever the subquery added. (We
* assume these added entries will go untouched by the future
* grouping_planner calls. We are also effectively assuming that
* sub-Queries will get planned identically each time, or at least
* that the impacts on their rangetables will be the same each time.
* Did I say this is ugly?)
*/
if (lnext(l) == NULL)
parse->rtable = subroot.parse->rtable;
else
{
int subrtlength = list_length(subroot.parse->rtable);
if (subrtlength > mainrtlength)
{
List *subrt;
subrt = list_copy_tail(subroot.parse->rtable, mainrtlength);
parse->rtable = list_concat(parse->rtable, subrt);
mainrtlength = subrtlength;
}
}
/* Save preprocessed tlist from first rel for use in Append */ /* Save preprocessed tlist from first rel for use in Append */
if (tlist == NIL) if (tlist == NIL)
tlist = subplan->targetlist; tlist = subplan->targetlist;
} }
/* Save the target-relations list for the executor, too */ /*
parse->resultRelations = inheritlist; * Planning might have modified the rangetable, due to changes of the
* Query structures inside subquery RTEs. We have to ensure that this
* gets propagated back to the master copy. But can't do this until we
* are done planning, because all the calls to grouping_planner need
* virgin sub-Queries to work from. (We are effectively assuming that
* sub-Queries will get planned identically each time, or at least that
* the impacts on their rangetables will be the same each time.)
*
* XXX should clean this up someday
*/
parse->rtable = subroot.parse->rtable;
/* Mark result as unordered (probably unnecessary) */ /* Mark result as unordered (probably unnecessary) */
root->query_pathkeys = NIL; root->query_pathkeys = NIL;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.33 2005/12/20 02:30:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.34 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -176,12 +176,13 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) ...@@ -176,12 +176,13 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
*/ */
subroot = makeNode(PlannerInfo); subroot = makeNode(PlannerInfo);
subroot->parse = subquery; subroot->parse = subquery;
subroot->in_info_list = NIL;
subroot->append_rel_list = NIL;
/* /*
* Pull up any IN clauses within the subquery's WHERE, so that we * Pull up any IN clauses within the subquery's WHERE, so that we
* don't leave unoptimized INs behind. * don't leave unoptimized INs behind.
*/ */
subroot->in_info_list = NIL;
if (subquery->hasSubLinks) if (subquery->hasSubLinks)
subquery->jointree->quals = pull_up_IN_clauses(subroot, subquery->jointree->quals = pull_up_IN_clauses(subroot,
subquery->jointree->quals); subquery->jointree->quals);
...@@ -228,11 +229,12 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) ...@@ -228,11 +229,12 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
/* /*
* Adjust level-0 varnos in subquery so that we can append its * Adjust level-0 varnos in subquery so that we can append its
* rangetable to upper query's. We have to fix the subquery's * rangetable to upper query's. We have to fix the subquery's
* in_info_list, as well. * in_info_list and append_rel_list, as well.
*/ */
rtoffset = list_length(parse->rtable); rtoffset = list_length(parse->rtable);
OffsetVarNodes((Node *) subquery, rtoffset, 0); OffsetVarNodes((Node *) subquery, rtoffset, 0);
OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0); OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0);
OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
/* /*
* Upper-level vars in subquery are now one level closer to their * Upper-level vars in subquery are now one level closer to their
...@@ -240,6 +242,7 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) ...@@ -240,6 +242,7 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
*/ */
IncrementVarSublevelsUp((Node *) subquery, -1, 1); IncrementVarSublevelsUp((Node *) subquery, -1, 1);
IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1); IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1);
IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
/* /*
* Replace all of the top query's references to the subquery's * Replace all of the top query's references to the subquery's
...@@ -263,6 +266,10 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) ...@@ -263,6 +266,10 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
ResolveNew((Node *) root->in_info_list, ResolveNew((Node *) root->in_info_list,
varno, 0, rte, varno, 0, rte,
subtlist, CMD_SELECT, 0); subtlist, CMD_SELECT, 0);
root->append_rel_list = (List *)
ResolveNew((Node *) root->append_rel_list,
varno, 0, rte,
subtlist, CMD_SELECT, 0);
foreach(rt, parse->rtable) foreach(rt, parse->rtable)
{ {
...@@ -327,6 +334,15 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) ...@@ -327,6 +334,15 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
root->in_info_list = list_concat(root->in_info_list, root->in_info_list = list_concat(root->in_info_list,
subroot->in_info_list); subroot->in_info_list);
/*
* XXX need to do something about adjusting AppendRelInfos too
*/
Assert(root->append_rel_list == NIL);
/* Also pull up any subquery AppendRelInfos */
root->append_rel_list = list_concat(root->append_rel_list,
subroot->append_rel_list);
/* /*
* We don't have to do the equivalent bookkeeping for outer-join * We don't have to do the equivalent bookkeeping for outer-join
* info, because that hasn't been set up yet. * info, because that hasn't been set up yet.
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.206 2006/01/25 20:29:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.207 2006/01/31 21:39:24 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -3209,6 +3209,15 @@ expression_tree_walker(Node *node, ...@@ -3209,6 +3209,15 @@ expression_tree_walker(Node *node,
return true; return true;
} }
break; break;
case T_AppendRelInfo:
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
if (expression_tree_walker((Node *) appinfo->translated_vars,
walker, context))
return true;
}
break;
default: default:
elog(ERROR, "unrecognized node type: %d", elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node)); (int) nodeTag(node));
...@@ -3744,6 +3753,16 @@ expression_tree_mutator(Node *node, ...@@ -3744,6 +3753,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_AppendRelInfo:
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
AppendRelInfo *newnode;
FLATCOPY(newnode, appinfo, AppendRelInfo);
MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
return (Node *) newnode;
}
break;
default: default:
elog(ERROR, "unrecognized node type: %d", elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node)); (int) nodeTag(node));
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.116 2006/01/05 10:07:45 petere Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.117 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -70,24 +70,11 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) ...@@ -70,24 +70,11 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
List *indexinfos = NIL; List *indexinfos = NIL;
/* /*
* Normally, we can assume the rewriter already acquired at least * We need not lock the relation since it was already locked, either
* AccessShareLock on each relation used in the query. However this will * by the rewriter or when expand_inherited_rtentry() added it to the
* not be the case for relations added to the query because they are * query's rangetable.
* inheritance children of some relation mentioned explicitly. For them,
* this is the first access during the parse/rewrite/plan pipeline, and so
* we need to obtain and keep a suitable lock.
*
* XXX really, a suitable lock is RowShareLock if the relation is an
* UPDATE/DELETE target, and AccessShareLock otherwise. However we cannot
* easily tell here which to get, so for the moment just get
* AccessShareLock always. The executor will get the right lock when it
* runs, which means there is a very small chance of deadlock trying to
* upgrade our lock.
*/ */
if (rel->reloptkind == RELOPT_BASEREL) relation = heap_open(relationObjectId, NoLock);
relation = heap_open(relationObjectId, NoLock);
else
relation = heap_open(relationObjectId, AccessShareLock);
rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1; rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
rel->max_attr = RelationGetNumberOfAttributes(relation); rel->max_attr = RelationGetNumberOfAttributes(relation);
...@@ -224,7 +211,6 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) ...@@ -224,7 +211,6 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
rel->indexlist = indexinfos; rel->indexlist = indexinfos;
/* close rel, but keep lock if any */
heap_close(relation, NoLock); heap_close(relation, NoLock);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.74 2005/12/20 02:30:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.75 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,8 +30,6 @@ typedef struct JoinHashEntry ...@@ -30,8 +30,6 @@ typedef struct JoinHashEntry
RelOptInfo *join_rel; RelOptInfo *join_rel;
} JoinHashEntry; } JoinHashEntry;
static RelOptInfo *make_reloptinfo(PlannerInfo *root, int relid,
RelOptKind reloptkind);
static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *input_rel); RelOptInfo *input_rel);
static List *build_joinrel_restrictlist(PlannerInfo *root, static List *build_joinrel_restrictlist(PlannerInfo *root,
...@@ -49,71 +47,25 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel, ...@@ -49,71 +47,25 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/* /*
* build_base_rel * build_simple_rel
* Construct a new base relation RelOptInfo, and put it in the query's * Construct a new RelOptInfo for a base relation or 'other' relation.
* base_rel_array.
*/
void
build_base_rel(PlannerInfo *root, int relid)
{
Assert(relid > 0);
/* Rel should not exist already */
if (relid < root->base_rel_array_size &&
root->base_rel_array[relid] != NULL)
elog(ERROR, "rel already exists");
/* No existing RelOptInfo for this base rel, so make a new one */
(void) make_reloptinfo(root, relid, RELOPT_BASEREL);
}
/*
* build_other_rel
* Returns relation entry corresponding to 'relid', creating a new one
* if necessary. This is for 'other' relations, which are much like
* base relations except that they have a different RelOptKind.
*/ */
RelOptInfo * RelOptInfo *
build_other_rel(PlannerInfo *root, int relid) build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
{ {
RelOptInfo *rel; RelOptInfo *rel;
RangeTblEntry *rte;
Assert(relid > 0); /* Fetch RTE for relation */
Assert(relid > 0 && relid <= list_length(root->parse->rtable));
/* Already made? */ rte = rt_fetch(relid, root->parse->rtable);
if (relid < root->base_rel_array_size)
{
rel = root->base_rel_array[relid];
if (rel)
{
/* it should not exist as a base rel */
if (rel->reloptkind == RELOPT_BASEREL)
elog(ERROR, "rel already exists as base rel");
/* otherwise, A-OK */
return rel;
}
}
/* No existing RelOptInfo for this other rel, so make a new one */ /* Rel should not exist already */
/* presently, must be an inheritance child rel */ Assert(relid < root->simple_rel_array_size);
rel = make_reloptinfo(root, relid, RELOPT_OTHER_CHILD_REL); if (root->simple_rel_array[relid] != NULL)
elog(ERROR, "rel %d already exists", relid);
return rel;
}
/*
* make_reloptinfo
* Construct a RelOptInfo for the specified rangetable index,
* and enter it into base_rel_array.
*
* Common code for build_base_rel and build_other_rel.
*/
static RelOptInfo *
make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind)
{
RelOptInfo *rel = makeNode(RelOptInfo);
RangeTblEntry *rte = rt_fetch(relid, root->parse->rtable);
rel = makeNode(RelOptInfo);
rel->reloptkind = reloptkind; rel->reloptkind = reloptkind;
rel->relids = bms_make_singleton(relid); rel->relids = bms_make_singleton(relid);
rel->rows = 0; rel->rows = 0;
...@@ -161,21 +113,8 @@ make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind) ...@@ -161,21 +113,8 @@ make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind)
break; break;
} }
/* Add the finished struct to the base_rel_array */ /* Save the finished struct in the query's simple_rel_array */
if (relid >= root->base_rel_array_size) root->simple_rel_array[relid] = rel;
{
int oldsize = root->base_rel_array_size;
int newsize;
newsize = Max(oldsize * 2, relid + 1);
root->base_rel_array = (RelOptInfo **)
repalloc(root->base_rel_array, newsize * sizeof(RelOptInfo *));
MemSet(root->base_rel_array + oldsize, 0,
(newsize - oldsize) * sizeof(RelOptInfo *));
root->base_rel_array_size = newsize;
}
root->base_rel_array[relid] = rel;
return rel; return rel;
} }
...@@ -191,9 +130,9 @@ find_base_rel(PlannerInfo *root, int relid) ...@@ -191,9 +130,9 @@ find_base_rel(PlannerInfo *root, int relid)
Assert(relid > 0); Assert(relid > 0);
if (relid < root->base_rel_array_size) if (relid < root->simple_rel_array_size)
{ {
rel = root->base_rel_array[relid]; rel = root->simple_rel_array[relid];
if (rel) if (rel)
return rel; return rel;
} }
...@@ -446,12 +385,24 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, ...@@ -446,12 +385,24 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
foreach(vars, input_rel->reltargetlist) foreach(vars, input_rel->reltargetlist)
{ {
Var *var = (Var *) lfirst(vars); Var *origvar = (Var *) lfirst(vars);
Var *var;
RelOptInfo *baserel; RelOptInfo *baserel;
int ndx; int ndx;
/* We can't run into any child RowExprs here */ /*
Assert(IsA(var, Var)); * We can't run into any child RowExprs here, but we could find
* a whole-row Var with a ConvertRowtypeExpr atop it.
*/
var = origvar;
while (!IsA(var, Var))
{
if (IsA(var, ConvertRowtypeExpr))
var = (Var *) ((ConvertRowtypeExpr *) var)->arg;
else
elog(ERROR, "unexpected node type in reltargetlist: %d",
(int) nodeTag(var));
}
/* Get the Var's original base rel */ /* Get the Var's original base rel */
baserel = find_base_rel(root, var->varno); baserel = find_base_rel(root, var->varno);
...@@ -461,8 +412,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, ...@@ -461,8 +412,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids)) if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
{ {
/* Yup, add it to the output */ /* Yup, add it to the output */
joinrel->reltargetlist = lappend(joinrel->reltargetlist, var); joinrel->reltargetlist = lappend(joinrel->reltargetlist, origvar);
Assert(baserel->attr_widths[ndx] > 0);
joinrel->width += baserel->attr_widths[ndx]; joinrel->width += baserel->attr_widths[ndx];
} }
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.95 2006/01/06 20:11:12 tgl Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.96 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -183,6 +183,17 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) ...@@ -183,6 +183,17 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
} }
/* fall through to examine children */ /* fall through to examine children */
} }
if (IsA(node, AppendRelInfo))
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
if (context->sublevels_up == 0)
{
appinfo->parent_relid += context->offset;
appinfo->child_relid += context->offset;
}
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
...@@ -323,6 +334,19 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) ...@@ -323,6 +334,19 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
} }
/* fall through to examine children */ /* fall through to examine children */
} }
if (IsA(node, AppendRelInfo))
{
AppendRelInfo *appinfo = (AppendRelInfo *) node;
if (context->sublevels_up == 0)
{
if (appinfo->parent_relid == context->rt_index)
appinfo->parent_relid = context->new_index;
if (appinfo->child_relid == context->rt_index)
appinfo->child_relid = context->new_index;
}
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
...@@ -527,16 +551,11 @@ rangeTableEntry_used_walker(Node *node, ...@@ -527,16 +551,11 @@ rangeTableEntry_used_walker(Node *node,
return true; return true;
/* fall through to examine children */ /* fall through to examine children */
} }
if (IsA(node, InClauseInfo)) /* Shouldn't need to handle planner auxiliary nodes here */
{ Assert(!IsA(node, OuterJoinInfo));
InClauseInfo *ininfo = (InClauseInfo *) node; Assert(!IsA(node, InClauseInfo));
Assert(!IsA(node, AppendRelInfo));
if (context->sublevels_up == 0 &&
(bms_is_member(context->rt_index, ininfo->lefthand) ||
bms_is_member(context->rt_index, ininfo->righthand)))
return true;
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.181 2006/01/01 01:41:42 neilc Exp $ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.182 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -190,6 +190,7 @@ typedef enum NodeTag ...@@ -190,6 +190,7 @@ typedef enum NodeTag
T_InnerIndexscanInfo, T_InnerIndexscanInfo,
T_OuterJoinInfo, T_OuterJoinInfo,
T_InClauseInfo, T_InClauseInfo,
T_AppendRelInfo,
/* /*
* TAGS FOR MEMORY NODES (memnodes.h) * TAGS FOR MEMORY NODES (memnodes.h)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.122 2005/12/20 02:30:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.123 2006/01/31 21:39:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -52,7 +52,7 @@ typedef struct QualCost ...@@ -52,7 +52,7 @@ typedef struct QualCost
* *
* This struct is conventionally called "root" in all the planner routines. * This struct is conventionally called "root" in all the planner routines.
* It holds links to all of the planner's working state, in addition to the * It holds links to all of the planner's working state, in addition to the
* original Query. Note that at present the planner extensively manipulates * original Query. Note that at present the planner extensively modifies
* the passed-in Query data structure; someday that should stop. * the passed-in Query data structure; someday that should stop.
*---------- *----------
*/ */
...@@ -63,14 +63,14 @@ typedef struct PlannerInfo ...@@ -63,14 +63,14 @@ typedef struct PlannerInfo
Query *parse; /* the Query being planned */ Query *parse; /* the Query being planned */
/* /*
* base_rel_array holds pointers to "base rels" and "other rels" (see * simple_rel_array holds pointers to "base rels" and "other rels" (see
* comments for RelOptInfo for more info). It is indexed by rangetable * comments for RelOptInfo for more info). It is indexed by rangetable
* index (so entry 0 is always wasted). Entries can be NULL when an RTE * index (so entry 0 is always wasted). Entries can be NULL when an RTE
* does not correspond to a base relation. Note that the array may be * does not correspond to a base relation, such as a join RTE or an
* enlarged on-the-fly. * unreferenced view RTE; or if the RelOptInfo hasn't been made yet.
*/ */
struct RelOptInfo **base_rel_array; /* All one-relation RelOptInfos */ struct RelOptInfo **simple_rel_array; /* All 1-relation RelOptInfos */
int base_rel_array_size; /* current allocated array len */ int simple_rel_array_size; /* allocated size of array */
/* /*
* join_rel_list is a list of all join-relation RelOptInfos we have * join_rel_list is a list of all join-relation RelOptInfos we have
...@@ -101,6 +101,8 @@ typedef struct PlannerInfo ...@@ -101,6 +101,8 @@ typedef struct PlannerInfo
List *in_info_list; /* list of InClauseInfos */ List *in_info_list; /* list of InClauseInfos */
List *append_rel_list; /* list of AppendRelInfos */
List *query_pathkeys; /* desired pathkeys for query_planner(), and List *query_pathkeys; /* desired pathkeys for query_planner(), and
* actual pathkeys afterwards */ * actual pathkeys afterwards */
...@@ -125,7 +127,7 @@ typedef struct PlannerInfo ...@@ -125,7 +127,7 @@ typedef struct PlannerInfo
* is the joining of two or more base rels. A joinrel is identified by * is the joining of two or more base rels. A joinrel is identified by
* the set of RT indexes for its component baserels. We create RelOptInfo * the set of RT indexes for its component baserels. We create RelOptInfo
* nodes for each baserel and joinrel, and store them in the PlannerInfo's * nodes for each baserel and joinrel, and store them in the PlannerInfo's
* base_rel_array and join_rel_list respectively. * simple_rel_array and join_rel_list respectively.
* *
* Note that there is only one joinrel for any given set of component * Note that there is only one joinrel for any given set of component
* baserels, no matter what order we assemble them in; so an unordered * baserels, no matter what order we assemble them in; so an unordered
...@@ -135,16 +137,15 @@ typedef struct PlannerInfo ...@@ -135,16 +137,15 @@ typedef struct PlannerInfo
* single RT indexes; but they are not part of the join tree, and are given * single RT indexes; but they are not part of the join tree, and are given
* a different RelOptKind to identify them. * a different RelOptKind to identify them.
* *
* Currently the only kind of otherrels are those made for child relations * Currently the only kind of otherrels are those made for member relations
* of an inheritance scan (SELECT FROM foo*). The parent table's RTE and * of an "append relation", that is an inheritance set or UNION ALL subquery.
* corresponding baserel represent the whole result of the inheritance scan. * An append relation has a parent RTE that is a base rel, which represents
* The planner creates separate RTEs and associated RelOptInfos for each child * the entire append relation. The member RTEs are otherrels. The parent
* table (including the parent table, in its capacity as a member of the * is present in the query join tree but the members are not. The member
* inheritance set). These RelOptInfos are physically identical to baserels, * RTEs and otherrels are used to plan the scans of the individual tables or
* but are otherrels because they are not in the main join tree. These added * subqueries of the append set; then the parent baserel is given an Append
* RTEs and otherrels are used to plan the scans of the individual tables in * plan comprising the best plans for the individual member rels. (See
* the inheritance set; then the parent baserel is given an Append plan * comments for AppendRelInfo for more information.)
* comprising the best plans for the individual child tables.
* *
* At one time we also made otherrels to represent join RTEs, for use in * At one time we also made otherrels to represent join RTEs, for use in
* handling join alias Vars. Currently this is not needed because all join * handling join alias Vars. Currently this is not needed because all join
...@@ -192,7 +193,7 @@ typedef struct PlannerInfo ...@@ -192,7 +193,7 @@ typedef struct PlannerInfo
* upon creation of the RelOptInfo object; they are filled in when * upon creation of the RelOptInfo object; they are filled in when
* set_base_rel_pathlist processes the object. * set_base_rel_pathlist processes the object.
* *
* For otherrels that are inheritance children, these fields are filled * For otherrels that are appendrel members, these fields are filled
* in just as for a baserel. * in just as for a baserel.
* *
* The presence of the remaining fields depends on the restrictions * The presence of the remaining fields depends on the restrictions
...@@ -232,7 +233,7 @@ typedef enum RelOptKind ...@@ -232,7 +233,7 @@ typedef enum RelOptKind
{ {
RELOPT_BASEREL, RELOPT_BASEREL,
RELOPT_JOINREL, RELOPT_JOINREL,
RELOPT_OTHER_CHILD_REL RELOPT_OTHER_MEMBER_REL
} RelOptKind; } RelOptKind;
typedef struct RelOptInfo typedef struct RelOptInfo
...@@ -508,8 +509,7 @@ typedef struct TidPath ...@@ -508,8 +509,7 @@ typedef struct TidPath
/* /*
* AppendPath represents an Append plan, ie, successive execution of * AppendPath represents an Append plan, ie, successive execution of
* several member plans. Currently it is only used to handle expansion * several member plans.
* of inheritance trees.
* *
* Note: it is possible for "subpaths" to contain only one, or even no, * Note: it is possible for "subpaths" to contain only one, or even no,
* elements. These cases are optimized during create_append_plan. * elements. These cases are optimized during create_append_plan.
...@@ -879,4 +879,96 @@ typedef struct InClauseInfo ...@@ -879,4 +879,96 @@ typedef struct InClauseInfo
*/ */
} InClauseInfo; } InClauseInfo;
/*
* Append-relation info.
*
* When we expand an inheritable table or a UNION-ALL subselect into an
* "append relation" (essentially, a list of child RTEs), we build an
* AppendRelInfo for each child RTE. The list of AppendRelInfos indicates
* which child RTEs must be included when expanding the parent, and each
* node carries information needed to translate Vars referencing the parent
* into Vars referencing that child.
*
* These structs are kept in the PlannerInfo node's append_rel_list.
* Note that we just throw all the structs into one list, and scan the
* whole list when desiring to expand any one parent. We could have used
* a more complex data structure (eg, one list per parent), but this would
* be harder to update during operations such as pulling up subqueries,
* and not really any easier to scan. Considering that typical queries
* will not have many different append parents, it doesn't seem worthwhile
* to complicate things.
*
* Note: after completion of the planner prep phase, any given RTE is an
* append parent having entries in append_rel_list if and only if its
* "inh" flag is set. We clear "inh" for plain tables that turn out not
* to have inheritance children, and (in an abuse of the original meaning
* of the flag) we set "inh" for subquery RTEs that turn out to be
* flattenable UNION ALL queries. This lets us avoid useless searches
* of append_rel_list.
*
* Note: the data structure assumes that append-rel members are single
* baserels. This is OK for inheritance, but it prevents us from pulling
* up a UNION ALL member subquery if it contains a join. While that could
* be fixed with a more complex data structure, at present there's not much
* point because no improvement in the plan could result.
*/
typedef struct AppendRelInfo
{
NodeTag type;
/*
* These fields uniquely identify this append relationship. There
* can be (in fact, always should be) multiple AppendRelInfos for the
* same parent_relid, but never more than one per child_relid, since
* a given RTE cannot be a child of more than one append parent.
*/
Index parent_relid; /* RT index of append parent rel */
Index child_relid; /* RT index of append child rel */
/*
* For an inheritance appendrel, the parent and child are both regular
* relations, and we store their rowtype OIDs here for use in translating
* whole-row Vars. For a UNION-ALL appendrel, the parent and child are
* both subqueries with no named rowtype, and we store InvalidOid here.
*/
Oid parent_reltype; /* OID of parent's composite type */
Oid child_reltype; /* OID of child's composite type */
/*
* The N'th element of this list is the integer column number of
* the child column corresponding to the N'th column of the parent.
* A list element is zero if it corresponds to a dropped column of the
* parent (this is only possible for inheritance cases, not UNION ALL).
*/
List *col_mappings; /* list of child attribute numbers */
/*
* The N'th element of this list is a Var or expression representing
* the child column corresponding to the N'th column of the parent.
* This is used to translate Vars referencing the parent rel into
* references to the child. A list element is NULL if it corresponds
* to a dropped column of the parent (this is only possible for
* inheritance cases, not UNION ALL).
*
* This might seem redundant with the col_mappings data, but it is handy
* because flattening of sub-SELECTs that are members of a UNION ALL
* will cause changes in the expressions that need to be substituted
* for a parent Var. Adjusting this data structure lets us track what
* really needs to be substituted.
*
* Notice we only store entries for user columns (attno > 0). Whole-row
* Vars are special-cased, and system columns (attno < 0) need no
* special translation since their attnos are the same for all tables.
*
* Caution: the Vars have varlevelsup = 0. Be careful to adjust
* as needed when copying into a subquery.
*/
List *translated_vars; /* Expressions in the child's Vars */
/*
* We store the parent table's OID here for inheritance, or InvalidOid
* for UNION ALL. This is only needed to help in generating error
* messages if an attempt is made to reference a dropped parent column.
*/
Oid parent_reloid; /* OID of parent relation */
} AppendRelInfo;
#endif /* RELATION_H */ #endif /* RELATION_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.63 2005/11/26 22:14:57 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.64 2006/01/31 21:39:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -85,8 +85,8 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root, ...@@ -85,8 +85,8 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root,
/* /*
* prototypes for relnode.c * prototypes for relnode.c
*/ */
extern void build_base_rel(PlannerInfo *root, int relid); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
extern RelOptInfo *build_other_rel(PlannerInfo *root, int relid); RelOptKind reloptkind);
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid); extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids); extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern RelOptInfo *build_join_rel(PlannerInfo *root, extern RelOptInfo *build_join_rel(PlannerInfo *root,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.53 2005/12/20 02:30:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.54 2006/01/31 21:39:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,10 +46,18 @@ extern Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction, ...@@ -46,10 +46,18 @@ extern Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction,
extern List *find_all_inheritors(Oid parentrel); extern List *find_all_inheritors(Oid parentrel);
extern List *expand_inherited_rtentry(PlannerInfo *root, Index rti); extern void expand_inherited_tables(PlannerInfo *root);
extern Node *adjust_inherited_attrs(Node *node, extern Node *adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo);
Index old_rt_index, Oid old_relid,
Index new_rt_index, Oid new_relid); extern Relids *adjust_appendrel_attr_needed(RelOptInfo *oldrel,
AppendRelInfo *appinfo,
AttrNumber new_min_attr,
AttrNumber new_max_attr);
extern Relids *adjust_other_rel_attr_needed(RelOptInfo *oldrel,
AppendRelInfo *appinfo,
AttrNumber new_min_attr,
AttrNumber new_max_attr);
#endif /* PREP_H */ #endif /* PREP_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment