*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.39 1999/08/21 03:49:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.40 1999/12/14 03:35:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+typedef struct {
+ Index rt_index;
+ int sublevels_up;
+ Oid old_relid;
+ Oid new_relid;
+} fix_parsetree_attnums_context;
+
static List *plan_inherit_query(Relids relids, Index rt_index,
RangeTblEntry *rt_entry, Query *parse, List *tlist,
List **union_rtentriesPtr);
static RangeTblEntry *new_rangetable_entry(Oid new_relid,
RangeTblEntry *old_entry);
-static Query *subst_rangetable(Query *root, Index index,
- RangeTblEntry *new_entry);
static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
Oid new_relid, Query *parsetree);
-static Append *make_append(List *appendplans, List *unionrtables, Index rt_index,
- List *inheritrtable, List *tlist);
+static bool fix_parsetree_attnums_walker (Node *node,
+ fix_parsetree_attnums_context *context);
+static Append *make_append(List *appendplans, List *unionrtables,
+ Index rt_index,
+ List *inheritrtable, List *tlist);
/*
/*
* plan_inherit_queries
*
- * Plans the queries for a given parent relation.
+ * Plans the queries for an inheritance tree rooted at a parent relation.
*
* Inputs:
* parse = parent parse tree
List *union_relids;
List *union_plans;
- union_relids = find_all_inheritors(lconsi(rt_entry->relid,
- NIL),
- NIL);
+ /* Make a list of the target relid plus all its descendants */
+ union_relids = find_all_inheritors(rt_entry->relid);
/*
- * Remove the flag for this relation, since we're about to handle it
- * (do it before recursing!). XXX destructive change to parent parse tree
+ * Remove the flag for this relation, since we're about to handle it.
+ * XXX destructive change to parent parse tree, but necessary to prevent
+ * infinite recursion.
*/
rt_entry->inh = false;
/*
* plan_inherit_query
- * Returns a list of plans for 'relids' and a list of range table entries
- * in union_rtentries.
+ * Returns a list of plans for 'relids', plus a list of range table entries
+ * in *union_rtentriesPtr.
*/
static List *
plan_inherit_query(Relids relids,
{
List *union_plans = NIL;
List *union_rtentries = NIL;
+ List *save_tlist = root->targetList;
List *i;
+ /*
+ * Avoid making copies of the root's tlist, which we aren't going to
+ * use anyway (we are going to make copies of the passed tlist, instead).
+ */
+ root->targetList = NIL;
+
foreach(i, relids)
{
int relid = lfirsti(i);
+ /*
+ * Make a modifiable copy of the original query,
+ * and replace the target rangetable entry with a new one
+ * identifying this child table.
+ */
+ Query *new_root = copyObject(root);
RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
rt_entry);
- Query *new_root = subst_rangetable(root,
- rt_index,
- new_rt_entry);
+ rt_store(rt_index, new_root->rtable, new_rt_entry);
/*
- * Insert the desired simplified tlist into the subquery
+ * Insert (a modifiable copy of) the desired simplified tlist
+ * into the subquery
*/
new_root->targetList = copyObject(tlist);
new_root->havingQual = NULL;
new_root->hasAggs = false; /* shouldn't be any left ... */
- /* Fix attribute numbers as necessary */
+ /*
+ * Update attribute numbers in case child has different ordering
+ * of columns than parent (as can happen after ALTER TABLE).
+ *
+ * XXX This is a crock, and it doesn't really work. It'd be better
+ * to fix ALTER TABLE to preserve consistency of attribute numbering.
+ */
fix_parsetree_attnums(rt_index,
rt_entry->relid,
relid,
union_rtentries = lappend(union_rtentries, new_rt_entry);
}
+ root->targetList = save_tlist;
+
*union_rtentriesPtr = union_rtentries;
return union_plans;
}
/*
* find_all_inheritors -
- * Returns a list of relids corresponding to relations that inherit
- * attributes from any relations listed in either of the argument relid
- * lists.
+ * Returns an integer list of relids including the given rel plus
+ * all relations that inherit from it, directly or indirectly.
*/
List *
-find_all_inheritors(Relids unexamined_relids,
- Relids examined_relids)
+find_all_inheritors(Oid parentrel)
{
- List *new_inheritors = NIL;
- List *new_examined_relids;
- List *new_unexamined_relids;
- List *rels;
+ List *examined_relids = NIL;
+ List *unexamined_relids = lconsi(parentrel, NIL);
/*
- * Find all relations which inherit from members of
- * 'unexamined_relids' and store them in 'new_inheritors'.
+ * While the queue of unexamined relids is nonempty, remove the
+ * first element, mark it examined, and find its direct descendants.
+ * NB: cannot use foreach(), since we modify the queue inside loop.
*/
- foreach(rels, unexamined_relids)
+ while (unexamined_relids != NIL)
{
- new_inheritors = LispUnioni(new_inheritors,
- find_inheritance_children(lfirsti(rels)));
- }
+ Oid currentrel = lfirsti(unexamined_relids);
+ List *currentchildren;
- new_examined_relids = LispUnioni(examined_relids, unexamined_relids);
- new_unexamined_relids = set_differencei(new_inheritors,
- new_examined_relids);
+ unexamined_relids = lnext(unexamined_relids);
+ examined_relids = lappendi(examined_relids, currentrel);
+ currentchildren = find_inheritance_children(currentrel);
+ /*
+ * Add to the queue only those children not already seen.
+ * This could probably be simplified to a plain nconc,
+ * because our inheritance relationships should always be a
+ * strict tree, no? Should never find any matches, ISTM...
+ */
+ currentchildren = set_differencei(currentchildren, examined_relids);
+ unexamined_relids = LispUnioni(unexamined_relids, currentchildren);
+ }
- if (new_unexamined_relids == NIL)
- return new_examined_relids;
- else
- return find_all_inheritors(new_unexamined_relids,
- new_examined_relids);
+ return examined_relids;
}
/*
* first_inherit_rt_entry -
* Given a rangetable, find the first rangetable entry that represents
- * the appropriate special case.
+ * an inheritance set.
*
- * Returns a rangetable index., Returns -1 if no matches
+ * Returns a rangetable index (1..n).
+ * Returns -1 if no matches
*/
int
first_inherit_rt_entry(List *rangetable)
{
RangeTblEntry *rt_entry = lfirst(temp);
- if (rt_entry->inh)
- return count + 1;
count++;
+ if (rt_entry->inh)
+ return count;
}
return -1;
}
/*
- * subst_rangetable
- * Replaces the 'index'th rangetable entry in 'root' with 'new_entry'.
+ * fix_parsetree_attnums
+ * Replaces attribute numbers from the relation represented by
+ * 'old_relid' in 'parsetree' with the attribute numbers from
+ * 'new_relid'.
*
- * Returns a new copy of 'root'.
+ * The parsetree is MODIFIED IN PLACE. This is OK only because
+ * plan_inherit_query made a copy of the tree for us to hack upon.
*/
-static Query *
-subst_rangetable(Query *root, Index index, RangeTblEntry *new_entry)
+static void
+fix_parsetree_attnums(Index rt_index,
+ Oid old_relid,
+ Oid new_relid,
+ Query *parsetree)
{
- Query *new_root = copyObject(root);
- List *temp;
- int i;
+ fix_parsetree_attnums_context context;
- for (temp = new_root->rtable, i = 1; i < index; temp = lnext(temp), i++)
- ;
- lfirst(temp) = new_entry;
+ if (old_relid == new_relid)
+ return; /* no work needed for parent rel itself */
- return new_root;
+ context.rt_index = rt_index;
+ context.old_relid = old_relid;
+ context.new_relid = new_relid;
+ context.sublevels_up = 0;
+ /*
+ * We must scan both the targetlist and qual, but we know the
+ * havingQual is empty, so we can ignore it.
+ */
+ fix_parsetree_attnums_walker((Node *) parsetree->targetList, &context);
+ fix_parsetree_attnums_walker((Node *) parsetree->qual, &context);
}
/*
* for inherited attributes. It'd be better to rip this code out and fix
* ALTER TABLE...
*/
-static void
-fix_parsetree_attnums_nodes(Index rt_index,
- Oid old_relid,
- Oid new_relid,
- Node *node)
+static bool
+fix_parsetree_attnums_walker (Node *node,
+ fix_parsetree_attnums_context *context)
{
if (node == NULL)
- return;
-
- switch (nodeTag(node))
+ return false;
+ if (IsA(node, Var))
{
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
- tle->expr);
- }
- break;
- case T_Expr:
- {
- Expr *expr = (Expr *) node;
-
- fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
- (Node *) expr->args);
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
-
- if (var->varno == rt_index && var->varattno != 0)
- {
- var->varattno = get_attnum(new_relid,
- get_attname(old_relid, var->varattno));
- }
- }
- break;
- case T_List:
- {
- List *l;
-
- foreach(l, (List *) node)
- {
- fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
- (Node *) lfirst(l));
- }
- }
- break;
- default:
- break;
- }
-}
+ Var *var = (Var *) node;
-/*
- * fix_parsetree_attnums
- * Replaces attribute numbers from the relation represented by
- * 'old_relid' in 'parsetree' with the attribute numbers from
- * 'new_relid'.
- *
- * Returns the destructively_modified parsetree.
- *
- */
-static void
-fix_parsetree_attnums(Index rt_index,
- Oid old_relid,
- Oid new_relid,
- Query *parsetree)
-{
- if (old_relid == new_relid)
- return;
+ if (var->varlevelsup == context->sublevels_up &&
+ var->varno == context->rt_index &&
+ var->varattno > 0)
+ {
+ var->varattno = get_attnum(context->new_relid,
+ get_attname(context->old_relid,
+ var->varattno));
+ }
+ return false;
+ }
+ if (IsA(node, SubLink))
+ {
+ /*
+ * Standard expression_tree_walker will not recurse into subselect,
+ * but here we must do so.
+ */
+ SubLink *sub = (SubLink *) node;
- fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
- (Node *) parsetree->targetList);
- fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
- parsetree->qual);
+ if (fix_parsetree_attnums_walker((Node *) (sub->lefthand), context))
+ return true;
+ context->sublevels_up++;
+ if (fix_parsetree_attnums_walker((Node *) (sub->subselect), context))
+ {
+ context->sublevels_up--;
+ return true;
+ }
+ context->sublevels_up--;
+ return false;
+ }
+ if (IsA(node, Query))
+ {
+ /* Reach here after recursing down into subselect above... */
+ Query *qry = (Query *) node;
+
+ if (fix_parsetree_attnums_walker((Node *) (qry->targetList), context))
+ return true;
+ if (fix_parsetree_attnums_walker((Node *) (qry->qual), context))
+ return true;
+ if (fix_parsetree_attnums_walker((Node *) (qry->havingQual), context))
+ return true;
+ return false;
+ }
+ return expression_tree_walker(node, fix_parsetree_attnums_walker,
+ (void *) context);
}
static Append *