OSDN Git Service

Restructure planner's handling of inheritance. Rather than processing
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 31 Jan 2006 21:39:25 +0000 (21:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 31 Jan 2006 21:39:25 +0000 (21:39 +0000)
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.

18 files changed:
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/relnode.c
src/backend/rewrite/rewriteManip.c
src/include/nodes/nodes.h
src/include/nodes/relation.h
src/include/optimizer/pathnode.h
src/include/optimizer/prep.h

index 7a16cbc..19b9879 100644 (file)
@@ -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
index b006cec..e7a9ced 100644 (file)
@@ -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:
index b60eab3..d6d63ee 100644 (file)
@@ -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);
index 19b1cfc..90add90 100644 (file)
@@ -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;
index c1c150d..137be08 100644 (file)
@@ -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.
index 4b132d6..442e52b 100644 (file)
@@ -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))
        {
index 3729fd2..0783f44 100644 (file)
@@ -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;
index 0dd9e1e..e007e4e 100644 (file)
@@ -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;
index a257876..546c4bd 100644 (file)
@@ -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.
                         */
index 227eab8..a33213e 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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;
                }
        }
index 5266ff8..c4639cb 100644 (file)
@@ -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));
index d9a9fdb..c926ae5 100644 (file)
@@ -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);
 }
 
index cef0c63..cedb808 100644 (file)
@@ -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];
                }
        }
index 12de726..2db3c27 100644 (file)
@@ -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 */
index 9b33a5d..98d0ed0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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
        T_InnerIndexscanInfo,
        T_OuterJoinInfo,
        T_InClauseInfo,
+       T_AppendRelInfo,
 
        /*
         * TAGS FOR MEMORY NODES (memnodes.h)
index 1d490fc..67460c0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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
  *
  * 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
- * 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.
  *----------
  */
@@ -63,14 +63,14 @@ typedef struct PlannerInfo
        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
         * 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
-        * enlarged on-the-fly.
+        * does not correspond to a base relation, such as a join RTE or an
+        * unreferenced view RTE; or if the RelOptInfo hasn't been made yet.
         */
-       struct RelOptInfo **base_rel_array; /* All one-relation RelOptInfos */
-       int                     base_rel_array_size;    /* current allocated array len */
+       struct RelOptInfo **simple_rel_array;   /* All 1-relation RelOptInfos */
+       int                     simple_rel_array_size;          /* allocated size of array */
 
        /*
         * join_rel_list is a list of all join-relation RelOptInfos we have
@@ -101,6 +101,8 @@ typedef struct PlannerInfo
 
        List       *in_info_list;       /* list of InClauseInfos */
 
+       List       *append_rel_list;            /* list of AppendRelInfos */
+
        List       *query_pathkeys; /* desired pathkeys for query_planner(), and
                                                                 * actual pathkeys afterwards */
 
@@ -125,7 +127,7 @@ typedef struct PlannerInfo
  * 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
  * 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
  * baserels, no matter what order we assemble them in; so an unordered
@@ -135,16 +137,15 @@ typedef struct PlannerInfo
  * single RT indexes; but they are not part of the join tree, and are given
  * a different RelOptKind to identify them.
  *
- * Currently the only kind of otherrels are those made for child relations
- * of an inheritance scan (SELECT FROM foo*).  The parent table's RTE and
- * corresponding baserel represent the whole result of the inheritance scan.
- * The planner creates separate RTEs and associated RelOptInfos for each child
- * table (including the parent table, in its capacity as a member of the
- * inheritance set).  These RelOptInfos are physically identical to baserels,
- * but are otherrels because they are not in the main join tree.  These added
- * RTEs and otherrels are used to plan the scans of the individual tables in
- * the inheritance set; then the parent baserel is given an Append plan
- * comprising the best plans for the individual child tables.
+ * Currently the only kind of otherrels are those made for member relations
+ * of an "append relation", that is an inheritance set or UNION ALL subquery.
+ * An append relation has a parent RTE that is a base rel, which represents
+ * the entire append relation.  The member RTEs are otherrels.  The parent
+ * is present in the query join tree but the members are not.  The member
+ * RTEs and otherrels are used to plan the scans of the individual tables or
+ * subqueries of the append set; then the parent baserel is given an Append
+ * plan comprising the best plans for the individual member rels.  (See
+ * comments for AppendRelInfo for more information.)
  *
  * 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
@@ -192,7 +193,7 @@ typedef struct PlannerInfo
  *             upon creation of the RelOptInfo object; they are filled in when
  *             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.
  *
  * The presence of the remaining fields depends on the restrictions
@@ -232,7 +233,7 @@ typedef enum RelOptKind
 {
        RELOPT_BASEREL,
        RELOPT_JOINREL,
-       RELOPT_OTHER_CHILD_REL
+       RELOPT_OTHER_MEMBER_REL
 } RelOptKind;
 
 typedef struct RelOptInfo
@@ -508,8 +509,7 @@ typedef struct TidPath
 
 /*
  * AppendPath represents an Append plan, ie, successive execution of
- * several member plans.  Currently it is only used to handle expansion
- * of inheritance trees.
+ * several member plans.
  *
  * Note: it is possible for "subpaths" to contain only one, or even no,
  * elements.  These cases are optimized during create_append_plan.
@@ -879,4 +879,96 @@ typedef struct 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 */
index 6c8d62a..84d9f86 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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,
 /*
  * prototypes for relnode.c
  */
-extern void build_base_rel(PlannerInfo *root, int relid);
-extern RelOptInfo *build_other_rel(PlannerInfo *root, int relid);
+extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
+                                                                       RelOptKind reloptkind);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
 extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
 extern RelOptInfo *build_join_rel(PlannerInfo *root,
index ce89771..ca28cbc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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,
 
 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,
-                                          Index old_rt_index, Oid old_relid,
-                                          Index new_rt_index, Oid new_relid);
+extern Node *adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo);
+
+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 */