diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-03-12 00:52:10 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-03-12 00:52:10 +0000 |
commit | 6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272 (patch) | |
tree | 5f209c5926768472f9d4fd7210065c18831dbd9c /src/backend/optimizer/plan/initsplan.c | |
parent | 66b6bf67a197db73a880d2a4d9dab05168cde8e0 (diff) | |
download | postgresql-6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272.tar.gz postgresql-6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272.zip |
Restructure representation of join alias variables. An explicit JOIN
now has an RTE of its own, and references to its outputs now are Vars
referencing the JOIN RTE, rather than CASE-expressions. This allows
reverse-listing in ruleutils.c to use the correct alias easily, rather
than painfully reverse-engineering the alias namespace as it used to do.
Also, nested FULL JOINs work correctly, because the result of the inner
joins are simple Vars that the planner can cope with. This fixes a bug
reported a couple times now, notably by Tatsuo on 18-Nov-01. The alias
Vars are expanded into COALESCE expressions where needed at the very end
of planning, rather than during parsing.
Also, beginnings of support for showing plan qualifier expressions in
EXPLAIN. There are probably still cases that need work.
initdb forced due to change of stored-rule representation.
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 214 |
1 files changed, 137 insertions, 77 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 76677b4059c..2c9acc73b7f 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.66 2002/03/01 06:01:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.67 2002/03/12 00:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,67 +53,29 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); /***************************************************************************** * - * TARGET LISTS + * JOIN TREES * *****************************************************************************/ /* - * build_base_rel_tlists - * Creates rel nodes for every relation mentioned in the target list - * 'tlist' (if a node hasn't already been created) and adds them to - * root->base_rel_list. Creates targetlist entries for each var seen - * in 'tlist' and adds them to the tlist of the appropriate rel node. - */ -void -build_base_rel_tlists(Query *root, List *tlist) -{ - List *tlist_vars = pull_var_clause((Node *) tlist, false); - - add_vars_to_targetlist(root, tlist_vars); - freeList(tlist_vars); -} - -/* - * add_vars_to_targetlist - * For each variable appearing in the list, add it to the relation's - * targetlist if not already present. Corresponding base rel nodes - * will be created if not already present. - */ -static void -add_vars_to_targetlist(Query *root, List *vars) -{ - List *temp; - - foreach(temp, vars) - { - Var *var = (Var *) lfirst(temp); - RelOptInfo *rel = build_base_rel(root, var->varno); - - add_var_to_tlist(rel, var); - } -} - -/*---------- - * add_missing_rels_to_query + * add_base_rels_to_query + * + * Scan the query's jointree and create baserel RelOptInfos for all + * the base relations (ie, table and subquery RTEs) appearing in the + * jointree. Also, create otherrel RelOptInfos for join RTEs. * - * If we have a relation listed in the join tree that does not appear - * in the target list nor qualifications, we must add it to the base - * relation list so that it can be processed. For instance, - * select count(*) from foo; - * would fail to scan foo if this routine were not called. More subtly, - * select f.x from foo f, foo f2 - * is a join of f and f2. Note that if we have - * select foo.x from foo f - * this also gets turned into a join (between foo as foo and foo as f). + * The return value is a list of all the baserel indexes (but not join RTE + * indexes) included in the scanned jointree. This is actually just an + * internal convenience for marking join otherrels properly; no outside + * caller uses the result. * - * Returns a list of all the base relations (RelOptInfo nodes) that appear - * in the join tree. This list can be used for cross-checking in the - * reverse direction, ie, that we have a join tree entry for every - * relation used in the query. - *---------- + * 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 again later to build rels for inheritance children. */ List * -add_missing_rels_to_query(Query *root, Node *jtnode) +add_base_rels_to_query(Query *root, Node *jtnode) { List *result = NIL; @@ -123,10 +85,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode) { int varno = ((RangeTblRef *) jtnode)->rtindex; - /* This call to build_base_rel does the primary work... */ - RelOptInfo *rel = build_base_rel(root, varno); - - result = makeList1(rel); + build_base_rel(root, varno); + result = makeListi1(varno); } else if (IsA(jtnode, FromExpr)) { @@ -136,19 +96,33 @@ add_missing_rels_to_query(Query *root, Node *jtnode) foreach(l, f->fromlist) { result = nconc(result, - add_missing_rels_to_query(root, lfirst(l))); + add_base_rels_to_query(root, lfirst(l))); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; + RelOptInfo *jrel; - result = add_missing_rels_to_query(root, j->larg); + result = add_base_rels_to_query(root, j->larg); result = nconc(result, - add_missing_rels_to_query(root, j->rarg)); + add_base_rels_to_query(root, j->rarg)); + /* the join's own rtindex is NOT added to result */ + jrel = build_other_rel(root, j->rtindex); + /* + * Mark the join's otherrel with outerjoinset = list of baserel ids + * included in the join. Note we must copy here because result list + * is destructively modified by nconcs at higher levels. + */ + jrel->outerjoinset = listCopy(result); + /* + * Safety check: join RTEs should not be SELECT FOR UPDATE targets + */ + if (intMember(j->rtindex, root->rowMarks)) + elog(ERROR, "SELECT FOR UPDATE cannot be applied to a join"); } else - elog(ERROR, "add_missing_rels_to_query: unexpected node type %d", + elog(ERROR, "add_base_rels_to_query: unexpected node type %d", nodeTag(jtnode)); return result; } @@ -156,6 +130,66 @@ add_missing_rels_to_query(Query *root, Node *jtnode) /***************************************************************************** * + * TARGET LISTS + * + *****************************************************************************/ + +/* + * build_base_rel_tlists + * Creates targetlist entries for each var seen in 'tlist' and adds + * them to the tlist of the appropriate rel node. + */ +void +build_base_rel_tlists(Query *root, List *tlist) +{ + List *tlist_vars = pull_var_clause((Node *) tlist, false); + + add_vars_to_targetlist(root, tlist_vars); + freeList(tlist_vars); +} + +/* + * add_vars_to_targetlist + * For each variable appearing in the list, add it to the owning + * relation's targetlist if not already present. + * + * Note that join alias variables will be attached to the otherrel for + * the join RTE. They will later be transferred to the tlist of + * the corresponding joinrel. We will also cause entries to be made + * for the Vars that the alias will eventually depend on. + */ +static void +add_vars_to_targetlist(Query *root, List *vars) +{ + List *temp; + + foreach(temp, vars) + { + Var *var = (Var *) lfirst(temp); + RelOptInfo *rel = find_base_rel(root, var->varno); + + add_var_to_tlist(rel, var); + + if (rel->reloptkind == RELOPT_OTHER_JOIN_REL) + { + /* Var is an alias */ + Var *leftsubvar, + *rightsubvar; + + build_join_alias_subvars(root, var, + &leftsubvar, &rightsubvar); + + rel = find_base_rel(root, leftsubvar->varno); + add_var_to_tlist(rel, leftsubvar); + rel = find_base_rel(root, rightsubvar->varno); + add_var_to_tlist(rel, rightsubvar); + } + } +} + + +/***************************************************************************** + * * QUALIFICATIONS * *****************************************************************************/ @@ -165,10 +199,9 @@ add_missing_rels_to_query(Query *root, Node *jtnode) * distribute_quals_to_rels * Recursively scan the query's join tree for WHERE and JOIN/ON qual * clauses, and add these to the appropriate RestrictInfo and JoinInfo - * lists belonging to base RelOptInfos. New base rel entries are created - * as needed. Also, base RelOptInfos are marked with outerjoinset - * information, to aid in proper positioning of qual clauses that appear - * above outer joins. + * lists belonging to base RelOptInfos. Also, base RelOptInfos are marked + * with outerjoinset information, to aid in proper positioning of qual + * clauses that appear above outer joins. * * NOTE: when dealing with inner joins, it is appropriate to let a qual clause * be evaluated at the lowest level where all the variables it mentions are @@ -181,7 +214,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode) * a rel, thereby forcing them up the join tree to the right level. * * To ease the calculation of these values, distribute_quals_to_rels() returns - * the list of Relids involved in its own level of join. This is just an + * the list of base Relids involved in its own level of join. This is just an * internal convenience; no outside callers pay attention to the result. */ Relids @@ -302,7 +335,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels) foreach(relid, rels) { int relno = lfirsti(relid); - RelOptInfo *rel = build_base_rel(root, relno); + RelOptInfo *rel = find_base_rel(root, relno); /* * Since we do this bottom-up, any outer-rels previously marked @@ -382,11 +415,41 @@ distribute_qual_to_rels(Query *root, Node *clause, clause_get_relids_vars(clause, &relids, &vars); /* - * Cross-check: clause should contain no relids not within its scope. - * Otherwise the parser messed up. + * The clause might contain some join alias vars; if so, we want to + * remove the join otherrelids from relids and add the referent joins' + * scope lists instead (thus ensuring that the clause can be evaluated + * no lower than that join node). We rely here on the marking done + * earlier by add_base_rels_to_query. + * + * We can combine this step with a cross-check that the clause contains + * no relids not within its scope. If the first crosscheck succeeds, + * the clause contains no aliases and we needn't look more closely. */ if (!is_subseti(relids, qualscope)) - elog(ERROR, "JOIN qualification may not refer to other relations"); + { + Relids newrelids = NIL; + List *relid; + + foreach(relid, relids) + { + RelOptInfo *rel = find_other_rel(root, lfirsti(relid)); + + if (rel && rel->outerjoinset) + { + /* this relid is for a join RTE */ + newrelids = set_unioni(newrelids, rel->outerjoinset); + } + else + { + /* this relid is for a true baserel */ + newrelids = lappendi(newrelids, lfirsti(relid)); + } + } + relids = newrelids; + /* Now repeat the crosscheck */ + if (!is_subseti(relids, qualscope)) + elog(ERROR, "JOIN qualification may not refer to other relations"); + } /* * If the clause is variable-free, we force it to be evaluated at its @@ -439,7 +502,7 @@ distribute_qual_to_rels(Query *root, Node *clause, can_be_equijoin = true; foreach(relid, relids) { - RelOptInfo *rel = build_base_rel(root, lfirsti(relid)); + RelOptInfo *rel = find_base_rel(root, lfirsti(relid)); if (rel->outerjoinset && !is_subseti(rel->outerjoinset, relids)) @@ -475,7 +538,7 @@ distribute_qual_to_rels(Query *root, Node *clause, * There is only one relation participating in 'clause', so * 'clause' is a restriction clause for that relation. */ - RelOptInfo *rel = build_base_rel(root, lfirsti(relids)); + RelOptInfo *rel = find_base_rel(root, lfirsti(relids)); /* * Check for a "mergejoinable" clause even though it's not a join @@ -595,7 +658,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, * Find or make the joininfo node for this combination of rels, * and add the restrictinfo node to it. */ - joininfo = find_joininfo_node(build_base_rel(root, cur_relid), + joininfo = find_joininfo_node(find_base_rel(root, cur_relid), unjoined_relids); joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo, restrictinfo); @@ -640,9 +703,6 @@ process_implied_equality(Query *root, Node *item1, Node *item2, * If both vars belong to same rel, we need to look at that rel's * baserestrictinfo list. If different rels, each will have a * joininfo node for the other, and we can scan either list. - * - * All baserel entries should already exist at this point, so use - * find_base_rel not build_base_rel. */ rel1 = find_base_rel(root, irel1); if (irel1 == irel2) |