summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep/prepunion.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep/prepunion.c')
-rw-r--r--src/backend/optimizer/prep/prepunion.c625
1 files changed, 427 insertions, 198 deletions
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 227eab83ce..a33213ef59 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -5,8 +5,12 @@
* from a time when only UNIONs were implemented.
*
* There is also some code here to support planning of queries that use
- * inheritance (SELECT FROM foo*). This no longer has much connection
- * to the processing of UNION queries, but it's still here.
+ * inheritance (SELECT FROM foo*). Although inheritance is radically
+ * different from set operations as far as the parser representation of
+ * a query is concerned, we try to handle it identically to the UNION ALL
+ * case during planning: both are converted to "append rels". (Note that
+ * UNION ALL is special-cased: other kinds of set operations go through
+ * a completely different code path.)
*
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
@@ -14,7 +18,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.128 2005/11/22 18:17:14 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.129 2006/01/31 21:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,18 +42,6 @@
#include "utils/lsyscache.h"
-typedef struct
-{
- Index old_rt_index;
- Index new_rt_index;
- Oid old_rel_type;
- Oid new_rel_type;
- TupleDesc old_tupdesc;
- TupleDesc new_tupdesc;
- char *old_rel_name;
- char *new_rel_name;
-} adjust_inherited_attrs_context;
-
static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
double tuple_fraction,
List *colTypes, bool junkOK,
@@ -73,11 +65,16 @@ static List *generate_append_tlist(List *colTypes, bool flag,
List *input_plans,
List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
-static Node *adjust_inherited_attrs_mutator(Node *node,
- adjust_inherited_attrs_context *context);
+static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
+ Index rti);
+static void make_translation_lists(Relation oldrelation, Relation newrelation,
+ Index newvarno,
+ List **col_mappings, List **translated_vars);
+static Node *adjust_appendrel_attrs_mutator(Node *node,
+ AppendRelInfo *context);
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
static List *adjust_inherited_tlist(List *tlist,
- adjust_inherited_attrs_context *context);
+ AppendRelInfo *context);
/*
@@ -740,50 +737,80 @@ find_all_inheritors(Oid parentrel)
}
/*
+ * expand_inherited_tables
+ * Expand each rangetable entry that represents an inheritance set
+ * into an "append relation". At the conclusion of this process,
+ * the "inh" flag is set in all and only those RTEs that are append
+ * relation parents.
+ */
+void
+expand_inherited_tables(PlannerInfo *root)
+{
+ Index nrtes;
+ Index rti;
+ ListCell *rl;
+
+ /*
+ * expand_inherited_rtentry may add RTEs to parse->rtable; there is
+ * no need to scan them since they can't have inh=true. So just
+ * scan as far as the original end of the rtable list.
+ */
+ nrtes = list_length(root->parse->rtable);
+ rl = list_head(root->parse->rtable);
+ for (rti = 1; rti <= nrtes; rti++)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
+
+ expand_inherited_rtentry(root, rte, rti);
+ rl = lnext(rl);
+ }
+}
+
+/*
* expand_inherited_rtentry
* Check whether a rangetable entry represents an inheritance set.
* If so, add entries for all the child tables to the query's
- * rangetable, and return an integer list of RT indexes for the
- * whole inheritance set (parent and children).
- * If not, return NIL.
+ * rangetable, and build AppendRelInfo nodes for all the child tables
+ * and add them to root->append_rel_list. If not, clear the entry's
+ * "inh" flag to prevent later code from looking for AppendRelInfos.
*
* Note that the original RTE is considered to represent the whole
- * inheritance set. The first member of the returned list is an RTE
- * for the same table, but with inh = false, to represent the parent table
- * in its role as a simple member of the set. The original RT index is
- * never a member of the returned list.
+ * inheritance set. The first of the generated RTEs is an RTE for the same
+ * table, but with inh = false, to represent the parent table in its role
+ * as a simple member of the inheritance set.
*
* A childless table is never considered to be an inheritance set; therefore
- * the result will never be a one-element list. It'll be either empty
- * or have two or more elements.
- *
- * Note: there are cases in which this routine will be invoked multiple
- * times on the same RTE. We will generate a separate set of child RTEs
- * for each invocation. This is somewhat wasteful but seems not worth
- * trying to avoid.
+ * a parent RTE must always have at least two associated AppendRelInfos.
*/
-List *
-expand_inherited_rtentry(PlannerInfo *root, Index rti)
+static void
+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
{
Query *parse = root->parse;
- RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
Oid parentOID;
+ Relation oldrelation;
+ LOCKMODE lockmode;
List *inhOIDs;
- List *inhRTIs;
+ List *appinfos;
ListCell *l;
/* Does RT entry allow inheritance? */
if (!rte->inh)
- return NIL;
- Assert(rte->rtekind == RTE_RELATION);
+ return;
+ /* Ignore any already-expanded UNION ALL nodes */
+ if (rte->rtekind != RTE_RELATION)
+ {
+ Assert(rte->rtekind == RTE_SUBQUERY);
+ return;
+ }
/* Fast path for common case of childless table */
parentOID = rte->relid;
if (!has_subclass(parentOID))
{
- /* Clear flag to save repeated tests if called again */
+ /* Clear flag before returning */
rte->inh = false;
- return NIL;
+ return;
}
+
/* Scan for all members of inheritance set */
inhOIDs = find_all_inheritors(parentOID);
@@ -794,18 +821,45 @@ expand_inherited_rtentry(PlannerInfo *root, Index rti)
*/
if (list_length(inhOIDs) < 2)
{
- /* Clear flag to save repeated tests if called again */
+ /* Clear flag before returning */
rte->inh = false;
- return NIL;
+ return;
}
- /* OK, it's an inheritance set; expand it */
- inhRTIs = NIL;
+ /*
+ * Must open the parent relation to examine its tupdesc. We need not
+ * lock it since the rewriter already obtained at least AccessShareLock
+ * on each relation used in the query.
+ */
+ oldrelation = heap_open(parentOID, NoLock);
+
+ /*
+ * However, for each child relation we add to the query, we must obtain
+ * an appropriate lock, because this will be the first use of those
+ * relations in the parse/rewrite/plan pipeline.
+ *
+ * If the parent relation is the query's result relation, then we need
+ * RowExclusiveLock. Otherwise, check to see if the relation is accessed
+ * FOR UPDATE/SHARE or not. We can't just grab AccessShareLock because
+ * then the executor would be trying to upgrade the lock, leading to
+ * possible deadlocks. (This code should match the parser and rewriter.)
+ */
+ if (rti == parse->resultRelation)
+ lockmode = RowExclusiveLock;
+ else if (list_member_int(parse->rowMarks, rti))
+ lockmode = RowShareLock;
+ else
+ lockmode = AccessShareLock;
+
+ /* Scan the inheritance set and expand it */
+ appinfos = NIL;
foreach(l, inhOIDs)
{
Oid childOID = lfirst_oid(l);
+ Relation newrelation;
RangeTblEntry *childrte;
Index childRTindex;
+ AppendRelInfo *appinfo;
/*
* It is possible that the parent table has children that are temp
@@ -817,6 +871,12 @@ expand_inherited_rtentry(PlannerInfo *root, Index rti)
isOtherTempNamespace(get_rel_namespace(childOID)))
continue;
+ /* Open rel, acquire the appropriate lock type */
+ if (childOID != parentOID)
+ newrelation = heap_open(childOID, lockmode);
+ else
+ newrelation = oldrelation;
+
/*
* Build an RTE for the child, and attach to query's rangetable list.
* We copy most fields of the parent's RTE, but replace relation OID,
@@ -827,75 +887,160 @@ expand_inherited_rtentry(PlannerInfo *root, Index rti)
childrte->inh = false;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = list_length(parse->rtable);
- inhRTIs = lappend_int(inhRTIs, childRTindex);
+
+ /*
+ * Build an AppendRelInfo for this parent and child.
+ */
+ appinfo = makeNode(AppendRelInfo);
+ appinfo->parent_relid = rti;
+ appinfo->child_relid = childRTindex;
+ appinfo->parent_reltype = oldrelation->rd_rel->reltype;
+ appinfo->child_reltype = newrelation->rd_rel->reltype;
+ make_translation_lists(oldrelation, newrelation, childRTindex,
+ &appinfo->col_mappings,
+ &appinfo->translated_vars);
+ appinfo->parent_reloid = parentOID;
+ appinfos = lappend(appinfos, appinfo);
+
+ /* Close child relations, but keep locks */
+ if (childOID != parentOID)
+ heap_close(newrelation, NoLock);
}
+ heap_close(oldrelation, NoLock);
+
/*
* If all the children were temp tables, pretend it's a non-inheritance
* situation. The duplicate RTE we added for the parent table is
- * harmless.
+ * harmless, so we don't bother to get rid of it.
*/
- if (list_length(inhRTIs) < 2)
+ if (list_length(appinfos) < 2)
{
- /* Clear flag to save repeated tests if called again */
+ /* Clear flag before returning */
rte->inh = false;
- return NIL;
+ return;
}
+ /* Otherwise, OK to add to root->append_rel_list */
+ root->append_rel_list = list_concat(root->append_rel_list, appinfos);
+
/*
* The executor will check the parent table's access permissions when it
- * examines the parent's inheritlist entry. There's no need to check
- * twice, so turn off access check bits in the original RTE. (If we are
- * invoked more than once, extra copies of the child RTEs will also not
- * cause duplicate permission checks.)
+ * examines the parent's added RTE entry. There's no need to check
+ * twice, so turn off access check bits in the original RTE.
*/
rte->requiredPerms = 0;
+}
+
+/*
+ * make_translation_lists
+ * Build the lists of translations from parent Vars to child Vars for
+ * an inheritance child. We need both a column number mapping list
+ * and a list of Vars representing the child columns.
+ *
+ * For paranoia's sake, we match type as well as attribute name.
+ */
+static void
+make_translation_lists(Relation oldrelation, Relation newrelation,
+ Index newvarno,
+ List **col_mappings, List **translated_vars)
+{
+ List *numbers = NIL;
+ List *vars = NIL;
+ TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
+ TupleDesc new_tupdesc = RelationGetDescr(newrelation);
+ int oldnatts = old_tupdesc->natts;
+ int newnatts = new_tupdesc->natts;
+ int old_attno;
+
+ for (old_attno = 0; old_attno < oldnatts; old_attno++)
+ {
+ Form_pg_attribute att;
+ char *attname;
+ Oid atttypid;
+ int32 atttypmod;
+ int new_attno;
+
+ att = old_tupdesc->attrs[old_attno];
+ if (att->attisdropped)
+ {
+ /* Just put 0/NULL into this list entry */
+ numbers = lappend_int(numbers, 0);
+ vars = lappend(vars, NULL);
+ continue;
+ }
+ attname = NameStr(att->attname);
+ atttypid = att->atttypid;
+ atttypmod = att->atttypmod;
- return inhRTIs;
+ /*
+ * When we are generating the "translation list" for the parent
+ * table of an inheritance set, no need to search for matches.
+ */
+ if (oldrelation == newrelation)
+ {
+ numbers = lappend_int(numbers, old_attno + 1);
+ vars = lappend(vars, makeVar(newvarno,
+ (AttrNumber) (old_attno + 1),
+ atttypid,
+ atttypmod,
+ 0));
+ continue;
+ }
+
+ /*
+ * Otherwise we have to search for the matching column by name.
+ * There's no guarantee it'll have the same column position,
+ * because of cases like ALTER TABLE ADD COLUMN and multiple
+ * inheritance.
+ */
+ for (new_attno = 0; new_attno < newnatts; new_attno++)
+ {
+ att = new_tupdesc->attrs[new_attno];
+ if (att->attisdropped || att->attinhcount == 0)
+ continue;
+ if (strcmp(attname, NameStr(att->attname)) != 0)
+ continue;
+ /* Found it, check type */
+ if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+ elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
+ attname, RelationGetRelationName(newrelation));
+
+ numbers = lappend_int(numbers, new_attno + 1);
+ vars = lappend(vars, makeVar(newvarno,
+ (AttrNumber) (new_attno + 1),
+ atttypid,
+ atttypmod,
+ 0));
+ break;
+ }
+
+ if (new_attno >= newnatts)
+ elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
+ attname, RelationGetRelationName(newrelation));
+ }
+
+ *col_mappings = numbers;
+ *translated_vars = vars;
}
/*
- * adjust_inherited_attrs
+ * adjust_appendrel_attrs
* Copy the specified query or expression and translate Vars referring
- * to old_rt_index to refer to new_rt_index.
+ * to the parent rel of the specified AppendRelInfo to refer to the
+ * child rel instead. We also update rtindexes appearing outside Vars,
+ * such as resultRelation and jointree relids.
*
- * We also adjust varattno to match the new table by column name, rather
- * than column number. This hack makes it possible for child tables to have
- * different column positions for the "same" attribute as a parent, which
- * is necessary for ALTER TABLE ADD COLUMN.
+ * Note: this is only applied after conversion of sublinks to subplans,
+ * so we don't need to cope with recursion into sub-queries.
+ *
+ * Note: this is not hugely different from what ResolveNew() does; maybe
+ * we should try to fold the two routines together.
*/
Node *
-adjust_inherited_attrs(Node *node,
- Index old_rt_index, Oid old_relid,
- Index new_rt_index, Oid new_relid)
+adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo)
{
Node *result;
- adjust_inherited_attrs_context context;
- Relation oldrelation;
- Relation newrelation;
-
- /* Handle simple case simply... */
- if (old_rt_index == new_rt_index)
- {
- Assert(old_relid == new_relid);
- return copyObject(node);
- }
-
- /*
- * We assume that by now the planner has acquired at least AccessShareLock
- * on both rels, and so we need no additional lock now.
- */
- oldrelation = heap_open(old_relid, NoLock);
- newrelation = heap_open(new_relid, NoLock);
-
- context.old_rt_index = old_rt_index;
- context.new_rt_index = new_rt_index;
- context.old_rel_type = oldrelation->rd_rel->reltype;
- context.new_rel_type = newrelation->rd_rel->reltype;
- context.old_tupdesc = RelationGetDescr(oldrelation);
- context.new_tupdesc = RelationGetDescr(newrelation);
- context.old_rel_name = RelationGetRelationName(oldrelation);
- context.new_rel_name = RelationGetRelationName(newrelation);
/*
* Must be prepared to start with a Query or a bare expression tree.
@@ -905,80 +1050,28 @@ adjust_inherited_attrs(Node *node,
Query *newnode;
newnode = query_tree_mutator((Query *) node,
- adjust_inherited_attrs_mutator,
- (void *) &context,
+ adjust_appendrel_attrs_mutator,
+ (void *) appinfo,
QTW_IGNORE_RT_SUBQUERIES);
- if (newnode->resultRelation == old_rt_index)
+ if (newnode->resultRelation == appinfo->parent_relid)
{
- newnode->resultRelation = new_rt_index;
+ newnode->resultRelation = appinfo->child_relid;
/* Fix tlist resnos too, if it's inherited UPDATE */
if (newnode->commandType == CMD_UPDATE)
newnode->targetList =
adjust_inherited_tlist(newnode->targetList,
- &context);
+ appinfo);
}
result = (Node *) newnode;
}
else
- result = adjust_inherited_attrs_mutator(node, &context);
-
- heap_close(oldrelation, NoLock);
- heap_close(newrelation, NoLock);
+ result = adjust_appendrel_attrs_mutator(node, appinfo);
return result;
}
-/*
- * Translate parent's attribute number into child's.
- *
- * For paranoia's sake, we match type as well as attribute name.
- */
-static AttrNumber
-translate_inherited_attnum(AttrNumber old_attno,
- adjust_inherited_attrs_context *context)
-{
- Form_pg_attribute att;
- char *attname;
- Oid atttypid;
- int32 atttypmod;
- int newnatts;
- int i;
-
- if (old_attno <= 0 || old_attno > context->old_tupdesc->natts)
- elog(ERROR, "attribute %d of relation \"%s\" does not exist",
- (int) old_attno, context->old_rel_name);
- att = context->old_tupdesc->attrs[old_attno - 1];
- if (att->attisdropped)
- elog(ERROR, "attribute %d of relation \"%s\" does not exist",
- (int) old_attno, context->old_rel_name);
- attname = NameStr(att->attname);
- atttypid = att->atttypid;
- atttypmod = att->atttypmod;
-
- newnatts = context->new_tupdesc->natts;
- for (i = 0; i < newnatts; i++)
- {
- att = context->new_tupdesc->attrs[i];
- if (att->attisdropped)
- continue;
- if (strcmp(attname, NameStr(att->attname)) == 0)
- {
- /* Found it, check type */
- if (atttypid != att->atttypid || atttypmod != att->atttypmod)
- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
- attname, context->new_rel_name);
- return (AttrNumber) (i + 1);
- }
- }
-
- elog(ERROR, "attribute \"%s\" of relation \"%s\" does not exist",
- attname, context->new_rel_name);
- return 0; /* keep compiler quiet */
-}
-
static Node *
-adjust_inherited_attrs_mutator(Node *node,
- adjust_inherited_attrs_context *context)
+adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
{
if (node == NULL)
return NULL;
@@ -987,36 +1080,54 @@ adjust_inherited_attrs_mutator(Node *node,
Var *var = (Var *) copyObject(node);
if (var->varlevelsup == 0 &&
- var->varno == context->old_rt_index)
+ var->varno == context->parent_relid)
{
- var->varno = context->new_rt_index;
- var->varnoold = context->new_rt_index;
+ var->varno = context->child_relid;
+ var->varnoold = context->child_relid;
if (var->varattno > 0)
{
- var->varattno = translate_inherited_attnum(var->varattno,
- context);
- var->varoattno = var->varattno;
+ Node *newnode;
+
+ if (var->varattno > list_length(context->translated_vars))
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ var->varattno, get_rel_name(context->parent_reloid));
+ newnode = copyObject(list_nth(context->translated_vars,
+ var->varattno - 1));
+ if (newnode == NULL)
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ var->varattno, get_rel_name(context->parent_reloid));
+ return newnode;
}
else if (var->varattno == 0)
{
/*
- * Whole-row Var: we need to insert a coercion step to convert
- * the tuple layout to the parent's rowtype.
+ * Whole-row Var: if we are dealing with named rowtypes,
+ * we can use a whole-row Var for the child table plus a
+ * coercion step to convert the tuple layout to the parent's
+ * rowtype. Otherwise we have to generate a RowExpr.
*/
- if (context->old_rel_type != context->new_rel_type)
+ if (OidIsValid(context->child_reltype))
{
- ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
-
- r->arg = (Expr *) var;
- r->resulttype = context->old_rel_type;
- r->convertformat = COERCE_IMPLICIT_CAST;
- /* Make sure the Var node has the right type ID, too */
- Assert(var->vartype == context->old_rel_type);
- var->vartype = context->new_rel_type;
- return (Node *) r;
+ Assert(var->vartype == context->parent_reltype);
+ if (context->parent_reltype != context->child_reltype)
+ {
+ ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
+
+ r->arg = (Expr *) var;
+ r->resulttype = context->parent_reltype;
+ r->convertformat = COERCE_IMPLICIT_CAST;
+ /* Make sure the Var node has the right type ID, too */
+ var->vartype = context->child_reltype;
+ return (Node *) r;
+ }
+ }
+ else
+ {
+ /* XXX copy some code from ResolveNew */
+ Assert(false);/* not done yet */
}
}
- /* system attributes don't need any translation */
+ /* system attributes don't need any other translation */
}
return (Node *) var;
}
@@ -1024,8 +1135,8 @@ adjust_inherited_attrs_mutator(Node *node,
{
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
- if (rtr->rtindex == context->old_rt_index)
- rtr->rtindex = context->new_rt_index;
+ if (rtr->rtindex == context->parent_relid)
+ rtr->rtindex = context->child_relid;
return (Node *) rtr;
}
if (IsA(node, JoinExpr))
@@ -1034,11 +1145,11 @@ adjust_inherited_attrs_mutator(Node *node,
JoinExpr *j;
j = (JoinExpr *) expression_tree_mutator(node,
- adjust_inherited_attrs_mutator,
+ adjust_appendrel_attrs_mutator,
(void *) context);
- /* now fix JoinExpr's rtindex */
- if (j->rtindex == context->old_rt_index)
- j->rtindex = context->new_rt_index;
+ /* now fix JoinExpr's rtindex (probably never happens) */
+ if (j->rtindex == context->parent_relid)
+ j->rtindex = context->child_relid;
return (Node *) j;
}
if (IsA(node, InClauseInfo))
@@ -1047,17 +1158,20 @@ adjust_inherited_attrs_mutator(Node *node,
InClauseInfo *ininfo;
ininfo = (InClauseInfo *) expression_tree_mutator(node,
- adjust_inherited_attrs_mutator,
+ adjust_appendrel_attrs_mutator,
(void *) context);
/* now fix InClauseInfo's relid sets */
ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
ininfo->righthand = adjust_relid_set(ininfo->righthand,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
return (Node *) ininfo;
}
+ /* Shouldn't need to handle OuterJoinInfo or AppendRelInfo here */
+ Assert(!IsA(node, OuterJoinInfo));
+ Assert(!IsA(node, AppendRelInfo));
/*
* We have to process RestrictInfo nodes specially.
@@ -1072,25 +1186,25 @@ adjust_inherited_attrs_mutator(Node *node,
/* Recursively fix the clause itself */
newinfo->clause = (Expr *)
- adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
+ adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
/* and the modified version, if an OR clause */
newinfo->orclause = (Expr *)
- adjust_inherited_attrs_mutator((Node *) oldinfo->orclause, context);
+ adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
/* adjust relid sets too */
newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
newinfo->required_relids = adjust_relid_set(oldinfo->required_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
- context->old_rt_index,
- context->new_rt_index);
+ context->parent_relid,
+ context->child_relid);
/*
* Reset cached derivative fields, since these might need to have
@@ -1119,7 +1233,7 @@ adjust_inherited_attrs_mutator(Node *node,
* BUT: although we don't need to recurse into subplans, we do need to
* make sure that they are copied, not just referenced as
* expression_tree_mutator will do by default. Otherwise we'll have the
- * same subplan node referenced from each arm of the inheritance APPEND
+ * same subplan node referenced from each arm of the finished APPEND
* plan, which will cause trouble in the executor. This is a kluge that
* should go away when we redesign querytrees.
*/
@@ -1128,7 +1242,7 @@ adjust_inherited_attrs_mutator(Node *node,
SubPlan *subplan;
/* Copy the node and process subplan args */
- node = expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
(void *) context);
/* Make sure we have separate copies of subplan and its rtable */
subplan = (SubPlan *) node;
@@ -1137,7 +1251,7 @@ adjust_inherited_attrs_mutator(Node *node,
return node;
}
- return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
(void *) context);
}
@@ -1159,6 +1273,110 @@ adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
}
/*
+ * adjust_appendrel_attr_needed
+ * Adjust an attr_needed[] array to reference a member rel instead of
+ * the original appendrel
+ *
+ * oldrel: source of data (we use the attr_needed, min_attr, max_attr fields)
+ * appinfo: supplies parent_relid, child_relid, col_mappings
+ * new_min_attr, new_max_attr: desired bounds of new attr_needed array
+ *
+ * The relid sets are adjusted by substituting child_relid for parent_relid.
+ * (NOTE: oldrel is not necessarily the parent_relid relation!) We are also
+ * careful to map attribute numbers within the array properly. User
+ * attributes have to be mapped through col_mappings, but system attributes
+ * and whole-row references always have the same attno.
+ *
+ * Returns a palloc'd array with the specified bounds
+ */
+Relids *
+adjust_appendrel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
+ AttrNumber new_min_attr, AttrNumber new_max_attr)
+{
+ Relids *new_attr_needed;
+ Index parent_relid = appinfo->parent_relid;
+ Index child_relid = appinfo->child_relid;
+ int parent_attr;
+ ListCell *lm;
+
+ /* Create empty result array */
+ Assert(new_min_attr <= oldrel->min_attr);
+ Assert(new_max_attr >= oldrel->max_attr);
+ new_attr_needed = (Relids *)
+ palloc0((new_max_attr - new_min_attr + 1) * sizeof(Relids));
+ /* Process user attributes, with appropriate attno mapping */
+ parent_attr = 1;
+ foreach(lm, appinfo->col_mappings)
+ {
+ int child_attr = lfirst_int(lm);
+
+ if (child_attr > 0)
+ {
+ Relids attrneeded;
+
+ Assert(parent_attr <= oldrel->max_attr);
+ Assert(child_attr <= new_max_attr);
+ attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
+ attrneeded = adjust_relid_set(attrneeded,
+ parent_relid, child_relid);
+ new_attr_needed[child_attr - new_min_attr] = attrneeded;
+ }
+ parent_attr++;
+ }
+ /* Process system attributes, including whole-row references */
+ for (parent_attr = oldrel->min_attr; parent_attr <= 0; parent_attr++)
+ {
+ Relids attrneeded;
+
+ attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
+ attrneeded = adjust_relid_set(attrneeded,
+ parent_relid, child_relid);
+ new_attr_needed[parent_attr - new_min_attr] = attrneeded;
+ }
+
+ return new_attr_needed;
+}
+
+/*
+ * adjust_other_rel_attr_needed
+ * Adjust an attr_needed[] array to reference a member rel instead of
+ * the original appendrel
+ *
+ * This is exactly like adjust_appendrel_attr_needed except that we disregard
+ * appinfo->col_mappings and instead assume that the mapping of user
+ * attributes is one-to-one. This is appropriate for generating an attr_needed
+ * array that describes another relation to be joined with a member rel.
+ */
+Relids *
+adjust_other_rel_attr_needed(RelOptInfo *oldrel, AppendRelInfo *appinfo,
+ AttrNumber new_min_attr, AttrNumber new_max_attr)
+{
+ Relids *new_attr_needed;
+ Index parent_relid = appinfo->parent_relid;
+ Index child_relid = appinfo->child_relid;
+ int parent_attr;
+
+ /* Create empty result array */
+ Assert(new_min_attr <= oldrel->min_attr);
+ Assert(new_max_attr >= oldrel->max_attr);
+ new_attr_needed = (Relids *)
+ palloc0((new_max_attr - new_min_attr + 1) * sizeof(Relids));
+ /* Process user attributes and system attributes */
+ for (parent_attr = oldrel->min_attr; parent_attr <= oldrel->max_attr;
+ parent_attr++)
+ {
+ Relids attrneeded;
+
+ attrneeded = oldrel->attr_needed[parent_attr - oldrel->min_attr];
+ attrneeded = adjust_relid_set(attrneeded,
+ parent_relid, child_relid);
+ new_attr_needed[parent_attr - new_min_attr] = attrneeded;
+ }
+
+ return new_attr_needed;
+}
+
+/*
* Adjust the targetlist entries of an inherited UPDATE operation
*
* The expressions have already been fixed, but we have to make sure that
@@ -1175,8 +1393,7 @@ adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
* Note that this is not needed for INSERT because INSERT isn't inheritable.
*/
static List *
-adjust_inherited_tlist(List *tlist,
- adjust_inherited_attrs_context *context)
+adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
{
bool changed_it = false;
ListCell *tl;
@@ -1184,19 +1401,31 @@ adjust_inherited_tlist(List *tlist,
bool more;
int attrno;
- /* Scan tlist and update resnos to match attnums of new_relid */
+ /* This should only happen for an inheritance case, not UNION ALL */
+ Assert(OidIsValid(context->parent_reloid));
+
+ /* Scan tlist and update resnos to match attnums of child rel */
foreach(tl, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ int newattno;
if (tle->resjunk)
continue; /* ignore junk items */
- attrno = translate_inherited_attnum(tle->resno, context);
-
- if (tle->resno != attrno)
+ /* Look up the translation of this column */
+ if (tle->resno <= 0 ||
+ tle->resno > list_length(context->col_mappings))
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ tle->resno, get_rel_name(context->parent_reloid));
+ newattno = list_nth_int(context->col_mappings, tle->resno - 1);
+ if (newattno <= 0)
+ elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+ tle->resno, get_rel_name(context->parent_reloid));
+
+ if (tle->resno != newattno)
{
- tle->resno = attrno;
+ tle->resno = newattno;
changed_it = true;
}
}