aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2006-01-31 21:39:25 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2006-01-31 21:39:25 +0000
commit8a1468af4e5c50dde8ce938886f4672459009b9b (patch)
treeeb9ae5decb8be1ebca8d124d947d0b7282802953 /src/backend
parent097df388b72584501762ac4c400dccff8a3671eb (diff)
downloadpostgresql-8a1468af4e5c50dde8ce938886f4672459009b9b.tar.gz
postgresql-8a1468af4e5c50dde8ce938886f4672459009b9b.zip
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.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/nodes/copyfuncs.c24
-rw-r--r--src/backend/nodes/equalfuncs.c19
-rw-r--r--src/backend/nodes/outfuncs.c24
-rw-r--r--src/backend/optimizer/path/allpaths.c150
-rw-r--r--src/backend/optimizer/path/joinpath.c161
-rw-r--r--src/backend/optimizer/plan/initsplan.c9
-rw-r--r--src/backend/optimizer/plan/planmain.c16
-rw-r--r--src/backend/optimizer/plan/planner.c143
-rw-r--r--src/backend/optimizer/prep/prepjointree.c22
-rw-r--r--src/backend/optimizer/prep/prepunion.c625
-rw-r--r--src/backend/optimizer/util/clauses.c21
-rw-r--r--src/backend/optimizer/util/plancat.c24
-rw-r--r--src/backend/optimizer/util/relnode.c116
-rw-r--r--src/backend/rewrite/rewriteManip.c39
14 files changed, 913 insertions, 480 deletions
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7a16cbcff56..19b987908bb 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
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
* ****************************************************************
@@ -2945,6 +2964,9 @@ copyObject(void *from)
case T_InClauseInfo:
retval = _copyInClauseInfo(from);
break;
+ case T_AppendRelInfo:
+ retval = _copyAppendRelInfo(from);
+ break;
/*
* VALUE NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b006cec150d..e7a9ced0ed3 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
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
@@ -1984,6 +1998,9 @@ equal(void *a, void *b)
case T_InClauseInfo:
retval = _equalInClauseInfo(a, b);
break;
+ case T_AppendRelInfo:
+ retval = _equalAppendRelInfo(a, b);
+ break;
case T_List:
case T_IntList:
case T_OidList:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b60eab31fd5..d6d63ee096f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* 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
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1178,6 +1178,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
WRITE_NODE_FIELD(full_join_clauses);
WRITE_NODE_FIELD(oj_info_list);
WRITE_NODE_FIELD(in_info_list);
+ WRITE_NODE_FIELD(append_rel_list);
WRITE_NODE_FIELD(query_pathkeys);
WRITE_NODE_FIELD(group_pathkeys);
WRITE_NODE_FIELD(sort_pathkeys);
@@ -1204,8 +1205,8 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
WRITE_NODE_FIELD(cheapest_unique_path);
WRITE_UINT_FIELD(relid);
WRITE_ENUM_FIELD(rtekind, RTEKind);
- WRITE_UINT_FIELD(min_attr);
- WRITE_UINT_FIELD(max_attr);
+ WRITE_INT_FIELD(min_attr);
+ WRITE_INT_FIELD(max_attr);
WRITE_NODE_FIELD(indexlist);
WRITE_UINT_FIELD(pages);
WRITE_FLOAT_FIELD(tuples, "%.0f");
@@ -1295,6 +1296,20 @@ _outInClauseInfo(StringInfo str, InClauseInfo *node)
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.
@@ -2048,6 +2063,9 @@ _outNode(StringInfo str, void *obj)
case T_InClauseInfo:
_outInClauseInfo(str, obj);
break;
+ case T_AppendRelInfo:
+ _outAppendRelInfo(str, obj);
+ break;
case T_CreateStmt:
_outCreateStmt(str, obj);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 19b1cfcaad4..90add90e4e9 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
*
*
* 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;
static void set_base_rel_pathlists(PlannerInfo *root);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
-static void set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
- Index rti, RangeTblEntry *rte,
- List *inheritlist);
+static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+ Index rti, RangeTblEntry *rte);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -96,9 +95,9 @@ make_one_rel(PlannerInfo *root, List *joinlist)
int num_base_rels = 0;
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)
continue;
@@ -131,16 +130,10 @@ set_base_rel_pathlists(PlannerInfo *root)
{
Index 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++)
+ for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
- RelOptInfo *rel = root->base_rel_array[rti];
+ RelOptInfo *rel = root->simple_rel_array[rti];
RangeTblEntry *rte;
- List *inheritlist;
/* there may be empty slots corresponding to non-baserel RTEs */
if (rel == NULL)
@@ -154,7 +147,12 @@ set_base_rel_pathlists(PlannerInfo *root)
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 */
set_subquery_pathlist(root, rel, rti, rte);
@@ -164,11 +162,6 @@ set_base_rel_pathlists(PlannerInfo *root)
/* RangeFunction --- generate a separate plan for it */
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
{
/* Plain relation */
@@ -188,6 +181,9 @@ set_base_rel_pathlists(PlannerInfo *root)
static void
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 */
set_baserel_size_estimates(root, rel);
@@ -224,37 +220,29 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
}
/*
- * 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 a duplicate of the parent itself. Note we will not come here
- * unless there's at least one child in addition to the parent.
+ * set_append_rel_pathlist
+ * Build access paths for an "append relation"
*
- * NOTE: the passed-in rel and RTE will henceforth represent the appended
- * result of the whole inheritance tree. The members of inheritlist represent
- * the individual tables --- in particular, the inheritlist member that is a
- * duplicate of the parent RTE represents the parent table alone.
- * We will generate plans to scan the individual tables that refer to
- * the inheritlist RTEs, whereas Vars elsewhere in the plan tree that
- * 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.
+ * The passed-in rel and RTE represent the entire append relation. The
+ * relation's contents are computed by appending together the output of
+ * the individual member relations. Note that in the inheritance case,
+ * the first member relation is actually the same table as is mentioned in
+ * the parent RTE ... but it has a different RTE and RelOptInfo. This is
+ * a good thing because their outputs are not the same size.
*/
static void
-set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
- Index rti, RangeTblEntry *rte,
- List *inheritlist)
+set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+ Index rti, RangeTblEntry *rte)
{
int parentRTindex = rti;
- Oid parentOID = rte->relid;
List *subpaths = NIL;
- ListCell *il;
+ ListCell *l;
/*
* 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))
ereport(ERROR,
@@ -262,64 +250,79 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
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->width = 0;
/*
- * Generate access paths for each table in the tree (parent AND children),
- * and pick the cheapest path for each table.
+ * Generate access paths for each member relation, and pick the cheapest
+ * path for each one.
*/
- foreach(il, inheritlist)
+ foreach(l, root->append_rel_list)
{
- int childRTindex = lfirst_int(il);
- RangeTblEntry *childrte;
- Oid childOID;
+ AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+ int childRTindex;
RelOptInfo *childrel;
+ RangeTblEntry *childrte;
ListCell *parentvars;
ListCell *childvars;
- childrte = rt_fetch(childRTindex, root->parse->rtable);
- childOID = childrte->relid;
+ /* append_rel_list contains all append rels; ignore others */
+ if (appinfo->parent_relid != parentRTindex)
+ continue;
+
+ childRTindex = appinfo->child_relid;
/*
* 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.
*/
- 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,
- * with attribute-number adjustment as needed. We don't bother to
- * 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.
+ * Copy the parent's targetlist and quals to the child, with
+ * appropriate substitution of variables.
*/
childrel->reltargetlist = (List *)
- adjust_inherited_attrs((Node *) rel->reltargetlist,
- parentRTindex,
- parentOID,
- childRTindex,
- childOID);
- childrel->attr_needed = NULL;
+ adjust_appendrel_attrs((Node *) rel->reltargetlist,
+ appinfo);
childrel->baserestrictinfo = (List *)
- adjust_inherited_attrs((Node *) rel->baserestrictinfo,
- parentRTindex,
- parentOID,
- childRTindex,
- childOID);
+ adjust_appendrel_attrs((Node *) rel->baserestrictinfo,
+ appinfo);
+ childrel->joininfo = (List *)
+ adjust_appendrel_attrs((Node *) rel->joininfo,
+ 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
* exclusion, just ignore it. (We have to have converted the
* 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;
- 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
@@ -363,7 +366,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Var *parentvar = (Var *) lfirst(parentvars);
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 cndx = childvar->varattno - childrel->min_attr;
@@ -392,9 +396,9 @@ has_multiple_baserels(PlannerInfo *root)
int num_base_rels = 0;
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)
continue;
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index c1c150de53d..137be08e753 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -8,7 +8,7 @@
*
*
* 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 @@
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
@@ -35,6 +36,9 @@ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
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,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
@@ -115,6 +119,13 @@ add_paths_to_joinrel(PlannerInfo *root,
if (enable_hashjoin)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
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);
}
/*
@@ -778,6 +789,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 that are usable for a particular join.
* Returns a list of RestrictInfo nodes for those clauses.
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 4b132d65611..442e52b52d7 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* 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);
*
* 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
- * is the only place that should call build_base_rel. But build_other_rel
- * will be used later to build rels for inheritance children.
+ * is the only place that should call build_simple_rel with reloptkind
+ * RELOPT_BASEREL. However, otherrels will be built later for append relation
+ * members.
*/
void
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;
- build_base_rel(root, varno);
+ (void) build_simple_rel(root, varno, RELOPT_BASEREL);
}
else if (IsA(jtnode, FromExpr))
{
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 3729fd2b199..0783f441b5b 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* 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,
&constant_quals);
/*
- * Init planner lists to empty. We create the base_rel_array with a size
- * that will be sufficient if no pullups or inheritance additions happen
- * ... otherwise it will be enlarged as needed.
+ * Init planner lists to empty, and set up the array to hold RelOptInfos
+ * for "simple" rels.
*
- * 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->base_rel_array = (RelOptInfo **)
- palloc0(root->base_rel_array_size * sizeof(RelOptInfo *));
+ root->simple_rel_array_size = list_length(parse->rtable) + 1;
+ root->simple_rel_array = (RelOptInfo **)
+ palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *));
root->join_rel_list = NIL;
root->join_rel_hash = NULL;
root->equi_key_list = NIL;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0dd9e1e8d2a..e007e4e594e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* 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 */
/* Expression kind codes for preprocess_expression */
-#define EXPRKIND_QUAL 0
-#define EXPRKIND_TARGET 1
-#define EXPRKIND_RTFUNC 2
-#define EXPRKIND_LIMIT 3
-#define EXPRKIND_ININFO 4
+#define EXPRKIND_QUAL 0
+#define EXPRKIND_TARGET 1
+#define EXPRKIND_RTFUNC 2
+#define EXPRKIND_LIMIT 3
+#define EXPRKIND_ININFO 4
+#define EXPRKIND_APPINFO 5
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
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 double preprocess_limit(PlannerInfo *root,
double tuple_fraction,
@@ -194,7 +195,6 @@ subquery_planner(Query *parse, double tuple_fraction,
PlannerInfo *root;
Plan *plan;
List *newHaving;
- List *lst;
ListCell *l;
/* Set up for a new level of subquery */
@@ -204,6 +204,8 @@ subquery_planner(Query *parse, double tuple_fraction,
/* Create a PlannerInfo data structure for this subquery */
root = makeNode(PlannerInfo);
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
@@ -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
* INs are processed just before pulling them up.
*/
- root->in_info_list = NIL;
if (parse->hasSubLinks)
parse->jointree->quals = pull_up_IN_clauses(root,
parse->jointree->quals);
@@ -253,6 +254,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
* because preprocess_expression will reduce a constant-true condition to
* an empty qual list ... but "HAVING TRUE" is not a semantic no-op.
@@ -279,6 +290,9 @@ subquery_planner(Query *parse, double tuple_fraction,
root->in_info_list = (List *)
preprocess_expression(root, (Node *) root->in_info_list,
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 */
foreach(l, parse->rtable)
@@ -357,8 +371,8 @@ subquery_planner(Query *parse, double tuple_fraction,
* needs special processing, else go straight to grouping_planner.
*/
if (parse->resultRelation &&
- (lst = expand_inherited_rtentry(root, parse->resultRelation)) != NIL)
- plan = inheritance_planner(root, lst);
+ rt_fetch(parse->resultRelation, parse->rtable)->inh)
+ plan = inheritance_planner(root);
else
plan = grouping_planner(root, tuple_fraction);
@@ -504,44 +518,50 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode)
(int) nodeTag(jtnode));
}
-/*--------------------
+/*
* 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.
- *
- * inheritlist is an integer list of RT indexes for the result relation 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. Also, for both UPDATE
+ * and DELETE, the executor needs the Append plan node at the top, else it
+ * can't keep track of which table is the current target table. 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.
*
* Returns a query plan.
- *--------------------
*/
static Plan *
-inheritance_planner(PlannerInfo *root, List *inheritlist)
+inheritance_planner(PlannerInfo *root)
{
Query *parse = root->parse;
int parentRTindex = parse->resultRelation;
- Oid parentOID = getrelid(parentRTindex, parse->rtable);
- int mainrtlength = list_length(parse->rtable);
List *subplans = NIL;
List *tlist = NIL;
+ PlannerInfo subroot;
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);
- Oid childOID = getrelid(childRTindex, parse->rtable);
- PlannerInfo subroot;
+ AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
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
* prepared to translate varnos in in_info_list as well as in the
@@ -549,14 +569,12 @@ inheritance_planner(PlannerInfo *root, List *inheritlist)
*/
memcpy(&subroot, root, sizeof(PlannerInfo));
subroot.parse = (Query *)
- adjust_inherited_attrs((Node *) parse,
- parentRTindex, parentOID,
- childRTindex, childOID);
+ adjust_appendrel_attrs((Node *) parse,
+ appinfo);
subroot.in_info_list = (List *)
- adjust_inherited_attrs((Node *) root->in_info_list,
- parentRTindex, parentOID,
- childRTindex, childOID);
- /* There shouldn't be any OJ info to translate, though */
+ adjust_appendrel_attrs((Node *) root->in_info_list,
+ appinfo);
+ /* There shouldn't be any OJ info to translate, as yet */
Assert(subroot.oj_info_list == NIL);
/* Generate plan */
@@ -564,48 +582,23 @@ inheritance_planner(PlannerInfo *root, List *inheritlist)
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 */
if (tlist == NIL)
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) */
root->query_pathkeys = NIL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index a25787685b1..546c4bd275d 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -15,7 +15,7 @@
*
*
* 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)
*/
subroot = makeNode(PlannerInfo);
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
* don't leave unoptimized INs behind.
*/
- subroot->in_info_list = NIL;
if (subquery->hasSubLinks)
subquery->jointree->quals = pull_up_IN_clauses(subroot,
subquery->jointree->quals);
@@ -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
* 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);
OffsetVarNodes((Node *) subquery, 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
@@ -240,6 +242,7 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
*/
IncrementVarSublevelsUp((Node *) subquery, -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
@@ -263,6 +266,10 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
ResolveNew((Node *) root->in_info_list,
varno, 0, rte,
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)
{
@@ -328,6 +335,15 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join)
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
* info, because that hasn't been set up yet.
*/
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 227eab83cef..a33213ef59d 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -5,8 +5,12 @@
* 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.
+ * inheritance (SELECT FROM foo*). Although inheritance is radically
+ * different from set operations as far as the parser representation of
+ * a query is concerned, we try to handle it identically to the UNION ALL
+ * case during planning: both are converted to "append rels". (Note that
+ * UNION ALL is special-cased: other kinds of set operations go through
+ * a completely different code path.)
*
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
@@ -14,7 +18,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.128 2005/11/22 18:17:14 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.129 2006/01/31 21:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,18 +42,6 @@
#include "utils/lsyscache.h"
-typedef struct
-{
- Index old_rt_index;
- Index new_rt_index;
- Oid old_rel_type;
- Oid new_rel_type;
- TupleDesc old_tupdesc;
- TupleDesc new_tupdesc;
- char *old_rel_name;
- char *new_rel_name;
-} adjust_inherited_attrs_context;
-
static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
double tuple_fraction,
List *colTypes, bool junkOK,
@@ -73,11 +65,16 @@ static List *generate_append_tlist(List *colTypes, bool flag,
List *input_plans,
List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
-static Node *adjust_inherited_attrs_mutator(Node *node,
- adjust_inherited_attrs_context *context);
+static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
+ Index rti);
+static void make_translation_lists(Relation oldrelation, Relation newrelation,
+ Index newvarno,
+ List **col_mappings, List **translated_vars);
+static Node *adjust_appendrel_attrs_mutator(Node *node,
+ AppendRelInfo *context);
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
static List *adjust_inherited_tlist(List *tlist,
- adjust_inherited_attrs_context *context);
+ AppendRelInfo *context);
/*
@@ -740,50 +737,80 @@ find_all_inheritors(Oid parentrel)
}
/*
+ * expand_inherited_tables
+ * Expand each rangetable entry that represents an inheritance set
+ * into an "append relation". At the conclusion of this process,
+ * the "inh" flag is set in all and only those RTEs that are append
+ * relation parents.
+ */
+void
+expand_inherited_tables(PlannerInfo *root)
+{
+ Index nrtes;
+ Index rti;
+ ListCell *rl;
+
+ /*
+ * expand_inherited_rtentry may add RTEs to parse->rtable; there is
+ * no need to scan them since they can't have inh=true. So just
+ * scan as far as the original end of the rtable list.
+ */
+ nrtes = list_length(root->parse->rtable);
+ rl = list_head(root->parse->rtable);
+ for (rti = 1; rti <= nrtes; rti++)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
+
+ expand_inherited_rtentry(root, rte, rti);
+ rl = lnext(rl);
+ }
+}
+
+/*
* expand_inherited_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.
+ * rangetable, and build AppendRelInfo nodes for all the child tables
+ * and add them to root->append_rel_list. If not, clear the entry's
+ * "inh" flag to prevent later code from looking for AppendRelInfos.
*
* Note that the original RTE is considered to represent the whole
- * inheritance set. The first member of the returned list is an RTE
- * for the same table, but with inh = false, to represent the parent table
- * in its role as a simple member of the set. The original RT index is
- * never a member of the returned list.
+ * inheritance set. The first of the generated RTEs is an RTE for the same
+ * table, but with inh = false, to represent the parent table in its role
+ * as a simple member of the inheritance set.
*
* A childless table is never considered to be an inheritance set; therefore
- * the result will never be a one-element list. It'll be either empty
- * or have two or more elements.
- *
- * Note: there are cases in which this routine will be invoked multiple
- * times on the same RTE. We will generate a separate set of child RTEs
- * for each invocation. This is somewhat wasteful but seems not worth
- * trying to avoid.
+ * a parent RTE must always have at least two associated AppendRelInfos.
*/
-List *
-expand_inherited_rtentry(PlannerInfo *root, Index rti)
+static void
+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
{
Query *parse = root->parse;
- RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
Oid parentOID;
+ Relation oldrelation;
+ LOCKMODE lockmode;
List *inhOIDs;
- List *inhRTIs;
+ List *appinfos;
ListCell *l;
/* Does RT entry allow inheritance? */
if (!rte->inh)
- return NIL;
- Assert(rte->rtekind == RTE_RELATION);
+ return;
+ /* Ignore any already-expanded UNION ALL nodes */
+ if (rte->rtekind != RTE_RELATION)
+ {
+ Assert(rte->rtekind == RTE_SUBQUERY);
+ return;
+ }
/* Fast path for common case of childless table */
parentOID = rte->relid;
if (!has_subclass(parentOID))
{
- /* Clear flag to save repeated tests if called again */
+ /* Clear flag before returning */
rte->inh = false;
- return NIL;
+ return;
}
+
/* Scan for all members of inheritance set */
inhOIDs = find_all_inheritors(parentOID);
@@ -794,18 +821,45 @@ expand_inherited_rtentry(PlannerInfo *root, Index rti)
*/
if (list_length(inhOIDs) < 2)
{
- /* Clear flag to save repeated tests if called again */
+ /* Clear flag before returning */
rte->inh = false;
- return NIL;
+ return;
}
- /* OK, it's an inheritance set; expand it */
- inhRTIs = NIL;
+ /*
+ * Must open the parent relation to examine its tupdesc. We need not
+ * lock it since the rewriter already obtained at least AccessShareLock
+ * on each relation used in the query.
+ */
+ oldrelation = heap_open(parentOID, NoLock);
+
+ /*
+ * However, for each child relation we add to the query, we must obtain
+ * an appropriate lock, because this will be the first use of those
+ * relations in the parse/rewrite/plan pipeline.
+ *
+ * If the parent relation is the query's result relation, then we need
+ * RowExclusiveLock. Otherwise, check to see if the relation is accessed
+ * FOR UPDATE/SHARE or not. We can't just grab AccessShareLock because
+ * then the executor would be trying to upgrade the lock, leading to
+ * possible deadlocks. (This code should match the parser and rewriter.)
+ */
+ if (rti == parse->resultRelation)
+ lockmode = RowExclusiveLock;
+ else if (list_member_int(parse->rowMarks, rti))
+ lockmode = RowShareLock;
+ else
+ lockmode = AccessShareLock;
+
+ /* Scan the inheritance set and expand it */
+ appinfos = NIL;
foreach(l, inhOIDs)
{
Oid childOID = lfirst_oid(l);
+ Relation newrelation;
RangeTblEntry *childrte;
Index childRTindex;
+ AppendRelInfo *appinfo;
/*
* It is possible that the parent table has children that are temp
@@ -817,6 +871,12 @@ expand_inherited_rtentry(PlannerInfo *root, Index rti)
isOtherTempNamespace(get_rel_namespace(childOID)))
continue;
+ /* Open rel, acquire the appropriate lock type */
+ if (childOID != parentOID)
+ newrelation = heap_open(childOID, lockmode);
+ else
+ newrelation = oldrelation;
+
/*
* 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 OID,
@@ -827,75 +887,160 @@ expand_inherited_rtentry(PlannerInfo *root, Index rti)
childrte->inh = false;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = list_length(parse->rtable);
- inhRTIs = lappend_int(inhRTIs, childRTindex);
+
+ /*
+ * Build an AppendRelInfo for this parent and child.
+ */
+ appinfo = makeNode(AppendRelInfo);
+ appinfo->parent_relid = rti;
+ appinfo->child_relid = childRTindex;
+ appinfo->parent_reltype = oldrelation->rd_rel->reltype;
+ appinfo->child_reltype = newrelation->rd_rel->reltype;
+ make_translation_lists(oldrelation, newrelation, childRTindex,
+ &appinfo->col_mappings,
+ &appinfo->translated_vars);
+ appinfo->parent_reloid = parentOID;
+ appinfos = lappend(appinfos, appinfo);
+
+ /* Close child relations, but keep locks */
+ if (childOID != parentOID)
+ heap_close(newrelation, NoLock);
}
+ heap_close(oldrelation, NoLock);
+
/*
* If all the children were temp tables, pretend it's a non-inheritance
* situation. The duplicate RTE we added for the parent table is
- * harmless.
+ * harmless, so we don't bother to get rid of it.
*/
- if (list_length(inhRTIs) < 2)
+ if (list_length(appinfos) < 2)
{
- /* Clear flag to save repeated tests if called again */
+ /* Clear flag before returning */
rte->inh = false;
- return NIL;
+ return;
}
+ /* Otherwise, OK to add to root->append_rel_list */
+ root->append_rel_list = list_concat(root->append_rel_list, appinfos);
+
/*
* The executor will check the parent table's access permissions when it
- * examines the parent's inheritlist entry. There's no need to check
- * twice, so turn off access check bits in the original RTE. (If we are
- * invoked more than once, extra copies of the child RTEs will also not
- * cause duplicate permission checks.)
+ * examines the parent's added RTE entry. There's no need to check
+ * twice, so turn off access check bits in the original RTE.
*/
rte->requiredPerms = 0;
+}
+
+/*
+ * make_translation_lists
+ * Build the lists of translations from parent Vars to child Vars for
+ * an inheritance child. We need both a column number mapping list
+ * and a list of Vars representing the child columns.
+ *
+ * For paranoia's sake, we match type as well as attribute name.
+ */
+static void
+make_translation_lists(Relation oldrelation, Relation newrelation,
+ Index newvarno,
+ List **col_mappings, List **translated_vars)
+{
+ List *numbers = NIL;
+ List *vars = NIL;
+ TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
+ TupleDesc new_tupdesc = RelationGetDescr(newrelation);
+ int oldnatts = old_tupdesc->natts;
+ int newnatts = new_tupdesc->natts;
+ int old_attno;
+
+ for (old_attno = 0; old_attno < oldnatts; old_attno++)
+ {
+ Form_pg_attribute att;
+ char *attname;
+ Oid atttypid;
+ int32 atttypmod;
+ int new_attno;
+
+ att = old_tupdesc->attrs[old_attno];
+ if (att->attisdropped)
+ {
+ /* Just put 0/NULL into this list entry */
+ numbers = lappend_int(numbers, 0);
+ vars = lappend(vars, NULL);
+ continue;
+ }
+ attname = NameStr(att->attname);
+ atttypid = att->atttypid;
+ atttypmod = att->atttypmod;
- return inhRTIs;
+ /*
+ * When we are generating the "translation list" for the parent
+ * table of an inheritance set, no need to search for matches.
+ */
+ if (oldrelation == newrelation)
+ {
+ numbers = lappend_int(numbers, old_attno + 1);
+ vars = lappend(vars, makeVar(newvarno,
+ (AttrNumber) (old_attno + 1),
+ atttypid,
+ atttypmod,
+ 0));
+ continue;
+ }
+
+ /*
+ * Otherwise we have to search for the matching column by name.
+ * There's no guarantee it'll have the same column position,
+ * because of cases like ALTER TABLE ADD COLUMN and multiple
+ * inheritance.
+ */
+ for (new_attno = 0; new_attno < newnatts; new_attno++)
+ {
+ att = new_tupdesc->attrs[new_attno];
+ if (att->attisdropped || att->attinhcount == 0)
+ continue;
+ if (strcmp(attname, NameStr(att->attname)) != 0)
+ continue;
+ /* Found it, check type */
+ if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+ elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
+ attname, RelationGetRelationName(newrelation));
+
+ numbers = lappend_int(numbers, new_attno + 1);
+ vars = lappend(vars, makeVar(newvarno,
+ (AttrNumber) (new_attno + 1),
+ atttypid,
+ atttypmod,
+ 0));
+ break;
+ }
+
+ if (new_attno >= newnatts)
+ elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
+ attname, RelationGetRelationName(newrelation));
+ }
+
+ *col_mappings = numbers;
+ *translated_vars = vars;
}
/*
- * adjust_inherited_attrs
+ * adjust_appendrel_attrs
* Copy the specified query or expression and translate Vars referring
- * to old_rt_index to refer to new_rt_index.
+ * to the parent rel of the specified AppendRelInfo to refer to the
+ * child rel instead. We also update rtindexes appearing outside Vars,
+ * such as resultRelation and jointree relids.
*
- * 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
- * is necessary for ALTER TABLE ADD COLUMN.
+ * Note: this is only applied after conversion of sublinks to subplans,
+ * so we don't need to cope with recursion into sub-queries.
+ *
+ * Note: this is not hugely different from what ResolveNew() does; maybe
+ * we should try to fold the two routines together.
*/
Node *
-adjust_inherited_attrs(Node *node,
- Index old_rt_index, Oid old_relid,
- Index new_rt_index, Oid new_relid)
+adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo)
{
Node *result;
- adjust_inherited_attrs_context context;
- Relation oldrelation;
- Relation newrelation;
-
- /* Handle simple case simply... */
- if (old_rt_index == new_rt_index)
- {
- Assert(old_relid == new_relid);
- return copyObject(node);
- }
-
- /*
- * We assume that by now the planner has acquired at least AccessShareLock
- * on both rels, and so we need no additional lock now.
- */
- oldrelation = heap_open(old_relid, NoLock);
- newrelation = heap_open(new_relid, NoLock);
-
- context.old_rt_index = old_rt_index;
- context.new_rt_index = new_rt_index;
- context.old_rel_type = oldrelation->rd_rel->reltype;
- context.new_rel_type = newrelation->rd_rel->reltype;
- context.old_tupdesc = RelationGetDescr(oldrelation);
- context.new_tupdesc = RelationGetDescr(newrelation);
- context.old_rel_name = RelationGetRelationName(oldrelation);
- context.new_rel_name = RelationGetRelationName(newrelation);
/*
* Must be prepared to start with a Query or a bare expression tree.
@@ -905,80 +1050,28 @@ adjust_inherited_attrs(Node *node,
Query *newnode;
newnode = query_tree_mutator((Query *) node,
- adjust_inherited_attrs_mutator,
- (void *) &context,
+ adjust_appendrel_attrs_mutator,
+ (void *) appinfo,
QTW_IGNORE_RT_SUBQUERIES);
- if (newnode->resultRelation == old_rt_index)
+ if (newnode->resultRelation == appinfo->parent_relid)
{
- newnode->resultRelation = new_rt_index;
+ newnode->resultRelation = appinfo->child_relid;
/* Fix tlist resnos too, if it's inherited UPDATE */
if (newnode->commandType == CMD_UPDATE)
newnode->targetList =
adjust_inherited_tlist(newnode->targetList,
- &context);
+ appinfo);
}
result = (Node *) newnode;
}
else
- result = adjust_inherited_attrs_mutator(node, &context);
-
- heap_close(oldrelation, NoLock);
- heap_close(newrelation, NoLock);
+ result = adjust_appendrel_attrs_mutator(node, appinfo);
return result;
}
-/*
- * Translate parent's attribute number into child's.
- *
- * For paranoia's sake, we match type as well as attribute name.
- */
-static AttrNumber
-translate_inherited_attnum(AttrNumber old_attno,
- adjust_inherited_attrs_context *context)
-{
- Form_pg_attribute att;
- char *attname;
- Oid atttypid;
- int32 atttypmod;
- int newnatts;
- int i;
-
- if (old_attno <= 0 || old_attno > context->old_tupdesc->natts)
- elog(ERROR, "attribute %d of relation \"%s\" does not exist",
- (int) old_attno, context->old_rel_name);
- att = context->old_tupdesc->attrs[old_attno - 1];
- if (att->attisdropped)
- elog(ERROR, "attribute %d of relation \"%s\" does not exist",
- (int) old_attno, context->old_rel_name);
- attname = NameStr(att->attname);
- atttypid = att->atttypid;
- atttypmod = att->atttypmod;
-
- newnatts = context->new_tupdesc->natts;
- for (i = 0; i < newnatts; i++)
- {
- att = context->new_tupdesc->attrs[i];
- if (att->attisdropped)
- continue;
- if (strcmp(attname, NameStr(att->attname)) == 0)
- {
- /* Found it, check type */
- if (atttypid != att->atttypid || atttypmod != att->atttypmod)
- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
- attname, context->new_rel_name);
- return (AttrNumber) (i + 1);
- }
- }
-
- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not exist",
- attname, context->new_rel_name);
- return 0; /* keep compiler quiet */
-}
-
static Node *
-adjust_inherited_attrs_mutator(Node *node,
- adjust_inherited_attrs_context *context)
+adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
{
if (node == NULL)
return NULL;
@@ -987,36 +1080,54 @@ adjust_inherited_attrs_mutator(Node *node,
Var *var = (Var *) copyObject(node);
if (var->varlevelsup == 0 &&
- var->varno == context->old_rt_index)
+ var->varno == context->parent_relid)
{
- var->varno = context->new_rt_index;
- var->varnoold = context->new_rt_index;
+ var->varno = context->child_relid;
+ var->varnoold = context->child_relid;
if (var->varattno > 0)
{
- var->varattno = translate_inherited_attnum(var->varattno,
- context);
- var->varoattno = var->varattno;
+ Node *newnode;
+
+ if (var->varattno > list_length(context->translated_vars))
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ var->varattno, get_rel_name(context->parent_reloid));
+ newnode = copyObject(list_nth(context->translated_vars,
+ var->varattno - 1));
+ if (newnode == NULL)
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ var->varattno, get_rel_name(context->parent_reloid));
+ return newnode;
}
else if (var->varattno == 0)
{
/*
- * Whole-row Var: we need to insert a coercion step to convert
- * the tuple layout to the parent's rowtype.
+ * Whole-row Var: if we are dealing with named rowtypes,
+ * we can use a whole-row Var for the child table plus a
+ * coercion step to convert the tuple layout to the parent's
+ * rowtype. Otherwise we have to generate a RowExpr.
*/
- if (context->old_rel_type != context->new_rel_type)
+ if (OidIsValid(context->child_reltype))
{
- ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
-
- r->arg = (Expr *) var;
- r->resulttype = context->old_rel_type;
- r->convertformat = COERCE_IMPLICIT_CAST;
- /* Make sure the Var node has the right type ID, too */
- Assert(var->vartype == context->old_rel_type);
- var->vartype = context->new_rel_type;
- return (Node *) r;
+ Assert(var->vartype == context->parent_reltype);
+ if (context->parent_reltype != context->child_reltype)
+ {
+ ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
+
+ r->arg = (Expr *) var;
+ r->resulttype = context->parent_reltype;
+ r->convertformat = COERCE_IMPLICIT_CAST;
+ /* Make sure the Var node has the right type ID, too */
+ var->vartype = context->child_reltype;
+ return (Node *) r;
+ }
+ }
+ else
+ {
+ /* XXX copy some code from ResolveNew */
+ Assert(false);/* not done yet */
}
}
- /* system attributes don't need any translation */
+ /* system attributes don't need any other translation */
}
return (Node *) var;
}
@@ -1024,8 +1135,8 @@ adjust_inherited_attrs_mutator(Node *node,
{
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
- if (rtr->rtindex == context->old_rt_index)
- rtr->rtindex = context->new_rt_index;
+ if (rtr->rtindex == context->parent_relid)
+ rtr->rtindex = context->child_relid;
return (Node *) rtr;
}
if (IsA(node, JoinExpr))
@@ -1034,11 +1145,11 @@ adjust_inherited_attrs_mutator(Node *node,
JoinExpr *j;
j = (JoinExpr *) expression_tree_mutator(node,
- adjust_inherited_attrs_mutator,
+ adjust_appendrel_attrs_mutator,
(void *) context);
- /* now fix JoinExpr's rtindex */
- if (j->rtindex == context->old_rt_index)
- j->rtindex = context->new_rt_index;
+ /* now fix JoinExpr's rtindex (probably never happens) */
+ if (j->rtindex == context->parent_relid)
+ j->rtindex = context->child_relid;
return (Node *) j;
}
if (IsA(node, InClauseInfo))
@@ -1047,17 +1158,20 @@ adjust_inherited_attrs_mutator(Node *node,
InClauseInfo *ininfo;
ininfo = (InClauseInfo *) expression_tree_mutator(node,
- adjust_inherited_attrs_mutator,
+ adjust_appendrel_attrs_mutator,
(void *) context);
/* now fix InClauseInfo's relid sets */
ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
ininfo->righthand = adjust_relid_set(ininfo->righthand,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
return (Node *) ininfo;
}
+ /* Shouldn't need to handle OuterJoinInfo or AppendRelInfo here */
+ Assert(!IsA(node, OuterJoinInfo));
+ Assert(!IsA(node, AppendRelInfo));
/*
* We have to process RestrictInfo nodes specially.
@@ -1072,25 +1186,25 @@ adjust_inherited_attrs_mutator(Node *node,
/* Recursively fix the clause itself */
newinfo->clause = (Expr *)
- adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
+ adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
/* and the modified version, if an OR clause */
newinfo->orclause = (Expr *)
- adjust_inherited_attrs_mutator((Node *) oldinfo->orclause, context);
+ adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
/* adjust relid sets too */
newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
newinfo->required_relids = adjust_relid_set(oldinfo->required_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
/*
* Reset cached derivative fields, since these might need to have
@@ -1119,7 +1233,7 @@ adjust_inherited_attrs_mutator(Node *node,
* BUT: although we don't need to recurse into subplans, we do need to
* make sure that they are copied, not just referenced as
* expression_tree_mutator will do by default. Otherwise we'll have the
- * same subplan node referenced from each arm of the inheritance APPEND
+ * same subplan node referenced from each arm of the finished APPEND
* plan, which will cause trouble in the executor. This is a kluge that
* should go away when we redesign querytrees.
*/
@@ -1128,7 +1242,7 @@ adjust_inherited_attrs_mutator(Node *node,
SubPlan *subplan;
/* Copy the node and process subplan args */
- node = expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
(void *) context);
/* Make sure we have separate copies of subplan and its rtable */
subplan = (SubPlan *) node;
@@ -1137,7 +1251,7 @@ adjust_inherited_attrs_mutator(Node *node,
return node;
}
- return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
(void *) context);
}
@@ -1159,6 +1273,110 @@ adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
}
/*
+ * adjust_appendrel_attr_needed
+ * Adjust an attr_needed[] array to reference a member rel instead of
+ * the original appendrel
+ *
+ * oldrel: source of data (we use the attr_needed, min_attr, max_attr fields)
+ * appinfo: supplies parent_relid, child_relid, col_mappings
+ * new_min_attr, new_max_attr: desired bounds of new attr_needed array
+ *
+ * The relid sets are adjusted by substituting child_relid for parent_relid.
+ * (NOTE: oldrel is not necessarily the parent_relid relation!) We are also
+ * careful to map attribute numbers within the array properly. User
+ * attributes have to be mapped through col_mappings, but system attributes
+ * and whole-row references always have the same attno.
+ *
+ * Returns a palloc'd array with the specified bounds
+ */
+Relids *
+adjust_appendrel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
+ AttrNumber new_min_attr, AttrNumber new_max_attr)
+{
+ Relids *new_attr_needed;
+ Index parent_relid = appinfo->parent_relid;
+ Index child_relid = appinfo->child_relid;
+ int parent_attr;
+ ListCell *lm;
+
+ /* Create empty result array */
+ Assert(new_min_attr <= oldrel->min_attr);
+ Assert(new_max_attr >= oldrel->max_attr);
+ new_attr_needed = (Relids *)
+ palloc0((new_max_attr - new_min_attr + 1) * sizeof(Relids));
+ /* Process user attributes, with appropriate attno mapping */
+ parent_attr = 1;
+ foreach(lm, appinfo->col_mappings)
+ {
+ int child_attr = lfirst_int(lm);
+
+ if (child_attr > 0)
+ {
+ Relids attrneeded;
+
+ Assert(parent_attr <= oldrel->max_attr);
+ Assert(child_attr <= new_max_attr);
+ attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
+ attrneeded = adjust_relid_set(attrneeded,
+ parent_relid, child_relid);
+ new_attr_needed[child_attr - new_min_attr] = attrneeded;
+ }
+ parent_attr++;
+ }
+ /* Process system attributes, including whole-row references */
+ for (parent_attr = oldrel->min_attr; parent_attr <= 0; parent_attr++)
+ {
+ Relids attrneeded;
+
+ attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
+ attrneeded = adjust_relid_set(attrneeded,
+ parent_relid, child_relid);
+ new_attr_needed[parent_attr - new_min_attr] = attrneeded;
+ }
+
+ return new_attr_needed;
+}
+
+/*
+ * adjust_other_rel_attr_needed
+ * Adjust an attr_needed[] array to reference a member rel instead of
+ * the original appendrel
+ *
+ * This is exactly like adjust_appendrel_attr_needed except that we disregard
+ * appinfo->col_mappings and instead assume that the mapping of user
+ * attributes is one-to-one. This is appropriate for generating an attr_needed
+ * array that describes another relation to be joined with a member rel.
+ */
+Relids *
+adjust_other_rel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
+ AttrNumber new_min_attr, AttrNumber new_max_attr)
+{
+ Relids *new_attr_needed;
+ Index parent_relid = appinfo->parent_relid;
+ Index child_relid = appinfo->child_relid;
+ int parent_attr;
+
+ /* Create empty result array */
+ Assert(new_min_attr <= oldrel->min_attr);
+ Assert(new_max_attr >= oldrel->max_attr);
+ new_attr_needed = (Relids *)
+ palloc0((new_max_attr - new_min_attr + 1) * sizeof(Relids));
+ /* Process user attributes and system attributes */
+ for (parent_attr = oldrel->min_attr; parent_attr <= oldrel->max_attr;
+ parent_attr++)
+ {
+ Relids attrneeded;
+
+ attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
+ attrneeded = adjust_relid_set(attrneeded,
+ parent_relid, child_relid);
+ new_attr_needed[parent_attr - new_min_attr] = attrneeded;
+ }
+
+ return new_attr_needed;
+}
+
+/*
* Adjust the targetlist entries of an inherited UPDATE operation
*
* The expressions have already been fixed, but we have to make sure that
@@ -1175,8 +1393,7 @@ adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
* Note that this is not needed for INSERT because INSERT isn't inheritable.
*/
static List *
-adjust_inherited_tlist(List *tlist,
- adjust_inherited_attrs_context *context)
+adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
{
bool changed_it = false;
ListCell *tl;
@@ -1184,19 +1401,31 @@ adjust_inherited_tlist(List *tlist,
bool more;
int attrno;
- /* Scan tlist and update resnos to match attnums of new_relid */
+ /* This should only happen for an inheritance case, not UNION ALL */
+ Assert(OidIsValid(context->parent_reloid));
+
+ /* Scan tlist and update resnos to match attnums of child rel */
foreach(tl, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ int newattno;
if (tle->resjunk)
continue; /* ignore junk items */
- attrno = translate_inherited_attnum(tle->resno, context);
-
- if (tle->resno != attrno)
+ /* Look up the translation of this column */
+ if (tle->resno <= 0 ||
+ tle->resno > list_length(context->col_mappings))
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ tle->resno, get_rel_name(context->parent_reloid));
+ newattno = list_nth_int(context->col_mappings, tle->resno - 1);
+ if (newattno <= 0)
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ tle->resno, get_rel_name(context->parent_reloid));
+
+ if (tle->resno != newattno)
{
- tle->resno = attrno;
+ tle->resno = newattno;
changed_it = true;
}
}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 5266ff85d82..c4639cbf0d1 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* 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
* AUTHOR DATE MAJOR EVENT
@@ -3209,6 +3209,15 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_AppendRelInfo:
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+ if (expression_tree_walker((Node *) appinfo->translated_vars,
+ walker, context))
+ return true;
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
@@ -3744,6 +3753,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
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:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d9a9fdb9cfa..c926ae5d488 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
*
*
* 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)
List *indexinfos = NIL;
/*
- * Normally, we can assume the rewriter already acquired at least
- * AccessShareLock on each relation used in the query. However this will
- * not be the case for relations added to the query because they are
- * 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.
+ * We need not lock the relation since it was already locked, either
+ * by the rewriter or when expand_inherited_rtentry() added it to the
+ * query's rangetable.
*/
- if (rel->reloptkind == RELOPT_BASEREL)
- relation = heap_open(relationObjectId, NoLock);
- else
- relation = heap_open(relationObjectId, AccessShareLock);
+ relation = heap_open(relationObjectId, NoLock);
rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
rel->max_attr = RelationGetNumberOfAttributes(relation);
@@ -224,7 +211,6 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
rel->indexlist = indexinfos;
- /* close rel, but keep lock if any */
heap_close(relation, NoLock);
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index cef0c63a66f..cedb8082271 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* 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
RelOptInfo *join_rel;
} JoinHashEntry;
-static RelOptInfo *make_reloptinfo(PlannerInfo *root, int relid,
- RelOptKind reloptkind);
static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *input_rel);
static List *build_joinrel_restrictlist(PlannerInfo *root,
@@ -49,71 +47,25 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/*
- * build_base_rel
- * Construct a new base relation RelOptInfo, and put it in the query's
- * 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.
+ * build_simple_rel
+ * Construct a new RelOptInfo for a base relation or 'other' relation.
*/
RelOptInfo *
-build_other_rel(PlannerInfo *root, int relid)
+build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
{
RelOptInfo *rel;
+ RangeTblEntry *rte;
- Assert(relid > 0);
-
- /* Already made? */
- 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;
- }
- }
+ /* Fetch RTE for relation */
+ Assert(relid > 0 && relid <= list_length(root->parse->rtable));
+ rte = rt_fetch(relid, root->parse->rtable);
- /* No existing RelOptInfo for this other rel, so make a new one */
- /* presently, must be an inheritance child rel */
- rel = make_reloptinfo(root, relid, RELOPT_OTHER_CHILD_REL);
-
- 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 should not exist already */
+ Assert(relid < root->simple_rel_array_size);
+ if (root->simple_rel_array[relid] != NULL)
+ elog(ERROR, "rel %d already exists", relid);
+ rel = makeNode(RelOptInfo);
rel->reloptkind = reloptkind;
rel->relids = bms_make_singleton(relid);
rel->rows = 0;
@@ -161,21 +113,8 @@ make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind)
break;
}
- /* Add the finished struct to the base_rel_array */
- if (relid >= root->base_rel_array_size)
- {
- 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;
+ /* Save the finished struct in the query's simple_rel_array */
+ root->simple_rel_array[relid] = rel;
return rel;
}
@@ -191,9 +130,9 @@ find_base_rel(PlannerInfo *root, int relid)
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)
return rel;
}
@@ -446,12 +385,24 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
foreach(vars, input_rel->reltargetlist)
{
- Var *var = (Var *) lfirst(vars);
+ Var *origvar = (Var *) lfirst(vars);
+ Var *var;
RelOptInfo *baserel;
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 */
baserel = find_base_rel(root, var->varno);
@@ -461,8 +412,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
{
/* Yup, add it to the output */
- joinrel->reltargetlist = lappend(joinrel->reltargetlist, var);
- Assert(baserel->attr_widths[ndx] > 0);
+ joinrel->reltargetlist = lappend(joinrel->reltargetlist, origvar);
joinrel->width += baserel->attr_widths[ndx];
}
}
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 12de72619a0..2db3c27cddf 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
*
*
* 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)
}
/* 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))
{
/* Recurse into subselects */
@@ -323,6 +334,19 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
}
/* 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))
{
/* Recurse into subselects */
@@ -527,16 +551,11 @@ rangeTableEntry_used_walker(Node *node,
return true;
/* fall through to examine children */
}
- if (IsA(node, InClauseInfo))
- {
- InClauseInfo *ininfo = (InClauseInfo *) node;
+ /* Shouldn't need to handle planner auxiliary nodes here */
+ Assert(!IsA(node, OuterJoinInfo));
+ 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))
{
/* Recurse into subselects */