diff options
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) |