diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinpath.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinrels.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/path/pathkeys.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 98 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 214 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 38 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 30 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 74 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 20 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 11 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 150 | ||||
-rw-r--r-- | src/backend/optimizer/util/var.c | 375 |
13 files changed, 832 insertions, 221 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index e215a6cd366..2bc0bb8a137 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -42,7 +42,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.82 2002/03/01 20:50:20 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -597,7 +597,7 @@ cost_mergejoin(Path *path, Query *root, leftvar = get_leftop(firstclause->clause); Assert(IsA(leftvar, Var)); - if (intMember(leftvar->varno, outer_path->parent->relids)) + if (VARISRELMEMBER(leftvar->varno, outer_path->parent)) { /* left side of clause is outer */ outerscansel = firstclause->left_mergescansel; @@ -748,7 +748,7 @@ cost_hashjoin(Path *path, Query *root, * a large query, we cache the bucketsize estimate in the RestrictInfo * node to avoid repeated lookups of statistics. */ - if (intMember(right->varno, inner_path->parent->relids)) + if (VARISRELMEMBER(right->varno, inner_path->parent)) { /* righthand side is inner */ innerbucketsize = restrictinfo->right_bucketsize; @@ -761,7 +761,7 @@ cost_hashjoin(Path *path, Query *root, } else { - Assert(intMember(left->varno, inner_path->parent->relids)); + Assert(VARISRELMEMBER(left->varno, inner_path->parent)); /* lefthand side is inner */ innerbucketsize = restrictinfo->left_bucketsize; if (innerbucketsize < 0) diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 4d60569e7ef..11bc8a9f7d3 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.67 2001/11/11 19:18:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.68 2002/03/12 00:51:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "parser/parsetree.h" #include "utils/lsyscache.h" + static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, List *mergeclause_list, @@ -720,8 +721,6 @@ hash_inner_and_outer(Query *root, List *restrictlist, JoinType jointype) { - Relids outerrelids = outerrel->relids; - Relids innerrelids = innerrel->relids; bool isouterjoin; List *i; @@ -773,13 +772,13 @@ hash_inner_and_outer(Query *root, /* * Check if clause is usable with these input rels. */ - if (intMember(left->varno, outerrelids) && - intMember(right->varno, innerrelids)) + if (VARISRELMEMBER(left->varno, outerrel) && + VARISRELMEMBER(right->varno, innerrel)) { /* righthand side is inner */ } - else if (intMember(left->varno, innerrelids) && - intMember(right->varno, outerrelids)) + else if (VARISRELMEMBER(left->varno, innerrel) && + VARISRELMEMBER(right->varno, outerrel)) { /* lefthand side is inner */ } @@ -901,8 +900,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel, JoinType jointype) { List *result_list = NIL; - Relids outerrelids = outerrel->relids; - Relids innerrelids = innerrel->relids; bool isouterjoin = IS_OUTER_JOIN(jointype); List *i; @@ -952,10 +949,10 @@ select_mergejoin_clauses(RelOptInfo *joinrel, left = get_leftop(clause); right = get_rightop(clause); - if ((intMember(left->varno, outerrelids) && - intMember(right->varno, innerrelids)) || - (intMember(left->varno, innerrelids) && - intMember(right->varno, outerrelids))) + if ((VARISRELMEMBER(left->varno, outerrel) && + VARISRELMEMBER(right->varno, innerrel)) || + (VARISRELMEMBER(left->varno, innerrel) && + VARISRELMEMBER(right->varno, outerrel))) result_list = lcons(restrictinfo, result_list); } diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 745a1eb0b5b..d2d511e75e7 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.55 2001/10/25 05:49:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.56 2002/03/12 00:51:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -340,7 +340,7 @@ make_jointree_rel(Query *root, Node *jtnode) { int varno = ((RangeTblRef *) jtnode)->rtindex; - return build_base_rel(root, varno); + return find_base_rel(root, varno); } else if (IsA(jtnode, FromExpr)) { diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index f91e25cdb4f..60e05ca340f 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.36 2001/11/11 20:33:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -854,7 +854,8 @@ make_pathkeys_for_mergeclauses(Query *root, cache_mergeclause_pathkeys(root, restrictinfo); key = (Node *) get_leftop(restrictinfo->clause); - if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids)) + if (IsA(key, Var) && + VARISRELMEMBER(((Var *) key)->varno, rel)) { /* Rel is left side of mergeclause */ pathkey = restrictinfo->left_pathkey; @@ -862,7 +863,8 @@ make_pathkeys_for_mergeclauses(Query *root, else { key = (Node *) get_rightop(restrictinfo->clause); - if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids)) + if (IsA(key, Var) && + VARISRELMEMBER(((Var *) key)->varno, rel)) { /* Rel is right side of mergeclause */ pathkey = restrictinfo->right_pathkey; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 88c5499b302..79d90bf5a3c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.111 2001/10/28 06:25:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.112 2002/03/12 00:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,7 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses); static SubqueryScan *create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses); -static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist, +static NestLoop *create_nestloop_plan(Query *root, + NestPath *best_path, List *tlist, List *joinclauses, List *otherclauses, Plan *outer_plan, List *outer_tlist, Plan *inner_plan, List *inner_tlist); @@ -52,7 +53,8 @@ static MergeJoin *create_mergejoin_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, List *outer_tlist, Plan *inner_plan, List *inner_tlist); -static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist, +static HashJoin *create_hashjoin_plan(Query *root, + HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, Plan *outer_plan, List *outer_tlist, Plan *inner_plan, List *inner_tlist); @@ -78,18 +80,18 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, static NestLoop *make_nestloop(List *tlist, List *joinclauses, List *otherclauses, Plan *lefttree, Plan *righttree, - JoinType jointype); + JoinType jointype, Index joinrti); static HashJoin *make_hashjoin(List *tlist, List *joinclauses, List *otherclauses, List *hashclauses, Plan *lefttree, Plan *righttree, - JoinType jointype); + JoinType jointype, Index joinrti); static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree); static MergeJoin *make_mergejoin(List *tlist, List *joinclauses, List *otherclauses, List *mergeclauses, Plan *lefttree, Plan *righttree, - JoinType jointype); + JoinType jointype, Index joinrti); /* * create_plan @@ -259,7 +261,8 @@ create_join_plan(Query *root, JoinPath *best_path) inner_tlist); break; case T_HashJoin: - plan = (Join *) create_hashjoin_plan((HashPath *) best_path, + plan = (Join *) create_hashjoin_plan(root, + (HashPath *) best_path, join_tlist, joinclauses, otherclauses, @@ -269,7 +272,8 @@ create_join_plan(Query *root, JoinPath *best_path) inner_tlist); break; case T_NestLoop: - plan = (Join *) create_nestloop_plan((NestPath *) best_path, + plan = (Join *) create_nestloop_plan(root, + (NestPath *) best_path, join_tlist, joinclauses, otherclauses, @@ -576,7 +580,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses) *****************************************************************************/ static NestLoop * -create_nestloop_plan(NestPath *best_path, +create_nestloop_plan(Query *root, + NestPath *best_path, List *tlist, List *joinclauses, List *otherclauses, @@ -586,6 +591,7 @@ create_nestloop_plan(NestPath *best_path, List *inner_tlist) { NestLoop *join_plan; + Index joinrti = best_path->path.parent->joinrti; if (IsA(inner_plan, IndexScan)) { @@ -630,19 +636,25 @@ create_nestloop_plan(NestPath *best_path, /* only refs to outer vars get changed in the inner indexqual */ innerscan->indxqualorig = join_references(indxqualorig, + root, outer_tlist, NIL, - innerrel); + innerrel, + joinrti); innerscan->indxqual = join_references(innerscan->indxqual, + root, outer_tlist, NIL, - innerrel); + innerrel, + joinrti); /* fix the inner qpqual too, if it has join clauses */ if (NumRelids((Node *) inner_plan->qual) > 1) inner_plan->qual = join_references(inner_plan->qual, + root, outer_tlist, NIL, - innerrel); + innerrel, + joinrti); } } else if (IsA(inner_plan, TidScan)) @@ -650,9 +662,11 @@ create_nestloop_plan(NestPath *best_path, TidScan *innerscan = (TidScan *) inner_plan; innerscan->tideval = join_references(innerscan->tideval, + root, outer_tlist, inner_tlist, - innerscan->scan.scanrelid); + innerscan->scan.scanrelid, + joinrti); } else if (IsA_Join(inner_plan)) { @@ -671,20 +685,25 @@ create_nestloop_plan(NestPath *best_path, * Set quals to contain INNER/OUTER var references. */ joinclauses = join_references(joinclauses, + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + joinrti); otherclauses = join_references(otherclauses, + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + joinrti); join_plan = make_nestloop(tlist, joinclauses, otherclauses, outer_plan, inner_plan, - best_path->jointype); + best_path->jointype, + joinrti); copy_path_costsize(&join_plan->join.plan, &best_path->path); @@ -704,6 +723,7 @@ create_mergejoin_plan(Query *root, { List *mergeclauses; MergeJoin *join_plan; + Index joinrti = best_path->jpath.path.parent->joinrti; mergeclauses = get_actual_clauses(best_path->path_mergeclauses); @@ -713,26 +733,32 @@ create_mergejoin_plan(Query *root, * clauses to contain INNER/OUTER var references. */ joinclauses = join_references(set_difference(joinclauses, mergeclauses), + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + joinrti); /* * Fix the additional qpquals too. */ otherclauses = join_references(otherclauses, + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + joinrti); /* * Now set the references in the mergeclauses and rearrange them so * that the outer variable is always on the left. */ mergeclauses = switch_outer(join_references(mergeclauses, + root, outer_tlist, inner_tlist, - (Index) 0)); + (Index) 0, + joinrti)); /* * Create explicit sort nodes for the outer and inner join paths if @@ -798,7 +824,8 @@ create_mergejoin_plan(Query *root, mergeclauses, outer_plan, inner_plan, - best_path->jpath.jointype); + best_path->jpath.jointype, + joinrti); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); @@ -806,7 +833,8 @@ create_mergejoin_plan(Query *root, } static HashJoin * -create_hashjoin_plan(HashPath *best_path, +create_hashjoin_plan(Query *root, + HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, @@ -819,6 +847,7 @@ create_hashjoin_plan(HashPath *best_path, HashJoin *join_plan; Hash *hash_plan; Node *innerhashkey; + Index joinrti = best_path->jpath.path.parent->joinrti; /* * NOTE: there will always be exactly one hashclause in the list @@ -834,26 +863,32 @@ create_hashjoin_plan(HashPath *best_path, * clauses to contain INNER/OUTER var references. */ joinclauses = join_references(set_difference(joinclauses, hashclauses), + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + joinrti); /* * Fix the additional qpquals too. */ otherclauses = join_references(otherclauses, + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + joinrti); /* * Now set the references in the hashclauses and rearrange them so * that the outer variable is always on the left. */ hashclauses = switch_outer(join_references(hashclauses, + root, outer_tlist, inner_tlist, - (Index) 0)); + (Index) 0, + joinrti)); /* Now the righthand op of the sole hashclause is the inner hash key. */ innerhashkey = (Node *) get_rightop(lfirst(hashclauses)); @@ -868,7 +903,8 @@ create_hashjoin_plan(HashPath *best_path, hashclauses, outer_plan, (Plan *) hash_plan, - best_path->jpath.jointype); + best_path->jpath.jointype, + joinrti); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); @@ -1327,7 +1363,8 @@ make_nestloop(List *tlist, List *otherclauses, Plan *lefttree, Plan *righttree, - JoinType jointype) + JoinType jointype, + Index joinrti) { NestLoop *node = makeNode(NestLoop); Plan *plan = &node->join.plan; @@ -1340,6 +1377,7 @@ make_nestloop(List *tlist, plan->righttree = righttree; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->join.joinrti = joinrti; return node; } @@ -1351,7 +1389,8 @@ make_hashjoin(List *tlist, List *hashclauses, Plan *lefttree, Plan *righttree, - JoinType jointype) + JoinType jointype, + Index joinrti) { HashJoin *node = makeNode(HashJoin); Plan *plan = &node->join.plan; @@ -1365,6 +1404,7 @@ make_hashjoin(List *tlist, node->hashclauses = hashclauses; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->join.joinrti = joinrti; return node; } @@ -1399,7 +1439,8 @@ make_mergejoin(List *tlist, List *mergeclauses, Plan *lefttree, Plan *righttree, - JoinType jointype) + JoinType jointype, + Index joinrti) { MergeJoin *node = makeNode(MergeJoin); Plan *plan = &node->join.plan; @@ -1413,6 +1454,7 @@ make_mergejoin(List *tlist, node->mergeclauses = mergeclauses; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->join.joinrti = joinrti; return node; } 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) diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 57c9b963c73..ee1d1890712 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.67 2001/10/25 05:49:33 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.68 2002/03/12 00:51:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -176,48 +176,32 @@ subplanner(Query *root, List *flat_tlist, double tuple_fraction) { - List *joined_rels; - List *brel; RelOptInfo *final_rel; Plan *resultplan; Path *cheapestpath; Path *presortedpath; - /* - * Examine the targetlist and qualifications, adding entries to - * base_rel_list as relation references are found (e.g., in the - * qualification, the targetlist, etc.). Restrict and join clauses - * are added to appropriate lists belonging to the mentioned - * relations. We also build lists of equijoined keys for pathkey - * construction. - */ + /* init lists to empty */ root->base_rel_list = NIL; root->other_rel_list = NIL; root->join_rel_list = NIL; root->equi_key_list = NIL; - build_base_rel_tlists(root, flat_tlist); - - (void) distribute_quals_to_rels(root, (Node *) root->jointree); - /* - * Make sure we have RelOptInfo nodes for all relations to be joined. + * Construct RelOptInfo nodes for all base relations in query. */ - joined_rels = add_missing_rels_to_query(root, (Node *) root->jointree); + (void) add_base_rels_to_query(root, (Node *) root->jointree); /* - * Check that the join tree includes all the base relations used in - * the query --- otherwise, the parser or rewriter messed up. + * Examine the targetlist and qualifications, adding entries to + * baserel targetlists for all referenced Vars. Restrict and join + * clauses are added to appropriate lists belonging to the mentioned + * relations. We also build lists of equijoined keys for pathkey + * construction. */ - foreach(brel, root->base_rel_list) - { - RelOptInfo *baserel = (RelOptInfo *) lfirst(brel); - int relid = lfirsti(baserel->relids); + build_base_rel_tlists(root, flat_tlist); - if (!ptrMember(baserel, joined_rels)) - elog(ERROR, "Internal error: no jointree entry for rel %s (%d)", - rt_fetch(relid, root->rtable)->eref->relname, relid); - } + (void) distribute_quals_to_rels(root, (Node *) root->jointree); /* * Use the completed lists of equijoined keys to deduce any implied diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index eb92c3d3af9..1df2cd29402 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.114 2001/12/10 22:54:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.115 2002/03/12 00:51:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -99,7 +99,7 @@ planner(Query *parse) result_plan->nParamExec = length(PlannerParamVar); /* final cleanup of the plan */ - set_plan_references(result_plan); + set_plan_references(parse, result_plan); /* restore state for outer planner, if any */ PlannerQueryLevel = save_PlannerQueryLevel; @@ -616,6 +616,9 @@ preprocess_jointree(Query *parse, Node *jtnode) static Node * preprocess_expression(Query *parse, Node *expr, int kind) { + bool has_join_rtes; + List *rt; + /* * Simplify constant expressions. * @@ -651,6 +654,29 @@ preprocess_expression(Query *parse, Node *expr, int kind) if (PlannerQueryLevel > 1) expr = SS_replace_correlation_vars(expr); + /* + * If the query has any join RTEs, try to replace join alias variables + * with base-relation variables, to allow quals to be pushed down. + * We must do this after sublink processing, since it does not recurse + * into sublinks. + * + * The flattening pass is expensive enough that it seems worthwhile to + * scan the rangetable to see if we can avoid it. + */ + has_join_rtes = false; + foreach(rt, parse->rtable) + { + RangeTblEntry *rte = lfirst(rt); + + if (rte->rtekind == RTE_JOIN) + { + has_join_rtes = true; + break; + } + } + if (has_join_rtes) + expr = flatten_join_alias_vars(expr, parse, 0); + return expr; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 167901acdf0..2f48821ece6 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,25 +9,29 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.73 2001/11/05 17:46:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.74 2002/03/12 00:51:48 tgl Exp $ * *------------------------------------------------------------------------- */ -#include <sys/types.h> - #include "postgres.h" +#include <sys/types.h> + #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/tlist.h" +#include "optimizer/var.h" + typedef struct { + Query *root; List *outer_tlist; List *inner_tlist; Index acceptable_rel; + Index join_rti; } join_references_context; typedef struct @@ -38,7 +42,7 @@ typedef struct } replace_vars_with_subplan_refs_context; static void fix_expr_references(Plan *plan, Node *node); -static void set_join_references(Join *join); +static void set_join_references(Query *root, Join *join); static void set_uppernode_references(Plan *plan, Index subvarno); static Node *join_references_mutator(Node *node, join_references_context *context); @@ -71,7 +75,7 @@ static bool fix_opids_walker(Node *node, void *context); * Returns nothing of interest, but modifies internal fields of nodes. */ void -set_plan_references(Plan *plan) +set_plan_references(Query *root, Plan *plan) { List *pl; @@ -115,16 +119,16 @@ set_plan_references(Plan *plan) fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); /* Recurse into subplan too */ - set_plan_references(((SubqueryScan *) plan)->subplan); + set_plan_references(root, ((SubqueryScan *) plan)->subplan); break; case T_NestLoop: - set_join_references((Join *) plan); + set_join_references(root, (Join *) plan); fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); break; case T_MergeJoin: - set_join_references((Join *) plan); + set_join_references(root, (Join *) plan); fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); @@ -132,7 +136,7 @@ set_plan_references(Plan *plan) (Node *) ((MergeJoin *) plan)->mergeclauses); break; case T_HashJoin: - set_join_references((Join *) plan); + set_join_references(root, (Join *) plan); fix_expr_references(plan, (Node *) plan->targetlist); fix_expr_references(plan, (Node *) plan->qual); fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); @@ -186,7 +190,7 @@ set_plan_references(Plan *plan) * recurse into subplans. */ foreach(pl, ((Append *) plan)->appendplans) - set_plan_references((Plan *) lfirst(pl)); + set_plan_references(root, (Plan *) lfirst(pl)); break; default: elog(ERROR, "set_plan_references: unknown plan type %d", @@ -203,21 +207,21 @@ set_plan_references(Plan *plan) * plan's var nodes against the already-modified nodes of the * subplans. */ - set_plan_references(plan->lefttree); - set_plan_references(plan->righttree); + set_plan_references(root, plan->lefttree); + set_plan_references(root, plan->righttree); foreach(pl, plan->initPlan) { SubPlan *sp = (SubPlan *) lfirst(pl); Assert(IsA(sp, SubPlan)); - set_plan_references(sp->plan); + set_plan_references(root, sp->plan); } foreach(pl, plan->subPlan) { SubPlan *sp = (SubPlan *) lfirst(pl); Assert(IsA(sp, SubPlan)); - set_plan_references(sp->plan); + set_plan_references(root, sp->plan); } } @@ -256,7 +260,7 @@ fix_expr_references(Plan *plan, Node *node) * 'join' is a join plan node */ static void -set_join_references(Join *join) +set_join_references(Query *root, Join *join) { Plan *outer = join->plan.lefttree; Plan *inner = join->plan.righttree; @@ -264,9 +268,11 @@ set_join_references(Join *join) List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist); join->plan.targetlist = join_references(join->plan.targetlist, + root, outer_tlist, inner_tlist, - (Index) 0); + (Index) 0, + join->joinrti); } /* @@ -343,7 +349,8 @@ set_uppernode_references(Plan *plan, Index subvarno) * Creates a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. + * relation target lists. Also, any join alias variables in the + * clauses are expanded into references to their component variables. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; @@ -360,21 +367,27 @@ set_uppernode_references(Plan *plan, Index subvarno) * 'inner_tlist' is the target list of the inner join relation, or NIL * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error. + * 'join_rti' is either zero or the join RTE index of join alias variables + * that should be expanded. * * Returns the new expression tree. The original clause structure is * not modified. */ List * join_references(List *clauses, + Query *root, List *outer_tlist, List *inner_tlist, - Index acceptable_rel) + Index acceptable_rel, + Index join_rti) { join_references_context context; + context.root = root; context.outer_tlist = outer_tlist; context.inner_tlist = inner_tlist; context.acceptable_rel = acceptable_rel; + context.join_rti = join_rti; return (List *) join_references_mutator((Node *) clauses, &context); } @@ -387,12 +400,14 @@ join_references_mutator(Node *node, if (IsA(node, Var)) { Var *var = (Var *) node; - Var *newvar = (Var *) copyObject(var); Resdom *resdom; + /* First look for the var in the input tlists */ resdom = tlist_member((Node *) var, context->outer_tlist); if (resdom) { + Var *newvar = (Var *) copyObject(var); + newvar->varno = OUTER; newvar->varattno = resdom->resno; return (Node *) newvar; @@ -400,18 +415,33 @@ join_references_mutator(Node *node, resdom = tlist_member((Node *) var, context->inner_tlist); if (resdom) { + Var *newvar = (Var *) copyObject(var); + newvar->varno = INNER; newvar->varattno = resdom->resno; return (Node *) newvar; } + /* Perhaps it's a join alias that can be resolved to input vars? */ + if (var->varno == context->join_rti) + { + Node *newnode; + + newnode = flatten_join_alias_vars((Node *) var, + context->root, + context->join_rti); + /* Must now resolve the input vars... */ + newnode = join_references_mutator(newnode, context); + return newnode; + } + /* - * Var not in either tlist --- either raise an error, or return - * the Var unmodified. + * No referent found for Var --- either raise an error, or return + * the Var unmodified if it's for acceptable_rel. */ if (var->varno != context->acceptable_rel) elog(ERROR, "join_references: variable not in subplan target lists"); - return (Node *) newvar; + return (Node *) copyObject(var); } return expression_tree_mutator(node, join_references_mutator, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 64d8b78f066..b2f18780fcc 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.71 2002/03/05 05:10:24 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.72 2002/03/12 00:51:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -673,7 +673,7 @@ List * expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent) { RangeTblEntry *rte = rt_fetch(rti, parse->rtable); - Oid parentOID = rte->relid; + Oid parentOID; List *inhOIDs; List *inhRTIs; List *l; @@ -681,10 +681,11 @@ expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent) /* Does RT entry allow inheritance? */ if (!rte->inh) return NIL; - Assert(parentOID != InvalidOid && rte->subquery == NULL); + Assert(rte->rtekind == RTE_RELATION); /* Always clear the parent's inh flag, see above comments */ rte->inh = false; /* Fast path for common case of childless table */ + parentOID = rte->relid; if (!has_subclass(parentOID)) return NIL; /* Scan for all members of inheritance set */ @@ -811,6 +812,19 @@ adjust_inherited_attrs_mutator(Node *node, rtr->rtindex = context->new_rt_index; return (Node *) rtr; } + if (IsA(node, JoinExpr)) + { + /* Copy the JoinExpr node with correct mutation of subnodes */ + JoinExpr *j; + + j = (JoinExpr *) expression_tree_mutator(node, + adjust_inherited_attrs_mutator, + (void *) context); + /* now fix JoinExpr's rtindex */ + if (j->rtindex == context->old_rt_index) + j->rtindex = context->new_rt_index; + return (Node *) j; + } /* * We have to process RestrictInfo nodes specially: we do NOT want to diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8a4bcf4d9c4..6585fb1905d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.93 2002/01/03 18:01:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.94 2002/03/12 00:51:50 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1808,12 +1808,8 @@ expression_tree_walker(Node *node, return true; if (walker(join->quals, context)) return true; - if (walker((Node *) join->colvars, context)) - return true; - /* - * alias clause, using list, colnames list are deemed - * uninteresting. + * alias clause, using list are deemed uninteresting. */ } break; @@ -2186,8 +2182,7 @@ expression_tree_mutator(Node *node, MUTATE(newnode->larg, join->larg, Node *); MUTATE(newnode->rarg, join->rarg, Node *); MUTATE(newnode->quals, join->quals, Node *); - MUTATE(newnode->colvars, join->colvars, List *); - /* We do not mutate alias, using, or colnames by default */ + /* We do not mutate alias or using by default */ return (Node *) newnode; } break; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 457826f5d31..0fad16fbdd0 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.35 2001/10/25 05:49:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,26 +40,26 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel, /* * build_base_rel - * Returns relation entry corresponding to 'relid', creating a new one - * if necessary. This is for base relations. + * Construct a new base relation RelOptInfo, and put it in the query's + * base_rel_list. */ -RelOptInfo * +void build_base_rel(Query *root, int relid) { List *rels; RelOptInfo *rel; - /* Already made? */ + /* Rel should not exist already */ foreach(rels, root->base_rel_list) { rel = (RelOptInfo *) lfirst(rels); /* length(rel->relids) == 1 for all members of base_rel_list */ if (lfirsti(rel->relids) == relid) - return rel; + elog(ERROR, "build_base_rel: rel already exists"); } - /* It should not exist as an "other" rel */ + /* It should not exist as an "other" rel, either */ foreach(rels, root->other_rel_list) { rel = (RelOptInfo *) lfirst(rels); @@ -73,14 +73,12 @@ build_base_rel(Query *root, int relid) /* and add it to the list */ root->base_rel_list = lcons(rel, root->base_rel_list); - - return rel; } /* * build_other_rel * Returns relation entry corresponding to 'relid', creating a new one - * if necessary. This is for 'other' relations, which are just like + * if necessary. This is for 'other' relations, which are much like * base relations except that they live in a different list. */ RelOptInfo * @@ -111,6 +109,10 @@ build_other_rel(Query *root, int relid) /* No existing RelOptInfo for this other rel, so make a new one */ rel = make_base_rel(root, relid); + /* if it's not a join rel, must be a child rel */ + if (rel->reloptkind == RELOPT_BASEREL) + rel->reloptkind = RELOPT_OTHER_CHILD_REL; + /* and add it to the list */ root->other_rel_list = lcons(rel, root->other_rel_list); @@ -127,8 +129,9 @@ static RelOptInfo * make_base_rel(Query *root, int relid) { RelOptInfo *rel = makeNode(RelOptInfo); - Oid relationObjectId; + RangeTblEntry *rte = rt_fetch(relid, root->rtable); + rel->reloptkind = RELOPT_BASEREL; rel->relids = makeListi1(relid); rel->rows = 0; rel->width = 0; @@ -142,29 +145,40 @@ make_base_rel(Query *root, int relid) rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; + rel->joinrti = 0; + rel->joinrteids = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost = 0; rel->outerjoinset = NIL; rel->joininfo = NIL; rel->innerjoin = NIL; - /* Check rtable to see if it's a plain relation or a subquery */ - relationObjectId = getrelid(relid, root->rtable); - - if (relationObjectId != InvalidOid) - { - /* Plain relation --- retrieve statistics from the system catalogs */ - bool indexed; - - get_relation_info(relationObjectId, - &indexed, &rel->pages, &rel->tuples); - if (indexed) - rel->indexlist = find_secondary_indexes(relationObjectId); - } - else + /* Check type of rtable entry */ + switch (rte->rtekind) { - /* subquery --- mark it as such for later processing */ - rel->issubquery = true; + case RTE_RELATION: + { + /* Table --- retrieve statistics from the system catalogs */ + bool indexed; + + get_relation_info(rte->relid, + &indexed, &rel->pages, &rel->tuples); + if (indexed) + rel->indexlist = find_secondary_indexes(rte->relid); + break; + } + case RTE_SUBQUERY: + /* Subquery --- mark it as such for later processing */ + rel->issubquery = true; + break; + case RTE_JOIN: + /* Join --- must be an otherrel */ + rel->reloptkind = RELOPT_OTHER_JOIN_REL; + break; + default: + elog(ERROR, "make_base_rel: unsupported RTE kind %d", + (int) rte->rtekind); + break; } return rel; @@ -204,6 +218,47 @@ find_base_rel(Query *root, int relid) } /* + * find_other_rel + * Find an otherrel entry, if one exists for the given relid. + * Return NULL if no entry. + */ +RelOptInfo * +find_other_rel(Query *root, int relid) +{ + List *rels; + + foreach(rels, root->other_rel_list) + { + RelOptInfo *rel = (RelOptInfo *) lfirst(rels); + + if (lfirsti(rel->relids) == relid) + return rel; + } + return NULL; +} + +/* + * find_other_rel_for_join + * Look for an otherrel for a join RTE matching the given baserel set. + * Return NULL if no entry. + */ +RelOptInfo * +find_other_rel_for_join(Query *root, List *relids) +{ + List *rels; + + foreach(rels, root->other_rel_list) + { + RelOptInfo *rel = (RelOptInfo *) lfirst(rels); + + if (rel->reloptkind == RELOPT_OTHER_JOIN_REL + && sameseti(relids, rel->outerjoinset)) + return rel; + } + return NULL; +} + +/* * find_join_rel * Returns relation entry corresponding to 'relids' (a list of RT indexes), * or NULL if none exists. This is for join relations. @@ -252,6 +307,7 @@ build_join_rel(Query *root, { List *joinrelids; RelOptInfo *joinrel; + RelOptInfo *joinrterel; List *restrictlist; List *new_outer_tlist; List *new_inner_tlist; @@ -286,6 +342,7 @@ build_join_rel(Query *root, * Nope, so make one. */ joinrel = makeNode(RelOptInfo); + joinrel->reloptkind = RELOPT_JOINREL; joinrel->relids = joinrelids; joinrel->rows = 0; joinrel->width = 0; @@ -299,24 +356,38 @@ build_join_rel(Query *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; + joinrel->joinrti = 0; + joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids), + inner_rel->joinrteids); joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost = 0; joinrel->outerjoinset = NIL; joinrel->joininfo = NIL; joinrel->innerjoin = NIL; + /* Is there a join RTE matching this join? */ + joinrterel = find_other_rel_for_join(root, joinrelids); + if (joinrterel) + { + /* Yes, remember its RT index */ + joinrel->joinrti = lfirsti(joinrterel->relids); + joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids); + } + /* * Create a new tlist by removing irrelevant elements from both tlists * of the outer and inner join relations and then merging the results * together. * + * XXX right now we don't remove any irrelevant elements, we just + * append the two tlists together. Someday consider pruning vars from the + * join's targetlist if they are needed only to evaluate restriction + * clauses of this join, and will never be accessed at higher levels of + * the plantree. + * * NOTE: the tlist order for a join rel will depend on which pair of * outer and inner rels we first try to build it from. But the * contents should be the same regardless. - * - * XXX someday: consider pruning vars from the join's targetlist if they - * are needed only to evaluate restriction clauses of this join, and - * will never be accessed at higher levels of the plantree. */ new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1); new_inner_tlist = new_join_tlist(inner_rel->targetlist, @@ -324,6 +395,23 @@ build_join_rel(Query *root, joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist); /* + * If there are any alias variables attached to the matching join RTE, + * attach them to the tlist too, so that they will be evaluated for use + * at higher plan levels. + */ + if (joinrterel) + { + List *jrtetl; + + foreach(jrtetl, joinrterel->targetlist) + { + TargetEntry *jrtete = lfirst(jrtetl); + + add_var_to_tlist(joinrel, (Var *) jrtete->expr); + } + } + + /* * Construct restrict and join clause lists for the new joinrel. (The * caller might or might not need the restrictlist, but I need it * anyway for set_joinrel_size_estimates().) diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 2521ffb2b6c..568e024d20b 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,15 +8,18 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.33 2001/10/25 05:49:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.34 2002/03/12 00:51:51 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "nodes/makefuncs.h" #include "nodes/plannodes.h" #include "optimizer/clauses.h" #include "optimizer/var.h" +#include "parser/parsetree.h" +#include "parser/parse_coerce.h" typedef struct @@ -38,6 +41,12 @@ typedef struct bool includeUpperVars; } pull_var_clause_context; +typedef struct +{ + Query *root; + int expandRTI; +} flatten_join_alias_vars_context; + static bool pull_varnos_walker(Node *node, pull_varnos_context *context); static bool contain_var_reference_walker(Node *node, @@ -45,6 +54,10 @@ static bool contain_var_reference_walker(Node *node, static bool contain_var_clause_walker(Node *node, void *context); static bool pull_var_clause_walker(Node *node, pull_var_clause_context *context); +static Node *flatten_join_alias_vars_mutator(Node *node, + flatten_join_alias_vars_context *context); +static Node *flatten_join_alias_var(Var *var, Query *root, int expandRTI); +static Node *find_jointree_item(Node *jtnode, int rtindex); /* @@ -297,3 +310,363 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) return expression_tree_walker(node, pull_var_clause_walker, (void *) context); } + + +/* + * flatten_join_alias_vars + * Whereever possible, replace Vars that reference JOIN outputs with + * references to the original relation variables instead. This allows + * quals involving such vars to be pushed down. Vars that cannot be + * simplified to non-join Vars are replaced by COALESCE expressions + * if they have varno = expandRTI, and are left as JOIN RTE references + * otherwise. (Pass expandRTI = 0 to prevent all COALESCE expansion.) + * + * Upper-level vars (with varlevelsup > 0) are ignored; normally there + * should not be any by the time this routine is called. + * + * Does not examine subqueries, therefore must only be used after reduction + * of sublinks to subplans! + */ +Node * +flatten_join_alias_vars(Node *node, Query *root, int expandRTI) +{ + flatten_join_alias_vars_context context; + + context.root = root; + context.expandRTI = expandRTI; + + return flatten_join_alias_vars_mutator(node, &context); +} + +static Node * +flatten_join_alias_vars_mutator(Node *node, + flatten_join_alias_vars_context *context) +{ + if (node == NULL) + return NULL; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup != 0) + return node; /* no need to copy, really */ + return flatten_join_alias_var(var, context->root, context->expandRTI); + } + return expression_tree_mutator(node, flatten_join_alias_vars_mutator, + (void *) context); +} + +static Node * +flatten_join_alias_var(Var *var, Query *root, int expandRTI) +{ + Index varno = var->varno; + AttrNumber varattno = var->varattno; + Oid vartype = var->vartype; + int32 vartypmod = var->vartypmod; + JoinExpr *jexpr = NULL; + + /* + * Loop to cope with joins of joins + */ + for (;;) + { + RangeTblEntry *rte = rt_fetch(varno, root->rtable); + Index leftrti, + rightrti; + AttrNumber leftattno, + rightattno; + RangeTblEntry *subrte; + Oid subtype; + int32 subtypmod; + + if (rte->rtekind != RTE_JOIN) + break; /* reached a non-join RTE */ + /* + * Find the RT indexes of the left and right children of the + * join node. We have to search the join tree to do this, + * which is a major pain in the neck --- but keeping RT indexes + * in other RT entries is worse, because it makes modifying + * querytrees difficult. (Perhaps we can improve on the + * rangetable/jointree datastructure someday.) One thing we + * can do is avoid repeated searches while tracing a single + * variable down to its baserel. + */ + if (jexpr == NULL) + jexpr = (JoinExpr *) + find_jointree_item((Node *) root->jointree, varno); + if (jexpr == NULL || + !IsA(jexpr, JoinExpr) || + jexpr->rtindex != varno) + elog(ERROR, "flatten_join_alias_var: failed to find JoinExpr"); + if (IsA(jexpr->larg, RangeTblRef)) + leftrti = ((RangeTblRef *) jexpr->larg)->rtindex; + else if (IsA(jexpr->larg, JoinExpr)) + leftrti = ((JoinExpr *) jexpr->larg)->rtindex; + else + { + elog(ERROR, "flatten_join_alias_var: unexpected subtree type"); + leftrti = 0; /* keep compiler quiet */ + } + if (IsA(jexpr->rarg, RangeTblRef)) + rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex; + else if (IsA(jexpr->rarg, JoinExpr)) + rightrti = ((JoinExpr *) jexpr->rarg)->rtindex; + else + { + elog(ERROR, "flatten_join_alias_var: unexpected subtree type"); + rightrti = 0; /* keep compiler quiet */ + } + /* + * See if the join var is from the left side, the right side, + * or both (ie, it is a USING/NATURAL JOIN merger column). + */ + Assert(varattno > 0); + leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols); + rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols); + if (leftattno && rightattno) + { + /* + * Var is a merge var. If a left or right join, we can replace + * it by the left or right input var respectively; we only need + * a COALESCE for a full join. However, beware of the possibility + * that there's been a type promotion to make the input vars + * compatible; do not replace a var by one of a different type! + */ + if (rte->jointype == JOIN_INNER || + rte->jointype == JOIN_LEFT) + { + subrte = rt_fetch(leftrti, root->rtable); + get_rte_attribute_type(subrte, leftattno, + &subtype, &subtypmod); + if (vartype == subtype && vartypmod == subtypmod) + { + varno = leftrti; + varattno = leftattno; + jexpr = (JoinExpr *) jexpr->larg; + continue; + } + } + if (rte->jointype == JOIN_INNER || + rte->jointype == JOIN_RIGHT) + { + subrte = rt_fetch(rightrti, root->rtable); + get_rte_attribute_type(subrte, rightattno, + &subtype, &subtypmod); + if (vartype == subtype && vartypmod == subtypmod) + { + varno = rightrti; + varattno = rightattno; + jexpr = (JoinExpr *) jexpr->rarg; + continue; + } + } + /* + * This var cannot be substituted directly, only with a COALESCE. + * Do so only if it belongs to the particular join indicated by + * the caller. + */ + if (varno != expandRTI) + break; + { + Node *l_var, + *r_var; + CaseExpr *c = makeNode(CaseExpr); + CaseWhen *w = makeNode(CaseWhen); + NullTest *n = makeNode(NullTest); + + subrte = rt_fetch(leftrti, root->rtable); + get_rte_attribute_type(subrte, leftattno, + &subtype, &subtypmod); + l_var = (Node *) makeVar(leftrti, + leftattno, + subtype, + subtypmod, + 0); + if (subtype != vartype) + { + l_var = coerce_type(NULL, l_var, subtype, + vartype, vartypmod); + l_var = coerce_type_typmod(NULL, l_var, + vartype, vartypmod); + } + else if (subtypmod != vartypmod) + l_var = coerce_type_typmod(NULL, l_var, + vartype, vartypmod); + + subrte = rt_fetch(rightrti, root->rtable); + get_rte_attribute_type(subrte, rightattno, + &subtype, &subtypmod); + r_var = (Node *) makeVar(rightrti, + rightattno, + subtype, + subtypmod, + 0); + if (subtype != vartype) + { + r_var = coerce_type(NULL, r_var, subtype, + vartype, vartypmod); + r_var = coerce_type_typmod(NULL, r_var, + vartype, vartypmod); + } + else if (subtypmod != vartypmod) + r_var = coerce_type_typmod(NULL, r_var, + vartype, vartypmod); + + n->arg = l_var; + n->nulltesttype = IS_NOT_NULL; + w->expr = (Node *) n; + w->result = l_var; + c->casetype = vartype; + c->args = makeList1(w); + c->defresult = r_var; + return (Node *) c; + } + } + else if (leftattno) + { + /* Here we do not need to check the type */ + varno = leftrti; + varattno = leftattno; + jexpr = (JoinExpr *) jexpr->larg; + } + else + { + Assert(rightattno); + /* Here we do not need to check the type */ + varno = rightrti; + varattno = rightattno; + jexpr = (JoinExpr *) jexpr->rarg; + } + } + + /* + * When we fall out of the loop, we've reached the base Var. + */ + return (Node *) makeVar(varno, + varattno, + vartype, + vartypmod, + 0); +} + +/* + * Given a join alias Var, construct Vars for the two input vars it directly + * depends on. Note that this should *only* be called for merger alias Vars. + * In practice it is only used for Vars that got past flatten_join_alias_vars. + */ +void +build_join_alias_subvars(Query *root, Var *aliasvar, + Var **leftsubvar, Var **rightsubvar) +{ + Index varno = aliasvar->varno; + AttrNumber varattno = aliasvar->varattno; + RangeTblEntry *rte; + JoinExpr *jexpr; + Index leftrti, + rightrti; + AttrNumber leftattno, + rightattno; + RangeTblEntry *subrte; + Oid subtype; + int32 subtypmod; + + Assert(aliasvar->varlevelsup == 0); + rte = rt_fetch(varno, root->rtable); + Assert(rte->rtekind == RTE_JOIN); + + /* + * Find the RT indexes of the left and right children of the + * join node. + */ + jexpr = (JoinExpr *) find_jointree_item((Node *) root->jointree, varno); + if (jexpr == NULL || + !IsA(jexpr, JoinExpr) || + jexpr->rtindex != varno) + elog(ERROR, "build_join_alias_subvars: failed to find JoinExpr"); + if (IsA(jexpr->larg, RangeTblRef)) + leftrti = ((RangeTblRef *) jexpr->larg)->rtindex; + else if (IsA(jexpr->larg, JoinExpr)) + leftrti = ((JoinExpr *) jexpr->larg)->rtindex; + else + { + elog(ERROR, "build_join_alias_subvars: unexpected subtree type"); + leftrti = 0; /* keep compiler quiet */ + } + if (IsA(jexpr->rarg, RangeTblRef)) + rightrti = ((RangeTblRef *) jexpr->rarg)->rtindex; + else if (IsA(jexpr->rarg, JoinExpr)) + rightrti = ((JoinExpr *) jexpr->rarg)->rtindex; + else + { + elog(ERROR, "build_join_alias_subvars: unexpected subtree type"); + rightrti = 0; /* keep compiler quiet */ + } + + Assert(varattno > 0); + leftattno = (AttrNumber) nthi(varattno-1, rte->joinleftcols); + rightattno = (AttrNumber) nthi(varattno-1, rte->joinrightcols); + if (!(leftattno && rightattno)) + elog(ERROR, "build_join_alias_subvars: non-merger variable"); + + subrte = rt_fetch(leftrti, root->rtable); + get_rte_attribute_type(subrte, leftattno, + &subtype, &subtypmod); + *leftsubvar = makeVar(leftrti, + leftattno, + subtype, + subtypmod, + 0); + + subrte = rt_fetch(rightrti, root->rtable); + get_rte_attribute_type(subrte, rightattno, + &subtype, &subtypmod); + *rightsubvar = makeVar(rightrti, + rightattno, + subtype, + subtypmod, + 0); +} + +/* + * Find jointree item matching the specified RT index + */ +static Node * +find_jointree_item(Node *jtnode, int rtindex) +{ + if (jtnode == NULL) + return NULL; + if (IsA(jtnode, RangeTblRef)) + { + if (((RangeTblRef *) jtnode)->rtindex == rtindex) + return jtnode; + } + else if (IsA(jtnode, FromExpr)) + { + FromExpr *f = (FromExpr *) jtnode; + List *l; + + foreach(l, f->fromlist) + { + jtnode = find_jointree_item(lfirst(l), rtindex); + if (jtnode) + return jtnode; + } + } + else if (IsA(jtnode, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) jtnode; + + if (j->rtindex == rtindex) + return jtnode; + jtnode = find_jointree_item(j->larg, rtindex); + if (jtnode) + return jtnode; + jtnode = find_jointree_item(j->rarg, rtindex); + if (jtnode) + return jtnode; + } + else + elog(ERROR, "find_jointree_item: unexpected node type %d", + nodeTag(jtnode)); + return NULL; +} |