* 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 $
*
*-------------------------------------------------------------------------
*/
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
* ****************************************************************
case T_InClauseInfo:
retval = _copyInClauseInfo(from);
break;
+ case T_AppendRelInfo:
+ retval = _copyAppendRelInfo(from);
+ break;
/*
* VALUE NODES
* 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 $
*
*-------------------------------------------------------------------------
*/
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
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:
*
*
* 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*
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);
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");
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.
case T_InClauseInfo:
_outInClauseInfo(str, obj);
break;
+ case T_AppendRelInfo:
+ _outAppendRelInfo(str, obj);
+ break;
case T_CreateStmt:
_outCreateStmt(str, obj);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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 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)
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);
/* 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 */
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);
}
/*
- * 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,
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
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;
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
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,
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);
}
/*
}
}
+/*
+ * 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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*
* 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)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- build_base_rel(root, varno);
+ (void) build_simple_rel(root, varno, RELOPT_BASEREL);
}
else if (IsA(jtnode, FromExpr))
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
&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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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,
PlannerInfo *root;
Plan *plan;
List *newHaving;
- List *lst;
ListCell *l;
/* Set up for a new level of subquery */
/* 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
* 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);
}
}
+ /*
+ * 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
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)
* 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);
(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
*/
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 */
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
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);
/*
* 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
*/
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
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)
{
root->in_info_list = list_concat(root->in_info_list,
subroot->in_info_list);
+ /*
+ * XXX need to do something about adjusting AppendRelInfos too
+ */
+ Assert(root->append_rel_list == NIL);
+
+ /* Also pull up any subquery AppendRelInfos */
+ root->append_rel_list = list_concat(root->append_rel_list,
+ subroot->append_rel_list);
+
/*
* We don't have to do the equivalent bookkeeping for outer-join
* info, because that hasn't been set up yet.
* 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
*
*
* 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,
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);
/*
return rels_list;
}
+/*
+ * 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);
*/
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
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,
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.
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;
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;
}
{
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))
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))
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.
/* 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
* 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.
*/
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;
return node;
}
- return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
(void *) context);
}
return relids;
}
+/*
+ * 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
*
* 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;
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;
}
}
*
*
* 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
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));
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));
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
rel->indexlist = indexinfos;
- /* close rel, but keep lock if any */
heap_close(relation, NoLock);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
/*
- * 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;
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;
}
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;
}
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);
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];
}
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
/* 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 */
}
/* 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 */
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
T_InnerIndexscanInfo,
T_OuterJoinInfo,
T_InClauseInfo,
+ T_AppendRelInfo,
/*
* TAGS FOR MEMORY NODES (memnodes.h)
* 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 $
*
*-------------------------------------------------------------------------
*/
*
* 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.
*----------
*/
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
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 */
* 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
* 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
* 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
{
RELOPT_BASEREL,
RELOPT_JOINREL,
- RELOPT_OTHER_CHILD_REL
+ RELOPT_OTHER_MEMBER_REL
} RelOptKind;
typedef struct RelOptInfo
/*
* 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.
*/
} 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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,
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */