aboutsummaryrefslogtreecommitdiff
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.c473
1 files changed, 165 insertions, 308 deletions
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ea9b902bc5b..ebb09f59393 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1,15 +1,20 @@
/*-------------------------------------------------------------------------
*
* prepunion.c
- * Routines to plan set-operation and inheritance queries. The filename
- * is a leftover from a time when only UNIONs were handled.
+ * Routines to plan set-operation queries. The filename is a leftover
+ * 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.
+ *
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.55 2000/11/09 02:46:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.56 2000/11/12 00:36:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,13 +35,19 @@
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+/* macros borrowed from expression_tree_mutator */
+
+#define FLATCOPY(newnode, node, nodetype) \
+ ( (newnode) = makeNode(nodetype), \
+ memcpy((newnode), (node), sizeof(nodetype)) )
+
typedef struct
{
- Index rt_index;
- int sublevels_up;
+ Index old_rt_index;
+ Index new_rt_index;
Oid old_relid;
Oid new_relid;
-} fix_parsetree_attnums_context;
+} adjust_inherited_attrs_context;
static Plan *recurse_set_operations(Node *setOp, Query *parse,
List *colTypes, bool junkOK,
@@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag,
List *input_tlist,
List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
-static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
- Oid new_relid, Query *parsetree);
-static bool fix_parsetree_attnums_walker(Node *node,
- fix_parsetree_attnums_context *context);
-static RangeTblEntry *new_rangetable_entry(Oid new_relid,
- RangeTblEntry *old_entry);
-static Append *make_append(List *appendplans, Index rt_index,
- List *inheritrtable, List *tlist);
+static Node *adjust_inherited_attrs_mutator(Node *node,
+ adjust_inherited_attrs_context *context);
/*
@@ -69,8 +74,8 @@ static Append *make_append(List *appendplans, Index rt_index,
* Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
*
* This routine only deals with the setOperations tree of the given query.
- * Any top-level ORDER BY requested in parse->sortClause will be added on
- * back in union_planner.
+ * Any top-level ORDER BY requested in parse->sortClause will be added
+ * when we return to grouping_planner.
*/
Plan *
plan_set_operations(Query *parse)
@@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse,
NIL,
rtr->rtindex,
subplan);
- copy_plan_costsize(plan, subplan);
return plan;
}
else if (IsA(setOp, SetOperationStmt))
@@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
*/
plan = (Plan *)
make_append(planlist,
- 0,
- NIL,
+ false,
generate_setop_tlist(op->colTypes, -1, false,
((Plan *) lfirst(planlist))->targetlist,
refnames_tlist));
@@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
*/
plan = (Plan *)
make_append(makeList2(lplan, rplan),
- 0,
- NIL,
+ false,
generate_setop_tlist(op->colTypes, 0, false,
lplan->targetlist,
refnames_tlist));
@@ -457,132 +459,6 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
/*
- * plan_inherit_queries
- * Plans the queries for an inheritance tree rooted at a parent relation.
- *
- * Inputs:
- * root = parent parse tree
- * tlist = target list for inheritance subqueries (not same as parent's!)
- * rt_index = rangetable index for current inheritance item
- * inheritors = list of OIDs of the target rel plus all its descendants
- *
- * Returns an APPEND node that forms the result of performing the given
- * query for each member relation of the inheritance group.
- *
- * If grouping, aggregation, or sorting is specified in the parent plan,
- * the subplans should not do any of those steps --- we must do those
- * operations just once above the APPEND node. The given tlist has been
- * modified appropriately to remove group/aggregate expressions, but the
- * Query node still has the relevant fields set. We remove them in the
- * copies used for subplans.
- *
- * NOTE: this can be invoked recursively if more than one inheritance wildcard
- * is present. At each level of recursion, the first wildcard remaining in
- * the rangetable is expanded.
- *
- * NOTE: don't bother optimizing this routine for the case that the target
- * rel has no children. We won't get here unless find_inheritable_rt_entry
- * found at least two members in the inheritance group, so an APPEND is
- * certainly necessary.
- */
-Plan *
-plan_inherit_queries(Query *root, List *tlist,
- Index rt_index, List *inheritors)
-{
- RangeTblEntry *rt_entry = rt_fetch(rt_index, root->rtable);
- List *union_plans = NIL;
- List *union_rtentries = NIL;
- List *save_tlist = root->targetList;
- double tuple_fraction;
- 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). This is purely a space-saving hack. Note we restore
- * the root's tlist before exiting.
- */
- root->targetList = NIL;
-
- /*
- * If we are going to need sorting or grouping at the top level, force
- * lower-level planners to assume that all tuples will be retrieved.
- */
- if (root->distinctClause || root->sortClause ||
- root->groupClause || root->hasAggs)
- tuple_fraction = 0.0; /* will need all tuples from each subplan */
- else
- tuple_fraction = -1.0; /* default behavior is OK (I think) */
-
- foreach(i, inheritors)
- {
- Oid relid = lfirsti(i);
-
- /*
- * Make a modifiable copy of the original query, and replace the
- * target rangetable entry in it with a new one identifying this
- * child table. The new rtentry is marked inh = false --- this
- * is essential to prevent infinite recursion when the subquery
- * is rescanned by find_inheritable_rt_entry!
- */
- Query *new_root = copyObject(root);
- RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
- rt_entry);
-
- new_rt_entry->inh = false;
- rt_store(rt_index, new_root->rtable, new_rt_entry);
-
- /*
- * Insert (a modifiable copy of) the desired simplified tlist into
- * the subquery
- */
- new_root->targetList = copyObject(tlist);
-
- /*
- * Clear the sorting and grouping qualifications in the subquery,
- * so that sorting will only be done once after append
- */
- new_root->distinctClause = NIL;
- new_root->sortClause = NIL;
- new_root->groupClause = NIL;
- new_root->havingQual = NULL;
- new_root->limitOffset = NULL; /* LIMIT's probably unsafe too */
- new_root->limitCount = NULL;
- new_root->hasAggs = false; /* shouldn't be any left ... */
-
- /*
- * 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,
- new_root);
-
- /*
- * Plan the subquery by recursively calling union_planner().
- * Add plan and child rtentry to lists for APPEND.
- */
- union_plans = lappend(union_plans,
- union_planner(new_root, tuple_fraction));
- union_rtentries = lappend(union_rtentries, new_rt_entry);
- }
-
- /* Restore root's tlist */
- root->targetList = save_tlist;
-
- /* Construct the finished Append plan. */
- return (Plan *) make_append(union_plans,
- rt_index,
- union_rtentries,
- ((Plan *) lfirst(union_plans))->targetlist);
-}
-
-/*
* find_all_inheritors -
* Returns an integer list of relids including the given rel plus
* all relations that inherit from it, directly or indirectly.
@@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel)
}
/*
- * find_inheritable_rt_entry -
- * Given a rangetable, find the first rangetable entry that represents
- * an inheritance set.
- *
- * If successful, set *rt_index to the index (1..n) of the entry,
- * set *inheritors to a list of the relation OIDs of the set,
- * and return TRUE.
- *
- * If there is no entry that requires inheritance processing,
- * return FALSE.
+ * expand_inherted_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.
*
- * NOTE: We return the inheritors list so that plan_inherit_queries doesn't
- * have to compute it again.
+ * 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: We clear the inh flag in any entries that have it set but turn
- * out not to have any actual inheritance children. This is an efficiency
- * hack to avoid having to repeat the inheritance checks if the list is
- * scanned again (as will happen during expansion of any subsequent entry
- * that does have inheritance children). Although modifying the input
- * rangetable in-place may seem uncool, there's no reason not to do it,
- * since any re-examination of the entry would just come to the same
- * conclusion that the table has no children.
+ * NOTE: after this routine executes, the specified RTE will always have
+ * its inh flag cleared, whether or not there were any children. This
+ * ensures we won't expand the same RTE twice, which would otherwise occur
+ * for the case of an inherited UPDATE/DELETE target relation.
*/
-bool
-find_inheritable_rt_entry(List *rangetable,
- Index *rt_index,
- List **inheritors)
+List *
+expand_inherted_rtentry(Query *parse, Index rti)
{
- Index count = 0;
- List *temp;
-
- foreach(temp, rangetable)
+ RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
+ Oid parentOID = rte->relid;
+ List *inhOIDs;
+ List *inhRTIs;
+ List *l;
+
+ /* Does RT entry allow inheritance? */
+ if (! rte->inh)
+ return NIL;
+ Assert(parentOID != InvalidOid && rte->subquery == NULL);
+ /* Always clear the parent's inh flag, see above comments */
+ rte->inh = false;
+ /* Fast path for common case of childless table */
+ if (! has_subclass(parentOID))
+ return NIL;
+ /* Scan for all members of inheritance set */
+ inhOIDs = find_all_inheritors(parentOID);
+ /*
+ * Check that there's at least one descendant, else treat as
+ * no-child case. This could happen despite above has_subclass()
+ * check, if table once had a child but no longer does.
+ */
+ if (lnext(inhOIDs) == NIL)
+ return NIL;
+ /* OK, it's an inheritance set; expand it */
+ inhRTIs = makeListi1(rti);
+ foreach(l, inhOIDs)
{
- RangeTblEntry *rt_entry = (RangeTblEntry *) lfirst(temp);
- List *inhs;
+ Oid childOID = (Oid) lfirsti(l);
+ RangeTblEntry *childrte;
+ Index childRTindex;
- count++;
- /* Ignore non-inheritable RT entries */
- if (! rt_entry->inh)
- continue;
- /* Fast path for common case of childless table */
- if (! has_subclass(rt_entry->relid))
- {
- rt_entry->inh = false;
+ /* parent will be in the list too, so ignore it */
+ if (childOID == parentOID)
continue;
- }
- /* Scan for all members of inheritance set */
- inhs = find_all_inheritors(rt_entry->relid);
+
/*
- * Check that there's at least one descendant, else treat as
- * no-child case. This could happen despite above has_subclass()
- * check, if table once had a child but no longer does.
+ * 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
+ * real name and OID. Note that inh will be false at this point.
*/
- if (lnext(inhs) == NIL)
- {
- rt_entry->inh = false;
- continue;
- }
- /* OK, found our boy */
- *rt_index = count;
- *inheritors = inhs;
- return true;
- }
+ childrte = copyObject(rte);
+ childrte->relname = get_rel_name(childOID);
+ childrte->relid = childOID;
+ parse->rtable = lappend(parse->rtable, childrte);
+ childRTindex = length(parse->rtable);
- return false;
-}
-
-/*
- * new_rangetable_entry -
- * Replaces the name and relid of 'old_entry' with the values for
- * 'new_relid'.
- *
- * Returns a copy of 'old_entry' with the parameters substituted.
- */
-static RangeTblEntry *
-new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
-{
- RangeTblEntry *new_entry = copyObject(old_entry);
-
- /* Replace relation real name and OID, but not the reference name */
- new_entry->relname = get_rel_name(new_relid);
- new_entry->relid = new_relid;
- return new_entry;
+ inhRTIs = lappendi(inhRTIs, childRTindex);
+ }
+ return inhRTIs;
}
/*
- * fix_parsetree_attnums
- * Replaces attribute numbers from the relation represented by
- * 'old_relid' in 'parsetree' with the attribute numbers from
- * 'new_relid'.
+ * adjust_inherited_attrs
+ * Copy the specified query or expression and translate Vars referring
+ * to old_rt_index to refer to new_rt_index.
*
- * The parsetree is MODIFIED IN PLACE. This is OK only because
- * plan_inherit_queries made a copy of the tree for us to hack upon.
+ * 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
+ * helps ALTER TABLE ADD COLUMN. Unfortunately this isn't nearly enough to
+ * make it work transparently; there are other places where things fall down
+ * if children and parents don't have the same column numbers for inherited
+ * attributes. It'd be better to rip this code out and fix ALTER TABLE...
*/
-static void
-fix_parsetree_attnums(Index rt_index,
- Oid old_relid,
- Oid new_relid,
- Query *parsetree)
+Node *
+adjust_inherited_attrs(Node *node,
+ Index old_rt_index, Oid old_relid,
+ Index new_rt_index, Oid new_relid)
{
- fix_parsetree_attnums_context context;
+ adjust_inherited_attrs_context context;
- if (old_relid == new_relid)
- return; /* no work needed for parent rel itself */
+ /* Handle simple case simply... */
+ if (old_rt_index == new_rt_index)
+ {
+ Assert(old_relid == new_relid);
+ return copyObject(node);
+ }
- context.rt_index = rt_index;
+ context.old_rt_index = old_rt_index;
+ context.new_rt_index = new_rt_index;
context.old_relid = old_relid;
context.new_relid = new_relid;
- context.sublevels_up = 0;
- query_tree_walker(parsetree,
- fix_parsetree_attnums_walker,
- (void *) &context,
- true);
+ /*
+ * Must be prepared to start with a Query or a bare expression tree.
+ */
+ if (node && IsA(node, Query))
+ {
+ Query *query = (Query *) node;
+ Query *newnode;
+
+ FLATCOPY(newnode, query, Query);
+ if (newnode->resultRelation == old_rt_index)
+ newnode->resultRelation = new_rt_index;
+ query_tree_mutator(newnode, adjust_inherited_attrs_mutator,
+ (void *) &context, false);
+ return (Node *) newnode;
+ }
+ else
+ return adjust_inherited_attrs_mutator(node, &context);
}
-/*
- * Adjust varnos for child tables. This routine makes it possible for
- * child tables to have different column positions for the "same" attribute
- * as a parent, which helps ALTER TABLE ADD COLUMN. Unfortunately this isn't
- * nearly enough to make it work transparently; there are other places where
- * things fall down if children and parents don't have the same column numbers
- * for inherited attributes. It'd be better to rip this code out and fix
- * ALTER TABLE...
- */
-static bool
-fix_parsetree_attnums_walker(Node *node,
- fix_parsetree_attnums_context *context)
+static Node *
+adjust_inherited_attrs_mutator(Node *node,
+ adjust_inherited_attrs_context *context)
{
if (node == NULL)
- return false;
+ return NULL;
if (IsA(node, Var))
{
- Var *var = (Var *) node;
+ Var *var = (Var *) copyObject(node);
- if (var->varlevelsup == context->sublevels_up &&
- var->varno == context->rt_index &&
- var->varattno > 0)
+ if (var->varlevelsup == 0 &&
+ var->varno == context->old_rt_index)
{
- var->varattno = get_attnum(context->new_relid,
- get_attname(context->old_relid,
- var->varattno));
+ var->varno = context->new_rt_index;
+ if (var->varattno > 0)
+ var->varattno = get_attnum(context->new_relid,
+ get_attname(context->old_relid,
+ var->varattno));
}
- return false;
+ return (Node *) var;
}
- if (IsA(node, Query))
+ if (IsA(node, RangeTblRef))
{
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- result = query_tree_walker((Query *) node,
- fix_parsetree_attnums_walker,
- (void *) context,
- true);
- context->sublevels_up--;
- return result;
- }
- return expression_tree_walker(node, fix_parsetree_attnums_walker,
- (void *) context);
-}
+ RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
-static Append *
-make_append(List *appendplans,
- Index rt_index,
- List *inheritrtable,
- List *tlist)
-{
- Append *node = makeNode(Append);
- List *subnode;
-
- node->appendplans = appendplans;
- node->inheritrelid = rt_index;
- node->inheritrtable = inheritrtable;
- node->plan.startup_cost = 0;
- node->plan.total_cost = 0;
- node->plan.plan_rows = 0;
- node->plan.plan_width = 0;
- foreach(subnode, appendplans)
- {
- Plan *subplan = (Plan *) lfirst(subnode);
-
- if (subnode == appendplans) /* first node? */
- node->plan.startup_cost = subplan->startup_cost;
- node->plan.total_cost += subplan->total_cost;
- node->plan.plan_rows += subplan->plan_rows;
- if (node->plan.plan_width < subplan->plan_width)
- node->plan.plan_width = subplan->plan_width;
+ if (rtr->rtindex == context->old_rt_index)
+ rtr->rtindex = context->new_rt_index;
+ return (Node *) rtr;
}
- node->plan.state = (EState *) NULL;
- node->plan.targetlist = tlist;
- node->plan.qual = NIL;
- node->plan.lefttree = (Plan *) NULL;
- node->plan.righttree = (Plan *) NULL;
+ /*
+ * We have to process RestrictInfo nodes specially: we do NOT want to
+ * copy the original subclauseindices list, since the new rel may have
+ * different indices. The list will be rebuilt during planning anyway.
+ */
+ if (IsA(node, RestrictInfo))
+ {
+ RestrictInfo *oldinfo = (RestrictInfo *) node;
+ RestrictInfo *newinfo = makeNode(RestrictInfo);
+
+ /* Copy all flat-copiable fields */
+ memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
+
+ newinfo->clause = (Expr *)
+ adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
- return node;
+ newinfo->subclauseindices = NIL;
+
+ return (Node *) newinfo;
+ }
+ /*
+ * NOTE: we do not need to recurse into sublinks, because they should
+ * already have been converted to subplans before we see them.
+ */
+ return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
+ (void *) context);
}