diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7a16cbcff56..19b987908bb 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.324 2005/12/28 01:29:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.325 2006/01/31 21:39:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1322,6 +1322,25 @@ _copyInClauseInfo(InClauseInfo *from) return newnode; } +/* + * _copyAppendRelInfo + */ +static AppendRelInfo * +_copyAppendRelInfo(AppendRelInfo *from) +{ + AppendRelInfo *newnode = makeNode(AppendRelInfo); + + COPY_SCALAR_FIELD(parent_relid); + COPY_SCALAR_FIELD(child_relid); + COPY_SCALAR_FIELD(parent_reltype); + COPY_SCALAR_FIELD(child_reltype); + COPY_NODE_FIELD(col_mappings); + COPY_NODE_FIELD(translated_vars); + COPY_SCALAR_FIELD(parent_reloid); + + return newnode; +} + /* **************************************************************** * parsenodes.h copy functions * **************************************************************** @@ -2945,6 +2964,9 @@ copyObject(void *from) case T_InClauseInfo: retval = _copyInClauseInfo(from); break; + case T_AppendRelInfo: + retval = _copyAppendRelInfo(from); + break; /* * VALUE NODES diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b006cec150d..e7a9ced0ed3 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.260 2005/12/28 01:29:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.261 2006/01/31 21:39:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -644,6 +644,20 @@ _equalInClauseInfo(InClauseInfo *a, InClauseInfo *b) return true; } +static bool +_equalAppendRelInfo(AppendRelInfo *a, AppendRelInfo *b) +{ + COMPARE_SCALAR_FIELD(parent_relid); + COMPARE_SCALAR_FIELD(child_relid); + COMPARE_SCALAR_FIELD(parent_reltype); + COMPARE_SCALAR_FIELD(child_reltype); + COMPARE_NODE_FIELD(col_mappings); + COMPARE_NODE_FIELD(translated_vars); + COMPARE_SCALAR_FIELD(parent_reloid); + + return true; +} + /* * Stuff from parsenodes.h @@ -1984,6 +1998,9 @@ equal(void *a, void *b) case T_InClauseInfo: retval = _equalInClauseInfo(a, b); break; + case T_AppendRelInfo: + retval = _equalAppendRelInfo(a, b); + break; case T_List: case T_IntList: case T_OidList: diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b60eab31fd5..d6d63ee096f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.266 2005/12/28 01:29:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.267 2006/01/31 21:39:23 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1178,6 +1178,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node) WRITE_NODE_FIELD(full_join_clauses); WRITE_NODE_FIELD(oj_info_list); WRITE_NODE_FIELD(in_info_list); + WRITE_NODE_FIELD(append_rel_list); WRITE_NODE_FIELD(query_pathkeys); WRITE_NODE_FIELD(group_pathkeys); WRITE_NODE_FIELD(sort_pathkeys); @@ -1204,8 +1205,8 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) WRITE_NODE_FIELD(cheapest_unique_path); WRITE_UINT_FIELD(relid); WRITE_ENUM_FIELD(rtekind, RTEKind); - WRITE_UINT_FIELD(min_attr); - WRITE_UINT_FIELD(max_attr); + WRITE_INT_FIELD(min_attr); + WRITE_INT_FIELD(max_attr); WRITE_NODE_FIELD(indexlist); WRITE_UINT_FIELD(pages); WRITE_FLOAT_FIELD(tuples, "%.0f"); @@ -1295,6 +1296,20 @@ _outInClauseInfo(StringInfo str, InClauseInfo *node) WRITE_NODE_FIELD(sub_targetlist); } +static void +_outAppendRelInfo(StringInfo str, AppendRelInfo *node) +{ + WRITE_NODE_TYPE("APPENDRELINFO"); + + WRITE_UINT_FIELD(parent_relid); + WRITE_UINT_FIELD(child_relid); + WRITE_OID_FIELD(parent_reltype); + WRITE_OID_FIELD(child_reltype); + WRITE_NODE_FIELD(col_mappings); + WRITE_NODE_FIELD(translated_vars); + WRITE_OID_FIELD(parent_reloid); +} + /***************************************************************************** * * Stuff from parsenodes.h. @@ -2048,6 +2063,9 @@ _outNode(StringInfo str, void *obj) case T_InClauseInfo: _outInClauseInfo(str, obj); break; + case T_AppendRelInfo: + _outAppendRelInfo(str, obj); + break; case T_CreateStmt: _outCreateStmt(str, obj); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 19b1cfcaad4..90add90e4e9 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.139 2005/12/20 02:30:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.140 2006/01/31 21:39:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,9 +44,8 @@ int geqo_threshold; static void set_base_rel_pathlists(PlannerInfo *root); static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); -static void set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte, - List *inheritlist); +static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, + Index rti, RangeTblEntry *rte); static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -96,9 +95,9 @@ make_one_rel(PlannerInfo *root, List *joinlist) int num_base_rels = 0; Index rti; - for (rti = 1; rti < root->base_rel_array_size; rti++) + for (rti = 1; rti < root->simple_rel_array_size; rti++) { - RelOptInfo *brel = root->base_rel_array[rti]; + RelOptInfo *brel = root->simple_rel_array[rti]; if (brel == NULL) continue; @@ -131,16 +130,10 @@ set_base_rel_pathlists(PlannerInfo *root) { Index rti; - /* - * Note: because we call expand_inherited_rtentry inside the loop, it's - * quite possible for the base_rel_array to be enlarged while the loop - * runs. Hence don't try to optimize the loop. - */ - for (rti = 1; rti < root->base_rel_array_size; rti++) + for (rti = 1; rti < root->simple_rel_array_size; rti++) { - RelOptInfo *rel = root->base_rel_array[rti]; + RelOptInfo *rel = root->simple_rel_array[rti]; RangeTblEntry *rte; - List *inheritlist; /* there may be empty slots corresponding to non-baserel RTEs */ if (rel == NULL) @@ -154,7 +147,12 @@ set_base_rel_pathlists(PlannerInfo *root) rte = rt_fetch(rti, root->parse->rtable); - if (rel->rtekind == RTE_SUBQUERY) + if (rte->inh) + { + /* It's an "append relation", process accordingly */ + set_append_rel_pathlist(root, rel, rti, rte); + } + else if (rel->rtekind == RTE_SUBQUERY) { /* Subquery --- generate a separate plan for it */ set_subquery_pathlist(root, rel, rti, rte); @@ -164,11 +162,6 @@ set_base_rel_pathlists(PlannerInfo *root) /* RangeFunction --- generate a separate plan for it */ set_function_pathlist(root, rel, rte); } - else if ((inheritlist = expand_inherited_rtentry(root, rti)) != NIL) - { - /* Relation is root of an inheritance tree, process specially */ - set_inherited_rel_pathlist(root, rel, rti, rte, inheritlist); - } else { /* Plain relation */ @@ -188,6 +181,9 @@ set_base_rel_pathlists(PlannerInfo *root) static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { + Assert(rel->rtekind == RTE_RELATION); + Assert(!rte->inh); + /* Mark rel with estimated output rows, width, etc */ set_baserel_size_estimates(root, rel); @@ -224,37 +220,29 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) } /* - * set_inherited_rel_pathlist - * Build access paths for a inheritance tree rooted at rel + * set_append_rel_pathlist + * Build access paths for an "append relation" * - * 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. - * - * NOTE: the passed-in rel and RTE will henceforth represent the appended - * result of the whole inheritance tree. The members of inheritlist represent - * the individual tables --- in particular, the inheritlist member that is a - * duplicate of the parent RTE represents the parent table alone. - * We will generate plans to scan the individual tables that refer to - * the inheritlist RTEs, whereas Vars elsewhere in the plan tree that - * refer to the original RTE are taken to refer to the append output. - * In particular, this means we have separate RelOptInfos for the parent - * table and for the append output, which is a good thing because they're - * not the same size. + * The passed-in rel and RTE represent the entire append relation. The + * relation's contents are computed by appending together the output of + * the individual member relations. Note that in the inheritance case, + * the first member relation is actually the same table as is mentioned in + * the parent RTE ... but it has a different RTE and RelOptInfo. This is + * a good thing because their outputs are not the same size. */ static void -set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte, - List *inheritlist) +set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, + Index rti, RangeTblEntry *rte) { int parentRTindex = rti; - Oid parentOID = rte->relid; List *subpaths = NIL; - ListCell *il; + ListCell *l; /* * XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can - * we do better? + * we do better? (This will take some redesign because the executor + * currently supposes that every rowMark relation is involved in every + * row returned by the query.) */ if (list_member_int(root->parse->rowMarks, parentRTindex)) ereport(ERROR, @@ -262,64 +250,79 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries"))); /* - * Initialize to compute size estimates for whole inheritance tree + * Initialize to compute size estimates for whole append relation */ rel->rows = 0; rel->width = 0; /* - * Generate access paths for each table in the tree (parent AND children), - * and pick the cheapest path for each table. + * Generate access paths for each member relation, and pick the cheapest + * path for each one. */ - foreach(il, inheritlist) + foreach(l, root->append_rel_list) { - int childRTindex = lfirst_int(il); - RangeTblEntry *childrte; - Oid childOID; + AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); + int childRTindex; RelOptInfo *childrel; + RangeTblEntry *childrte; ListCell *parentvars; ListCell *childvars; - childrte = rt_fetch(childRTindex, root->parse->rtable); - childOID = childrte->relid; + /* append_rel_list contains all append rels; ignore others */ + if (appinfo->parent_relid != parentRTindex) + continue; + + childRTindex = appinfo->child_relid; /* * Make a RelOptInfo for the child so we can do planning. Mark it as * an "other rel" since it will not be part of the main join tree. */ - childrel = build_other_rel(root, childRTindex); + childrel = build_simple_rel(root, childRTindex, + RELOPT_OTHER_MEMBER_REL); /* - * Copy the parent's targetlist and restriction quals to the child, - * with attribute-number adjustment as needed. We don't bother to - * copy the join quals, since we can't do any joining of the - * individual tables. Also, we just zap attr_needed rather than - * trying to adjust it; it won't be looked at in the child. + * Copy the parent's targetlist and quals to the child, with + * appropriate substitution of variables. */ childrel->reltargetlist = (List *) - adjust_inherited_attrs((Node *) rel->reltargetlist, - parentRTindex, - parentOID, - childRTindex, - childOID); - childrel->attr_needed = NULL; + adjust_appendrel_attrs((Node *) rel->reltargetlist, + appinfo); childrel->baserestrictinfo = (List *) - adjust_inherited_attrs((Node *) rel->baserestrictinfo, - parentRTindex, - parentOID, - childRTindex, - childOID); + adjust_appendrel_attrs((Node *) rel->baserestrictinfo, + appinfo); + childrel->joininfo = (List *) + adjust_appendrel_attrs((Node *) rel->joininfo, + appinfo); + + /* + * Copy the parent's attr_needed data as well, with appropriate + * adjustment of relids and attribute numbers. + */ + pfree(childrel->attr_needed); + childrel->attr_needed = + adjust_appendrel_attr_needed(rel, appinfo, + childrel->min_attr, + childrel->max_attr); /* * If we can prove we don't need to scan this child via constraint * exclusion, just ignore it. (We have to have converted the * baserestrictinfo Vars before we can make the test.) + * + * XXX it'd probably be better to give the child some kind of dummy + * cheapest path, or otherwise explicitly mark it as ignorable. + * Currently there is an ugly check in join_before_append() to handle + * excluded children. */ - if (constraint_exclusion) + childrte = rt_fetch(childRTindex, root->parse->rtable); + if (constraint_exclusion && + childrte->rtekind == RTE_RELATION) { List *constraint_pred; - constraint_pred = get_relation_constraints(childOID, childrel); + constraint_pred = get_relation_constraints(childrte->relid, + childrel); /* * We do not currently enforce that CHECK constraints contain only @@ -363,7 +366,8 @@ set_inherited_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Var *parentvar = (Var *) lfirst(parentvars); Var *childvar = (Var *) lfirst(childvars); - if (IsA(parentvar, Var) &&IsA(childvar, Var)) + if (IsA(parentvar, Var) && + IsA(childvar, Var)) { int pndx = parentvar->varattno - rel->min_attr; int cndx = childvar->varattno - childrel->min_attr; @@ -392,9 +396,9 @@ has_multiple_baserels(PlannerInfo *root) int num_base_rels = 0; Index rti; - for (rti = 1; rti < root->base_rel_array_size; rti++) + for (rti = 1; rti < root->simple_rel_array_size; rti++) { - RelOptInfo *brel = root->base_rel_array[rti]; + RelOptInfo *brel = root->simple_rel_array[rti]; if (brel == NULL) continue; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index c1c150de53d..137be08e753 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.98 2005/11/22 18:17:12 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.99 2006/01/31 21:39:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/prep.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" @@ -35,6 +36,9 @@ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype); +static void join_before_append(PlannerInfo *root, RelOptInfo *joinrel, + RelOptInfo *outerrel, RelOptInfo *innerrel, + JoinType jointype); static List *select_mergejoin_clauses(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, @@ -115,6 +119,13 @@ add_paths_to_joinrel(PlannerInfo *root, if (enable_hashjoin) hash_inner_and_outer(root, joinrel, outerrel, innerrel, restrictlist, jointype); + + /* + * 5. If the inner relation is an append relation, consider joining + * the outer rel to each append member and then appending the results. + */ + if (innerrel->cheapest_total_path->pathtype == T_Append) + join_before_append(root, joinrel, outerrel, innerrel, jointype); } /* @@ -777,6 +788,154 @@ hash_inner_and_outer(PlannerInfo *root, } } +/* + * join_before_append + * Creates possible join paths for processing a single join relation + * 'joinrel' when the inner input is an append relation. + * + * The idea here is to swap the order of the APPEND and JOIN operators. + * This is only really helpful if it allows us to reduce the cost of + * scanning the members of the append relation, and so we only consider + * plans involving nestloops with inner indexscans. Also, since the APPEND + * will certainly yield an unsorted result, there's no point in considering + * any but the cheapest-total outer path. + * + * XXX this is a bit of a kluge, because the resulting plan has to evaluate + * the outer relation multiple times. Would be better to allow + * best_inner_indexscan to generate an AppendPath and not have this routine + * at all. But we can't do that without some executor changes (need a way + * to pass outer keys down through Append). FIXME later. + * + * 'joinrel' is the join relation + * 'outerrel' is the outer join relation + * 'innerrel' is the inner join relation + * 'jointype' is the type of join to do + */ +static void +join_before_append(PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype) +{ + Path *outer_cheapest_total = outerrel->cheapest_total_path; + int parentRTindex = innerrel->relid; + List *append_paths = NIL; + ListCell *l; + + /* + * Swapping JOIN with APPEND only works for inner joins, not outer joins. + * However, we can also handle a unique-ified outer path. + */ + switch (jointype) + { + case JOIN_INNER: + break; + case JOIN_UNIQUE_OUTER: + outer_cheapest_total = (Path *) + create_unique_path(root, outerrel, outer_cheapest_total); + break; + case JOIN_LEFT: + case JOIN_RIGHT: + case JOIN_FULL: + case JOIN_IN: + case JOIN_UNIQUE_INNER: + return; /* can't join this way */ + default: + elog(ERROR, "unrecognized join type: %d", + (int) jointype); + break; + } + + /* + * Generate suitable access paths for each member relation. + */ + foreach(l, root->append_rel_list) + { + AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); + int childRTindex; + RelOptInfo *childrel; + Path *bestinnerjoin; + Relids joinrelids; + Relids *save_attr_needed; + RelOptInfo *this_joinrel; + List *this_restrictlist; + + /* append_rel_list contains all append rels; ignore others */ + if (appinfo->parent_relid != parentRTindex) + continue; + + childRTindex = appinfo->child_relid; + childrel = find_base_rel(root, childRTindex); + Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + + /* + * If the child has no cheapest_total_path, assume it was deemed + * excludable by constraint exclusion (see set_append_rel_pathlist). + */ + if (childrel->cheapest_total_path == NULL) + { + Assert(constraint_exclusion); + continue; + } + + /* + * Get the best innerjoin indexpath (if any) for this outer rel. + */ + bestinnerjoin = best_inner_indexscan(root, childrel, + outerrel->relids, JOIN_INNER); + /* + * If no luck on an indexpath for this rel, we'll still consider + * an Append substituting the cheapest-total inner path. This + * is only likely to win if there's at least one member rel for + * which an indexscan path does exist. + */ + if (!bestinnerjoin) + bestinnerjoin = childrel->cheapest_total_path; + + /* + * We need a joinrel that describes this join accurately. Although + * the joinrel won't ever be used by the join path search algorithm + * in joinrels.c, it provides necessary context for the Path, + * such as properly-translated target and quals lists. + */ + joinrelids = bms_copy(joinrel->relids); + joinrelids = bms_del_member(joinrelids, parentRTindex); + joinrelids = bms_add_member(joinrelids, childRTindex); + + /* + * Kluge: temporarily adjust the outer rel's attr_needed info so + * that it references the member rel instead of the appendrel. + * This is needed to build the correct targetlist for the joinrel. + */ + save_attr_needed = outerrel->attr_needed; + outerrel->attr_needed = + adjust_other_rel_attr_needed(outerrel, appinfo, + outerrel->min_attr, + outerrel->max_attr); + + this_joinrel = build_join_rel(root, joinrelids, outerrel, childrel, + jointype, &this_restrictlist); + + /* Now we can undo the hack on attr_needed */ + pfree(outerrel->attr_needed); + outerrel->attr_needed = save_attr_needed; + + /* Build Path for join and add to result list */ + append_paths = lappend(append_paths, + create_nestloop_path(root, + this_joinrel, + JOIN_INNER, + outer_cheapest_total, + bestinnerjoin, + this_restrictlist, + NIL)); + } + + /* Form the completed Append path and add it to the join relation. */ + add_path(joinrel, (Path *) create_append_path(joinrel, append_paths)); +} + /* * select_mergejoin_clauses * Select mergejoin clauses that are usable for a particular join. diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 4b132d65611..442e52b52d7 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.113 2005/12/20 02:30:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.114 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -74,8 +74,9 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); * * At the end of this process, there should be one baserel RelOptInfo for * every non-join RTE that is used in the query. Therefore, this routine - * is the only place that should call build_base_rel. But build_other_rel - * will be used later to build rels for inheritance children. + * is the only place that should call build_simple_rel with reloptkind + * RELOPT_BASEREL. However, otherrels will be built later for append relation + * members. */ void add_base_rels_to_query(PlannerInfo *root, Node *jtnode) @@ -86,7 +87,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) { int varno = ((RangeTblRef *) jtnode)->rtindex; - build_base_rel(root, varno); + (void) build_simple_rel(root, varno, RELOPT_BASEREL); } else if (IsA(jtnode, FromExpr)) { diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 3729fd2b199..0783f441b5b 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.91 2005/12/20 02:30:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.92 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -120,15 +120,15 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction, &constant_quals); /* - * Init planner lists to empty. We create the base_rel_array with a size - * that will be sufficient if no pullups or inheritance additions happen - * ... otherwise it will be enlarged as needed. + * Init planner lists to empty, and set up the array to hold RelOptInfos + * for "simple" rels. * - * NOTE: in_info_list was set up by subquery_planner, do not touch here + * NOTE: in_info_list and append_rel_list were set up by subquery_planner, + * do not touch here */ - root->base_rel_array_size = list_length(parse->rtable) + 1; - root->base_rel_array = (RelOptInfo **) - palloc0(root->base_rel_array_size * sizeof(RelOptInfo *)); + root->simple_rel_array_size = list_length(parse->rtable) + 1; + root->simple_rel_array = (RelOptInfo **) + palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *)); root->join_rel_list = NIL; root->join_rel_hash = NULL; root->equi_key_list = NIL; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 0dd9e1e8d2a..e007e4e594e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.196 2005/12/20 02:30:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.197 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,16 +47,17 @@ ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */ /* Expression kind codes for preprocess_expression */ -#define EXPRKIND_QUAL 0 -#define EXPRKIND_TARGET 1 -#define EXPRKIND_RTFUNC 2 -#define EXPRKIND_LIMIT 3 -#define EXPRKIND_ININFO 4 +#define EXPRKIND_QUAL 0 +#define EXPRKIND_TARGET 1 +#define EXPRKIND_RTFUNC 2 +#define EXPRKIND_LIMIT 3 +#define EXPRKIND_ININFO 4 +#define EXPRKIND_APPINFO 5 static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode); -static Plan *inheritance_planner(PlannerInfo *root, List *inheritlist); +static Plan *inheritance_planner(PlannerInfo *root); static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction); static double preprocess_limit(PlannerInfo *root, double tuple_fraction, @@ -194,7 +195,6 @@ subquery_planner(Query *parse, double tuple_fraction, PlannerInfo *root; Plan *plan; List *newHaving; - List *lst; ListCell *l; /* Set up for a new level of subquery */ @@ -204,6 +204,8 @@ subquery_planner(Query *parse, double tuple_fraction, /* Create a PlannerInfo data structure for this subquery */ root = makeNode(PlannerInfo); root->parse = parse; + root->in_info_list = NIL; + root->append_rel_list = NIL; /* * Look for IN clauses at the top level of WHERE, and transform them into @@ -211,7 +213,6 @@ subquery_planner(Query *parse, double tuple_fraction, * level of WHERE; if we pull up any subqueries in the next step, their * INs are processed just before pulling them up. */ - root->in_info_list = NIL; if (parse->hasSubLinks) parse->jointree->quals = pull_up_IN_clauses(root, parse->jointree->quals); @@ -252,6 +253,16 @@ subquery_planner(Query *parse, double tuple_fraction, } } + /* + * Expand any rangetable entries that are inheritance sets into "append + * relations". This can add entries to the rangetable, but they must be + * plain base relations not joins, so it's OK (and marginally more + * efficient) to do it after checking for join RTEs. We must do it after + * pulling up subqueries, else we'd fail to handle inherited tables in + * subqueries. + */ + expand_inherited_tables(root); + /* * Set hasHavingQual to remember if HAVING clause is present. Needed * because preprocess_expression will reduce a constant-true condition to @@ -279,6 +290,9 @@ subquery_planner(Query *parse, double tuple_fraction, root->in_info_list = (List *) preprocess_expression(root, (Node *) root->in_info_list, EXPRKIND_ININFO); + root->append_rel_list = (List *) + preprocess_expression(root, (Node *) root->append_rel_list, + EXPRKIND_APPINFO); /* Also need to preprocess expressions for function RTEs */ foreach(l, parse->rtable) @@ -357,8 +371,8 @@ subquery_planner(Query *parse, double tuple_fraction, * needs special processing, else go straight to grouping_planner. */ if (parse->resultRelation && - (lst = expand_inherited_rtentry(root, parse->resultRelation)) != NIL) - plan = inheritance_planner(root, lst); + rt_fetch(parse->resultRelation, parse->rtable)->inh) + plan = inheritance_planner(root); else plan = grouping_planner(root, tuple_fraction); @@ -504,44 +518,50 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode) (int) nodeTag(jtnode)); } -/*-------------------- +/* * inheritance_planner * Generate a plan in the case where the result relation is an * inheritance set. * - * We have to handle this case differently from cases where a source - * relation is an inheritance set. Source inheritance is expanded at - * the bottom of the plan tree (see allpaths.c), but target inheritance - * has to be expanded at the top. The reason is that for UPDATE, each - * target relation needs a different targetlist matching its own column - * set. (This is not so critical for DELETE, but for simplicity we treat - * inherited DELETE the same way.) Fortunately, the UPDATE/DELETE target - * can never be the nullable side of an outer join, so it's OK to generate - * the plan this way. - * - * inheritlist is an integer list of RT indexes for the result relation set. + * We have to handle this case differently from cases where a source relation + * is an inheritance set. Source inheritance is expanded at the bottom of the + * plan tree (see allpaths.c), but target inheritance has to be expanded at + * the top. The reason is that for UPDATE, each target relation needs a + * different targetlist matching its own column set. Also, for both UPDATE + * and DELETE, the executor needs the Append plan node at the top, else it + * can't keep track of which table is the current target table. Fortunately, + * the UPDATE/DELETE target can never be the nullable side of an outer join, + * so it's OK to generate the plan this way. * * Returns a query plan. - *-------------------- */ static Plan * -inheritance_planner(PlannerInfo *root, List *inheritlist) +inheritance_planner(PlannerInfo *root) { Query *parse = root->parse; int parentRTindex = parse->resultRelation; - Oid parentOID = getrelid(parentRTindex, parse->rtable); - int mainrtlength = list_length(parse->rtable); List *subplans = NIL; List *tlist = NIL; + PlannerInfo subroot; ListCell *l; - foreach(l, inheritlist) + subroot.parse = NULL; /* catch it if no matches in loop */ + + parse->resultRelations = NIL; + + foreach(l, root->append_rel_list) { - int childRTindex = lfirst_int(l); - Oid childOID = getrelid(childRTindex, parse->rtable); - PlannerInfo subroot; + AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); Plan *subplan; + /* append_rel_list contains all append rels; ignore others */ + if (appinfo->parent_relid != parentRTindex) + continue; + + /* Build target-relations list for the executor */ + parse->resultRelations = lappend_int(parse->resultRelations, + appinfo->child_relid); + /* * Generate modified query with this rel as target. We have to be * prepared to translate varnos in in_info_list as well as in the @@ -549,14 +569,12 @@ inheritance_planner(PlannerInfo *root, List *inheritlist) */ memcpy(&subroot, root, sizeof(PlannerInfo)); subroot.parse = (Query *) - adjust_inherited_attrs((Node *) parse, - parentRTindex, parentOID, - childRTindex, childOID); + adjust_appendrel_attrs((Node *) parse, + appinfo); subroot.in_info_list = (List *) - adjust_inherited_attrs((Node *) root->in_info_list, - parentRTindex, parentOID, - childRTindex, childOID); - /* There shouldn't be any OJ info to translate, though */ + adjust_appendrel_attrs((Node *) root->in_info_list, + appinfo); + /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot.oj_info_list == NIL); /* Generate plan */ @@ -564,48 +582,23 @@ inheritance_planner(PlannerInfo *root, List *inheritlist) subplans = lappend(subplans, subplan); - /* - * XXX my goodness this next bit is ugly. Really need to think about - * ways to rein in planner's habit of scribbling on its input. - * - * Planning of the subquery might have modified the rangetable, either - * by addition of RTEs due to expansion of inherited source tables, or - * by changes of the Query structures inside subquery RTEs. We have - * to ensure that this gets propagated back to the master copy. - * However, if we aren't done planning yet, we also need to ensure - * that subsequent calls to grouping_planner have virgin sub-Queries - * to work from. So, if we are at the last list entry, just copy the - * subquery rangetable back to the master copy; if we are not, then - * extend the master copy by adding whatever the subquery added. (We - * assume these added entries will go untouched by the future - * grouping_planner calls. We are also effectively assuming that - * sub-Queries will get planned identically each time, or at least - * that the impacts on their rangetables will be the same each time. - * Did I say this is ugly?) - */ - if (lnext(l) == NULL) - parse->rtable = subroot.parse->rtable; - else - { - int subrtlength = list_length(subroot.parse->rtable); - - if (subrtlength > mainrtlength) - { - List *subrt; - - subrt = list_copy_tail(subroot.parse->rtable, mainrtlength); - parse->rtable = list_concat(parse->rtable, subrt); - mainrtlength = subrtlength; - } - } - /* Save preprocessed tlist from first rel for use in Append */ if (tlist == NIL) tlist = subplan->targetlist; } - /* Save the target-relations list for the executor, too */ - parse->resultRelations = inheritlist; + /* + * Planning might have modified the rangetable, due to changes of the + * Query structures inside subquery RTEs. We have to ensure that this + * gets propagated back to the master copy. But can't do this until we + * are done planning, because all the calls to grouping_planner need + * virgin sub-Queries to work from. (We are effectively assuming that + * sub-Queries will get planned identically each time, or at least that + * the impacts on their rangetables will be the same each time.) + * + * XXX should clean this up someday + */ + parse->rtable = subroot.parse->rtable; /* Mark result as unordered (probably unnecessary) */ root->query_pathkeys = NIL; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index a25787685b1..546c4bd275d 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.33 2005/12/20 02:30:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.34 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -176,12 +176,13 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) */ subroot = makeNode(PlannerInfo); subroot->parse = subquery; + subroot->in_info_list = NIL; + subroot->append_rel_list = NIL; /* * Pull up any IN clauses within the subquery's WHERE, so that we * don't leave unoptimized INs behind. */ - subroot->in_info_list = NIL; if (subquery->hasSubLinks) subquery->jointree->quals = pull_up_IN_clauses(subroot, subquery->jointree->quals); @@ -228,11 +229,12 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) /* * Adjust level-0 varnos in subquery so that we can append its * rangetable to upper query's. We have to fix the subquery's - * in_info_list, as well. + * in_info_list and append_rel_list, as well. */ rtoffset = list_length(parse->rtable); OffsetVarNodes((Node *) subquery, rtoffset, 0); OffsetVarNodes((Node *) subroot->in_info_list, rtoffset, 0); + OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0); /* * Upper-level vars in subquery are now one level closer to their @@ -240,6 +242,7 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) */ IncrementVarSublevelsUp((Node *) subquery, -1, 1); IncrementVarSublevelsUp((Node *) subroot->in_info_list, -1, 1); + IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1); /* * Replace all of the top query's references to the subquery's @@ -263,6 +266,10 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) ResolveNew((Node *) root->in_info_list, varno, 0, rte, subtlist, CMD_SELECT, 0); + root->append_rel_list = (List *) + ResolveNew((Node *) root->append_rel_list, + varno, 0, rte, + subtlist, CMD_SELECT, 0); foreach(rt, parse->rtable) { @@ -327,6 +334,15 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, bool below_outer_join) 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. diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 227eab83cef..a33213ef59d 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); /* @@ -739,51 +736,81 @@ find_all_inheritors(Oid parentrel) 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); @@ -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; - - return inhRTIs; } /* - * adjust_inherited_attrs - * Copy the specified query or expression and translate Vars referring - * to old_rt_index to refer to new_rt_index. + * 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. * - * 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. + * For paranoia's sake, we match type as well as attribute name. */ -Node * -adjust_inherited_attrs(Node *node, - Index old_rt_index, Oid old_relid, - Index new_rt_index, Oid new_relid) +static void +make_translation_lists(Relation oldrelation, Relation newrelation, + Index newvarno, + List **col_mappings, List **translated_vars) { - Node *result; - adjust_inherited_attrs_context context; - Relation oldrelation; - Relation newrelation; + 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; - /* Handle simple case simply... */ - if (old_rt_index == new_rt_index) + for (old_attno = 0; old_attno < oldnatts; old_attno++) { - Assert(old_relid == new_relid); - return copyObject(node); + 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; + + /* + * 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)); } - /* - * 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); + *col_mappings = numbers; + *translated_vars = vars; +} - 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); +/* + * adjust_appendrel_attrs + * Copy the specified query or expression and translate Vars referring + * 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. + * + * 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_appendrel_attrs(Node *node, AppendRelInfo *appinfo) +{ + Node *result; /* * 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); + Assert(var->vartype == context->parent_reltype); + if (context->parent_reltype != 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; + 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); } @@ -1158,6 +1272,110 @@ adjust_relid_set(Relids relids, Index oldrelid, Index newrelid) 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 * @@ -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); + /* 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 != attrno) + if (tle->resno != newattno) { - tle->resno = attrno; + tle->resno = newattno; changed_it = true; } } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 5266ff85d82..c4639cbf0d1 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.206 2006/01/25 20:29:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.207 2006/01/31 21:39:24 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -3209,6 +3209,15 @@ expression_tree_walker(Node *node, return true; } break; + case T_AppendRelInfo: + { + AppendRelInfo *appinfo = (AppendRelInfo *) node; + + if (expression_tree_walker((Node *) appinfo->translated_vars, + walker, context)) + return true; + } + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); @@ -3744,6 +3753,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_AppendRelInfo: + { + AppendRelInfo *appinfo = (AppendRelInfo *) node; + AppendRelInfo *newnode; + + FLATCOPY(newnode, appinfo, AppendRelInfo); + MUTATE(newnode->translated_vars, appinfo->translated_vars, List *); + return (Node *) newnode; + } + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index d9a9fdb9cfa..c926ae5d488 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.116 2006/01/05 10:07:45 petere Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.117 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -70,24 +70,11 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) List *indexinfos = NIL; /* - * Normally, we can assume the rewriter already acquired at least - * AccessShareLock on each relation used in the query. However this will - * not be the case for relations added to the query because they are - * inheritance children of some relation mentioned explicitly. For them, - * this is the first access during the parse/rewrite/plan pipeline, and so - * we need to obtain and keep a suitable lock. - * - * XXX really, a suitable lock is RowShareLock if the relation is an - * UPDATE/DELETE target, and AccessShareLock otherwise. However we cannot - * easily tell here which to get, so for the moment just get - * AccessShareLock always. The executor will get the right lock when it - * runs, which means there is a very small chance of deadlock trying to - * upgrade our lock. + * We need not lock the relation since it was already locked, either + * by the rewriter or when expand_inherited_rtentry() added it to the + * query's rangetable. */ - if (rel->reloptkind == RELOPT_BASEREL) - relation = heap_open(relationObjectId, NoLock); - else - relation = heap_open(relationObjectId, AccessShareLock); + relation = heap_open(relationObjectId, NoLock); rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1; rel->max_attr = RelationGetNumberOfAttributes(relation); @@ -224,7 +211,6 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) rel->indexlist = indexinfos; - /* close rel, but keep lock if any */ heap_close(relation, NoLock); } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index cef0c63a66f..cedb8082271 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.74 2005/12/20 02:30:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.75 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,8 +30,6 @@ typedef struct JoinHashEntry RelOptInfo *join_rel; } JoinHashEntry; -static RelOptInfo *make_reloptinfo(PlannerInfo *root, int relid, - RelOptKind reloptkind); static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *input_rel); static List *build_joinrel_restrictlist(PlannerInfo *root, @@ -49,71 +47,25 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel, /* - * build_base_rel - * Construct a new base relation RelOptInfo, and put it in the query's - * base_rel_array. - */ -void -build_base_rel(PlannerInfo *root, int relid) -{ - Assert(relid > 0); - - /* Rel should not exist already */ - if (relid < root->base_rel_array_size && - root->base_rel_array[relid] != NULL) - elog(ERROR, "rel already exists"); - - /* No existing RelOptInfo for this base rel, so make a new one */ - (void) make_reloptinfo(root, relid, RELOPT_BASEREL); -} - -/* - * build_other_rel - * Returns relation entry corresponding to 'relid', creating a new one - * if necessary. This is for 'other' relations, which are much like - * base relations except that they have a different RelOptKind. + * build_simple_rel + * Construct a new RelOptInfo for a base relation or 'other' relation. */ RelOptInfo * -build_other_rel(PlannerInfo *root, int relid) +build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) { RelOptInfo *rel; + RangeTblEntry *rte; - Assert(relid > 0); + /* Fetch RTE for relation */ + Assert(relid > 0 && relid <= list_length(root->parse->rtable)); + rte = rt_fetch(relid, root->parse->rtable); - /* 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; - } - } - - /* No existing RelOptInfo for this other rel, so make a new one */ - /* presently, must be an inheritance child rel */ - rel = make_reloptinfo(root, relid, RELOPT_OTHER_CHILD_REL); - - return rel; -} - -/* - * make_reloptinfo - * Construct a RelOptInfo for the specified rangetable index, - * and enter it into base_rel_array. - * - * Common code for build_base_rel and build_other_rel. - */ -static RelOptInfo * -make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind) -{ - RelOptInfo *rel = makeNode(RelOptInfo); - RangeTblEntry *rte = rt_fetch(relid, root->parse->rtable); + /* Rel should not exist already */ + Assert(relid < root->simple_rel_array_size); + if (root->simple_rel_array[relid] != NULL) + elog(ERROR, "rel %d already exists", relid); + rel = makeNode(RelOptInfo); rel->reloptkind = reloptkind; rel->relids = bms_make_singleton(relid); rel->rows = 0; @@ -161,21 +113,8 @@ make_reloptinfo(PlannerInfo *root, int relid, RelOptKind reloptkind) break; } - /* Add the finished struct to the base_rel_array */ - if (relid >= root->base_rel_array_size) - { - int oldsize = root->base_rel_array_size; - int newsize; - - newsize = Max(oldsize * 2, relid + 1); - root->base_rel_array = (RelOptInfo **) - repalloc(root->base_rel_array, newsize * sizeof(RelOptInfo *)); - MemSet(root->base_rel_array + oldsize, 0, - (newsize - oldsize) * sizeof(RelOptInfo *)); - root->base_rel_array_size = newsize; - } - - root->base_rel_array[relid] = rel; + /* Save the finished struct in the query's simple_rel_array */ + root->simple_rel_array[relid] = rel; return rel; } @@ -191,9 +130,9 @@ find_base_rel(PlannerInfo *root, int relid) Assert(relid > 0); - if (relid < root->base_rel_array_size) + if (relid < root->simple_rel_array_size) { - rel = root->base_rel_array[relid]; + rel = root->simple_rel_array[relid]; if (rel) return rel; } @@ -446,12 +385,24 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, foreach(vars, input_rel->reltargetlist) { - Var *var = (Var *) lfirst(vars); + Var *origvar = (Var *) lfirst(vars); + Var *var; RelOptInfo *baserel; int ndx; - /* We can't run into any child RowExprs here */ - Assert(IsA(var, Var)); + /* + * We can't run into any child RowExprs here, but we could find + * a whole-row Var with a ConvertRowtypeExpr atop it. + */ + var = origvar; + while (!IsA(var, Var)) + { + if (IsA(var, ConvertRowtypeExpr)) + var = (Var *) ((ConvertRowtypeExpr *) var)->arg; + else + elog(ERROR, "unexpected node type in reltargetlist: %d", + (int) nodeTag(var)); + } /* Get the Var's original base rel */ baserel = find_base_rel(root, var->varno); @@ -461,8 +412,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, if (bms_nonempty_difference(baserel->attr_needed[ndx], relids)) { /* Yup, add it to the output */ - joinrel->reltargetlist = lappend(joinrel->reltargetlist, var); - Assert(baserel->attr_widths[ndx] > 0); + joinrel->reltargetlist = lappend(joinrel->reltargetlist, origvar); joinrel->width += baserel->attr_widths[ndx]; } } diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 12de72619a0..2db3c27cddf 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.95 2006/01/06 20:11:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.96 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -183,6 +183,17 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) } /* fall through to examine children */ } + if (IsA(node, AppendRelInfo)) + { + AppendRelInfo *appinfo = (AppendRelInfo *) node; + + if (context->sublevels_up == 0) + { + appinfo->parent_relid += context->offset; + appinfo->child_relid += context->offset; + } + /* fall through to examine children */ + } if (IsA(node, Query)) { /* Recurse into subselects */ @@ -323,6 +334,19 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) } /* fall through to examine children */ } + if (IsA(node, AppendRelInfo)) + { + AppendRelInfo *appinfo = (AppendRelInfo *) node; + + if (context->sublevels_up == 0) + { + if (appinfo->parent_relid == context->rt_index) + appinfo->parent_relid = context->new_index; + if (appinfo->child_relid == context->rt_index) + appinfo->child_relid = context->new_index; + } + /* fall through to examine children */ + } if (IsA(node, Query)) { /* Recurse into subselects */ @@ -527,16 +551,11 @@ rangeTableEntry_used_walker(Node *node, return true; /* fall through to examine children */ } - if (IsA(node, InClauseInfo)) - { - InClauseInfo *ininfo = (InClauseInfo *) node; + /* Shouldn't need to handle planner auxiliary nodes here */ + Assert(!IsA(node, OuterJoinInfo)); + Assert(!IsA(node, InClauseInfo)); + Assert(!IsA(node, AppendRelInfo)); - if (context->sublevels_up == 0 && - (bms_is_member(context->rt_index, ininfo->lefthand) || - bms_is_member(context->rt_index, ininfo->righthand))) - return true; - /* fall through to examine children */ - } if (IsA(node, Query)) { /* Recurse into subselects */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 9b33a5de915..98d0ed0321e 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.181 2006/01/01 01:41:42 neilc Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.182 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -190,6 +190,7 @@ typedef enum NodeTag T_InnerIndexscanInfo, T_OuterJoinInfo, T_InClauseInfo, + T_AppendRelInfo, /* * TAGS FOR MEMORY NODES (memnodes.h) diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 1d490fc17bf..67460c0d701 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.122 2005/12/20 02:30:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.123 2006/01/31 21:39:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,7 +52,7 @@ typedef struct QualCost * * This struct is conventionally called "root" in all the planner routines. * It holds links to all of the planner's working state, in addition to the - * original Query. Note that at present the planner extensively manipulates + * original Query. Note that at present the planner extensively modifies * the passed-in Query data structure; someday that should stop. *---------- */ @@ -63,14 +63,14 @@ typedef struct PlannerInfo Query *parse; /* the Query being planned */ /* - * base_rel_array holds pointers to "base rels" and "other rels" (see + * simple_rel_array holds pointers to "base rels" and "other rels" (see * comments for RelOptInfo for more info). It is indexed by rangetable * index (so entry 0 is always wasted). Entries can be NULL when an RTE - * does not correspond to a base relation. Note that the array may be - * enlarged on-the-fly. + * does not correspond to a base relation, such as a join RTE or an + * unreferenced view RTE; or if the RelOptInfo hasn't been made yet. */ - struct RelOptInfo **base_rel_array; /* All one-relation RelOptInfos */ - int base_rel_array_size; /* current allocated array len */ + struct RelOptInfo **simple_rel_array; /* All 1-relation RelOptInfos */ + int simple_rel_array_size; /* allocated size of array */ /* * join_rel_list is a list of all join-relation RelOptInfos we have @@ -101,6 +101,8 @@ typedef struct PlannerInfo List *in_info_list; /* list of InClauseInfos */ + List *append_rel_list; /* list of AppendRelInfos */ + List *query_pathkeys; /* desired pathkeys for query_planner(), and * actual pathkeys afterwards */ @@ -125,7 +127,7 @@ typedef struct PlannerInfo * is the joining of two or more base rels. A joinrel is identified by * the set of RT indexes for its component baserels. We create RelOptInfo * nodes for each baserel and joinrel, and store them in the PlannerInfo's - * base_rel_array and join_rel_list respectively. + * simple_rel_array and join_rel_list respectively. * * Note that there is only one joinrel for any given set of component * baserels, no matter what order we assemble them in; so an unordered @@ -135,16 +137,15 @@ typedef struct PlannerInfo * single RT indexes; but they are not part of the join tree, and are given * a different RelOptKind to identify them. * - * Currently the only kind of otherrels are those made for child relations - * of an inheritance scan (SELECT FROM foo*). The parent table's RTE and - * corresponding baserel represent the whole result of the inheritance scan. - * The planner creates separate RTEs and associated RelOptInfos for each child - * table (including the parent table, in its capacity as a member of the - * inheritance set). These RelOptInfos are physically identical to baserels, - * but are otherrels because they are not in the main join tree. These added - * RTEs and otherrels are used to plan the scans of the individual tables in - * the inheritance set; then the parent baserel is given an Append plan - * comprising the best plans for the individual child tables. + * Currently the only kind of otherrels are those made for member relations + * of an "append relation", that is an inheritance set or UNION ALL subquery. + * An append relation has a parent RTE that is a base rel, which represents + * the entire append relation. The member RTEs are otherrels. The parent + * is present in the query join tree but the members are not. The member + * RTEs and otherrels are used to plan the scans of the individual tables or + * subqueries of the append set; then the parent baserel is given an Append + * plan comprising the best plans for the individual member rels. (See + * comments for AppendRelInfo for more information.) * * At one time we also made otherrels to represent join RTEs, for use in * handling join alias Vars. Currently this is not needed because all join @@ -192,7 +193,7 @@ typedef struct PlannerInfo * upon creation of the RelOptInfo object; they are filled in when * set_base_rel_pathlist processes the object. * - * For otherrels that are inheritance children, these fields are filled + * For otherrels that are appendrel members, these fields are filled * in just as for a baserel. * * The presence of the remaining fields depends on the restrictions @@ -232,7 +233,7 @@ typedef enum RelOptKind { RELOPT_BASEREL, RELOPT_JOINREL, - RELOPT_OTHER_CHILD_REL + RELOPT_OTHER_MEMBER_REL } RelOptKind; typedef struct RelOptInfo @@ -508,8 +509,7 @@ typedef struct TidPath /* * AppendPath represents an Append plan, ie, successive execution of - * several member plans. Currently it is only used to handle expansion - * of inheritance trees. + * several member plans. * * Note: it is possible for "subpaths" to contain only one, or even no, * elements. These cases are optimized during create_append_plan. @@ -879,4 +879,96 @@ typedef struct InClauseInfo */ } InClauseInfo; +/* + * Append-relation info. + * + * When we expand an inheritable table or a UNION-ALL subselect into an + * "append relation" (essentially, a list of child RTEs), we build an + * AppendRelInfo for each child RTE. The list of AppendRelInfos indicates + * which child RTEs must be included when expanding the parent, and each + * node carries information needed to translate Vars referencing the parent + * into Vars referencing that child. + * + * These structs are kept in the PlannerInfo node's append_rel_list. + * Note that we just throw all the structs into one list, and scan the + * whole list when desiring to expand any one parent. We could have used + * a more complex data structure (eg, one list per parent), but this would + * be harder to update during operations such as pulling up subqueries, + * and not really any easier to scan. Considering that typical queries + * will not have many different append parents, it doesn't seem worthwhile + * to complicate things. + * + * Note: after completion of the planner prep phase, any given RTE is an + * append parent having entries in append_rel_list if and only if its + * "inh" flag is set. We clear "inh" for plain tables that turn out not + * to have inheritance children, and (in an abuse of the original meaning + * of the flag) we set "inh" for subquery RTEs that turn out to be + * flattenable UNION ALL queries. This lets us avoid useless searches + * of append_rel_list. + * + * Note: the data structure assumes that append-rel members are single + * baserels. This is OK for inheritance, but it prevents us from pulling + * up a UNION ALL member subquery if it contains a join. While that could + * be fixed with a more complex data structure, at present there's not much + * point because no improvement in the plan could result. + */ + +typedef struct AppendRelInfo +{ + NodeTag type; + /* + * These fields uniquely identify this append relationship. There + * can be (in fact, always should be) multiple AppendRelInfos for the + * same parent_relid, but never more than one per child_relid, since + * a given RTE cannot be a child of more than one append parent. + */ + Index parent_relid; /* RT index of append parent rel */ + Index child_relid; /* RT index of append child rel */ + /* + * For an inheritance appendrel, the parent and child are both regular + * relations, and we store their rowtype OIDs here for use in translating + * whole-row Vars. For a UNION-ALL appendrel, the parent and child are + * both subqueries with no named rowtype, and we store InvalidOid here. + */ + Oid parent_reltype; /* OID of parent's composite type */ + Oid child_reltype; /* OID of child's composite type */ + + /* + * The N'th element of this list is the integer column number of + * the child column corresponding to the N'th column of the parent. + * A list element is zero if it corresponds to a dropped column of the + * parent (this is only possible for inheritance cases, not UNION ALL). + */ + List *col_mappings; /* list of child attribute numbers */ + + /* + * The N'th element of this list is a Var or expression representing + * the child column corresponding to the N'th column of the parent. + * This is used to translate Vars referencing the parent rel into + * references to the child. A list element is NULL if it corresponds + * to a dropped column of the parent (this is only possible for + * inheritance cases, not UNION ALL). + * + * This might seem redundant with the col_mappings data, but it is handy + * because flattening of sub-SELECTs that are members of a UNION ALL + * will cause changes in the expressions that need to be substituted + * for a parent Var. Adjusting this data structure lets us track what + * really needs to be substituted. + * + * Notice we only store entries for user columns (attno > 0). Whole-row + * Vars are special-cased, and system columns (attno < 0) need no + * special translation since their attnos are the same for all tables. + * + * Caution: the Vars have varlevelsup = 0. Be careful to adjust + * as needed when copying into a subquery. + */ + List *translated_vars; /* Expressions in the child's Vars */ + /* + * We store the parent table's OID here for inheritance, or InvalidOid + * for UNION ALL. This is only needed to help in generating error + * messages if an attempt is made to reference a dropped parent column. + */ + Oid parent_reloid; /* OID of parent relation */ +} AppendRelInfo; + #endif /* RELATION_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 6c8d62ac44f..84d9f865e37 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.63 2005/11/26 22:14:57 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.64 2006/01/31 21:39:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -85,8 +85,8 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root, /* * prototypes for relnode.c */ -extern void build_base_rel(PlannerInfo *root, int relid); -extern RelOptInfo *build_other_rel(PlannerInfo *root, int relid); +extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, + RelOptKind reloptkind); extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid); extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids); extern RelOptInfo *build_join_rel(PlannerInfo *root, diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index ce89771b179..ca28cbc886f 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.53 2005/12/20 02:30:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.54 2006/01/31 21:39:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -46,10 +46,18 @@ extern Plan *plan_set_operations(PlannerInfo *root, double tuple_fraction, extern List *find_all_inheritors(Oid parentrel); -extern List *expand_inherited_rtentry(PlannerInfo *root, Index rti); +extern void expand_inherited_tables(PlannerInfo *root); -extern Node *adjust_inherited_attrs(Node *node, - Index old_rt_index, Oid old_relid, - Index new_rt_index, Oid new_relid); +extern Node *adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo); + +extern Relids *adjust_appendrel_attr_needed(RelOptInfo *oldrel, + AppendRelInfo *appinfo, + AttrNumber new_min_attr, + AttrNumber new_max_attr); + +extern Relids *adjust_other_rel_attr_needed(RelOptInfo *oldrel, + AppendRelInfo *appinfo, + AttrNumber new_min_attr, + AttrNumber new_max_attr); #endif /* PREP_H */