From cb02610e503957d7ed9b4375537fb6275c16f1fa Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 15 Jun 2003 22:51:45 +0000 Subject: [PATCH] Adjust nestloop-with-inner-indexscan plan generation so that we catch some cases of redundant clauses that were formerly not caught. We have to special-case this because the clauses involved never get attached to the same join restrictlist and so the existing logic does not notice that they are redundant. --- src/backend/nodes/list.c | 17 ++- src/backend/nodes/outfuncs.c | 6 +- src/backend/optimizer/path/indxpath.c | 63 +++++--- src/backend/optimizer/path/orindxpath.c | 5 +- src/backend/optimizer/plan/createplan.c | 132 +++++++++-------- src/backend/optimizer/util/pathnode.c | 5 +- src/backend/optimizer/util/restrictinfo.c | 170 +++++++++++++++------- src/include/nodes/pg_list.h | 5 +- src/include/nodes/relation.h | 12 +- src/include/optimizer/restrictinfo.h | 6 +- 10 files changed, 284 insertions(+), 137 deletions(-) diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c index 207acea9472..7467e1a00e1 100644 --- a/src/backend/nodes/list.c +++ b/src/backend/nodes/list.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.49 2003/05/28 22:32:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.50 2003/06/15 22:51:45 tgl Exp $ * * NOTES * XXX a few of the following functions are duplicated to handle @@ -358,6 +358,21 @@ llast(List *l) return lfirst(l); } +/* + * llastnode + * + * Get the last node of l ... NIL if empty list + */ +List * +llastnode(List *l) +{ + if (l == NIL) + return NIL; + while (lnext(l) != NIL) + l = lnext(l); + return l; +} + /* * freeList * diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index cec7f09f0a9..f042e8a8d21 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.207 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.208 2003/06/15 22:51:45 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -933,6 +933,7 @@ _outIndexPath(StringInfo str, IndexPath *node) WRITE_NODE_FIELD(indexinfo); WRITE_NODE_FIELD(indexqual); + WRITE_NODE_FIELD(indexjoinclauses); WRITE_ENUM_FIELD(indexscandir, ScanDirection); WRITE_FLOAT_FIELD(rows, "%.2f"); } @@ -1034,6 +1035,7 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node) { WRITE_NODE_TYPE("RESTRICTINFO"); + /* NB: this isn't a complete set of fields */ WRITE_NODE_FIELD(clause); WRITE_BOOL_FIELD(ispusheddown); WRITE_NODE_FIELD(subclauseindices); @@ -1042,6 +1044,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node) WRITE_OID_FIELD(mergejoinoperator); WRITE_OID_FIELD(left_sortop); WRITE_OID_FIELD(right_sortop); + WRITE_NODE_FIELD(left_pathkey); + WRITE_NODE_FIELD(right_pathkey); WRITE_OID_FIELD(hashjoinoperator); } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index ace494ba20b..fbbf983869c 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.143 2003/05/28 22:32:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.144 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -59,10 +59,10 @@ static bool match_or_subclause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index, Expr *clause); static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index); -static List *group_clauses_by_indexkey_for_join(RelOptInfo *rel, - IndexOptInfo *index, - Relids outer_relids, - bool isouterjoin); +static List *group_clauses_by_indexkey_for_join(Query *root, + RelOptInfo *rel, IndexOptInfo *index, + Relids outer_relids, + JoinType jointype, bool isouterjoin); static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index, int indexcol, Oid opclass, Expr *clause); static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index, @@ -583,8 +583,10 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) * will already have been generated for it.) */ static List * -group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, - Relids outer_relids, bool isouterjoin) +group_clauses_by_indexkey_for_join(Query *root, + RelOptInfo *rel, IndexOptInfo *index, + Relids outer_relids, + JoinType jointype, bool isouterjoin) { FastList clausegroup_list; bool jfound = false; @@ -629,6 +631,24 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, } } + /* + * If we found join clauses in more than one joininfo list, we may + * now have clauses that are known redundant. Get rid of 'em. + * (There is no point in looking at restriction clauses, because + * remove_redundant_join_clauses will never think they are + * redundant, so we do this before adding restriction clauses to + * the clause group.) + */ + if (FastListValue(&clausegroup) != NIL) + { + List *nl; + + nl = remove_redundant_join_clauses(root, + FastListValue(&clausegroup), + jointype); + FastListFromList(&clausegroup, nl); + } + /* We can also use plain restriction clauses for the rel */ foreach(i, rel->baserestrictinfo) { @@ -1461,9 +1481,11 @@ best_inner_indexscan(Query *root, RelOptInfo *rel, List *clausegroups; /* find useful clauses for this index and outerjoin set */ - clausegroups = group_clauses_by_indexkey_for_join(rel, + clausegroups = group_clauses_by_indexkey_for_join(root, + rel, index, index_outer_relids, + jointype, isouterjoin); if (clausegroups) { @@ -1520,7 +1542,7 @@ make_innerjoin_index_path(Query *root, *allclauses, *l; - /* XXX this code ought to be merged with create_index_path? */ + /* XXX perhaps this code should be merged with create_index_path? */ pathnode->path.pathtype = T_IndexScan; pathnode->path.parent = rel; @@ -1535,12 +1557,25 @@ make_innerjoin_index_path(Query *root, /* Convert RestrictInfo nodes to indexquals the executor can handle */ indexquals = expand_indexqual_conditions(index, clausegroups); + /* + * Also make a flattened list of the RestrictInfo nodes; createplan.c + * will need this later. We assume here that we can destructively + * modify the passed-in clausegroups list structure. + */ + allclauses = NIL; + foreach(l, clausegroups) + { + /* nconc okay here since same clause couldn't be in two sublists */ + allclauses = nconc(allclauses, (List *) lfirst(l)); + } + /* * Note that we are making a pathnode for a single-scan indexscan; - * therefore, both indexinfo and indexqual should be single-element lists. + * therefore, indexinfo and indexqual should be single-element lists. */ pathnode->indexinfo = makeList1(index); pathnode->indexqual = makeList1(indexquals); + pathnode->indexjoinclauses = makeList1(allclauses); /* We don't actually care what order the index scans in ... */ pathnode->indexscandir = NoMovementScanDirection; @@ -1558,17 +1593,9 @@ make_innerjoin_index_path(Query *root, * linking them into different lists, it should be sufficient to use * pointer comparison to remove duplicates.) * - * We assume we can destructively modify the input sublists. - * * Always assume the join type is JOIN_INNER; even if some of the * join clauses come from other contexts, that's not our problem. */ - allclauses = NIL; - foreach(l, clausegroups) - { - /* nconc okay here since same clause couldn't be in two sublists */ - allclauses = nconc(allclauses, (List *) lfirst(l)); - } allclauses = set_ptrUnion(rel->baserestrictinfo, allclauses); pathnode->rows = rel->tuples * restrictlist_selectivity(root, diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index 10eb050f3a3..a078b3f5a93 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.50 2003/05/28 22:32:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.51 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,6 +89,9 @@ create_or_index_paths(Query *root, RelOptInfo *rel) */ pathnode->path.pathkeys = NIL; + /* It's not an innerjoin path. */ + pathnode->indexjoinclauses = NIL; + /* We don't actually care what order the index scans in. */ pathnode->indexscandir = NoMovementScanDirection; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 08b5944a1dd..273a80129eb 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.144 2003/05/28 23:06:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.145 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -51,17 +51,11 @@ static SubqueryScan *create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses); static FunctionScan *create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses); -static NestLoop *create_nestloop_plan(Query *root, - NestPath *best_path, List *tlist, - List *joinclauses, List *otherclauses, +static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path, Plan *outer_plan, Plan *inner_plan); -static MergeJoin *create_mergejoin_plan(Query *root, - MergePath *best_path, List *tlist, - List *joinclauses, List *otherclauses, +static MergeJoin *create_mergejoin_plan(Query *root, MergePath *best_path, Plan *outer_plan, Plan *inner_plan); -static HashJoin *create_hashjoin_plan(Query *root, - HashPath *best_path, List *tlist, - List *joinclauses, List *otherclauses, +static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); static void fix_indxqual_references(List *indexquals, IndexPath *index_path, List **fixed_indexquals, @@ -356,59 +350,35 @@ disuse_physical_tlist(Plan *plan, Path *path) static Join * create_join_plan(Query *root, JoinPath *best_path) { - List *join_tlist = best_path->path.parent->targetlist; Plan *outer_plan; Plan *inner_plan; - List *joinclauses; - List *otherclauses; Join *plan; outer_plan = create_plan(root, best_path->outerjoinpath); inner_plan = create_plan(root, best_path->innerjoinpath); - if (IS_OUTER_JOIN(best_path->jointype)) - { - get_actual_join_clauses(best_path->joinrestrictinfo, - &joinclauses, &otherclauses); - } - else - { - /* We can treat all clauses alike for an inner join */ - joinclauses = get_actual_clauses(best_path->joinrestrictinfo); - otherclauses = NIL; - } - switch (best_path->path.pathtype) { case T_MergeJoin: plan = (Join *) create_mergejoin_plan(root, (MergePath *) best_path, - join_tlist, - joinclauses, - otherclauses, outer_plan, inner_plan); break; case T_HashJoin: plan = (Join *) create_hashjoin_plan(root, (HashPath *) best_path, - join_tlist, - joinclauses, - otherclauses, outer_plan, inner_plan); break; case T_NestLoop: plan = (Join *) create_nestloop_plan(root, (NestPath *) best_path, - join_tlist, - joinclauses, - otherclauses, outer_plan, inner_plan); break; default: - elog(ERROR, "create_join_plan: unknown node type: %d", + elog(ERROR, "unsupported node type %d", best_path->path.pathtype); plan = NULL; /* keep compiler quiet */ break; @@ -869,15 +839,16 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses) static NestLoop * create_nestloop_plan(Query *root, NestPath *best_path, - List *tlist, - List *joinclauses, - List *otherclauses, Plan *outer_plan, Plan *inner_plan) { + List *tlist = best_path->path.parent->targetlist; + List *joinrestrictclauses = best_path->joinrestrictinfo; + List *joinclauses; + List *otherclauses; NestLoop *join_plan; - if (IsA(inner_plan, IndexScan)) + if (IsA(best_path->innerjoinpath, IndexPath)) { /* * An index is being used to reduce the number of tuples scanned @@ -888,22 +859,41 @@ create_nestloop_plan(Query *root, * (otherwise, several different sets of clauses are being ORed * together). * - * Note we must compare against indxqualorig not the "fixed" indxqual - * (which has index attnos instead of relation attnos, and may have - * been commuted as well). + * We can also remove any join clauses that are redundant with those + * being used in the index scan; prior redundancy checks will not + * have caught this case because the join clauses would never have + * been put in the same joininfo list. + * + * This would be a waste of time if the indexpath was an ordinary + * indexpath and not a special innerjoin path. We will skip it in + * that case since indexjoinclauses is NIL in an ordinary indexpath. */ - IndexScan *innerscan = (IndexScan *) inner_plan; - List *indxqualorig = innerscan->indxqualorig; + IndexPath *innerpath = (IndexPath *) best_path->innerjoinpath; + List *indexjoinclauses = innerpath->indexjoinclauses; - if (length(indxqualorig) == 1) /* single indexscan? */ + if (length(indexjoinclauses) == 1) /* single indexscan? */ { - /* No work needed if indxqual refers only to its own relation... */ - if (NumRelids((Node *) indxqualorig) > 1) - joinclauses = set_difference(joinclauses, - lfirst(indxqualorig)); + joinrestrictclauses = + select_nonredundant_join_clauses(root, + joinrestrictclauses, + lfirst(indexjoinclauses), + best_path->jointype); } } + /* Get the join qual clauses (in plain expression form) */ + if (IS_OUTER_JOIN(best_path->jointype)) + { + get_actual_join_clauses(joinrestrictclauses, + &joinclauses, &otherclauses); + } + else + { + /* We can treat all clauses alike for an inner join */ + joinclauses = get_actual_clauses(joinrestrictclauses); + otherclauses = NIL; + } + join_plan = make_nestloop(tlist, joinclauses, otherclauses, @@ -919,15 +909,28 @@ create_nestloop_plan(Query *root, static MergeJoin * create_mergejoin_plan(Query *root, MergePath *best_path, - List *tlist, - List *joinclauses, - List *otherclauses, Plan *outer_plan, Plan *inner_plan) { + List *tlist = best_path->jpath.path.parent->targetlist; + List *joinclauses; + List *otherclauses; List *mergeclauses; MergeJoin *join_plan; + /* Get the join qual clauses (in plain expression form) */ + if (IS_OUTER_JOIN(best_path->jpath.jointype)) + { + get_actual_join_clauses(best_path->jpath.joinrestrictinfo, + &joinclauses, &otherclauses); + } + else + { + /* We can treat all clauses alike for an inner join */ + joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo); + otherclauses = NIL; + } + /* * Remove the mergeclauses from the list of join qual clauses, leaving * the list of quals that must be checked as qpquals. @@ -986,18 +989,31 @@ create_mergejoin_plan(Query *root, static HashJoin * create_hashjoin_plan(Query *root, HashPath *best_path, - List *tlist, - List *joinclauses, - List *otherclauses, Plan *outer_plan, Plan *inner_plan) { + List *tlist = best_path->jpath.path.parent->targetlist; + List *joinclauses; + List *otherclauses; List *hashclauses; HashJoin *join_plan; Hash *hash_plan; List *innerhashkeys; List *hcl; + /* Get the join qual clauses (in plain expression form) */ + if (IS_OUTER_JOIN(best_path->jpath.jointype)) + { + get_actual_join_clauses(best_path->jpath.joinrestrictinfo, + &joinclauses, &otherclauses); + } + else + { + /* We can treat all clauses alike for an inner join */ + joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo); + otherclauses = NIL; + } + /* * Remove the hashclauses from the list of join qual clauses, leaving * the list of quals that must be checked as qpquals. @@ -1056,13 +1072,9 @@ create_hashjoin_plan(Query *root, * Adjust indexqual clauses to the form the executor's indexqual * machinery needs, and check for recheckable (lossy) index conditions. * - * We have four tasks here: + * We have three tasks here: * * Index keys must be represented by Var nodes with varattno set to the * index's attribute number, not the attribute number in the original rel. - * * indxpath.c may have selected an index that is binary-compatible with - * the actual expression operator, but not exactly the same datatype. - * We must replace the expression's operator with the binary-compatible - * equivalent operator that the index will recognize. * * If the index key is on the right, commute the clause to put it on the * left. (Someday the executor might not need this, but for now it does.) * * If the indexable operator is marked 'amopreqcheck' in pg_amop, then diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 25648beed19..da9497d58a4 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.89 2003/05/26 00:11:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.90 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -362,6 +362,9 @@ create_index_path(Query *root, pathnode->indexinfo = makeList1(index); pathnode->indexqual = makeList1(indexquals); + /* It's not an innerjoin path. */ + pathnode->indexjoinclauses = NIL; + pathnode->indexscandir = indexscandir; /* diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index bdcc338d609..334fc5784cf 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.16 2003/01/24 03:58:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.17 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,12 @@ #include "optimizer/var.h" +static bool join_clause_is_redundant(Query *root, + RestrictInfo *rinfo, + List *reference_list, + JoinType jointype); + + /* * restriction_is_or_clause * @@ -94,6 +100,76 @@ get_actual_join_clauses(List *restrictinfo_list, * discussion). We detect that case and omit the redundant clause from the * result list. * + * The result is a fresh List, but it points to the same member nodes + * as were in the input. + */ +List * +remove_redundant_join_clauses(Query *root, List *restrictinfo_list, + JoinType jointype) +{ + List *result = NIL; + List *item; + + foreach(item, restrictinfo_list) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); + + /* drop it if redundant with any prior clause */ + if (join_clause_is_redundant(root, rinfo, result, jointype)) + continue; + + /* otherwise, add it to result list */ + result = lappend(result, rinfo); + } + + return result; +} + +/* + * select_nonredundant_join_clauses + * + * Given a list of RestrictInfo clauses that are to be applied in a join, + * select the ones that are not redundant with any clause in the + * reference_list. + * + * This is similar to remove_redundant_join_clauses, but we are looking for + * redundancies with a separate list of clauses (i.e., clauses that have + * already been applied below the join itself). + * + * Note that we assume the given restrictinfo_list has already been checked + * for local redundancies, so we don't check again. + */ +List * +select_nonredundant_join_clauses(Query *root, + List *restrictinfo_list, + List *reference_list, + JoinType jointype) +{ + List *result = NIL; + List *item; + + foreach(item, restrictinfo_list) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); + + /* drop it if redundant with any reference clause */ + if (join_clause_is_redundant(root, rinfo, reference_list, jointype)) + continue; + + /* otherwise, add it to result list */ + result = lappend(result, rinfo); + } + + return result; +} + +/* + * join_clause_is_redundant + * Returns true if rinfo is redundant with any clause in reference_list. + * + * This is the guts of both remove_redundant_join_clauses and + * select_nonredundant_join_clauses. See the docs above for motivation. + * * We can detect redundant mergejoinable clauses very cheaply by using their * left and right pathkeys, which uniquely identify the sets of equijoined * variables in question. All the members of a pathkey set that are in the @@ -113,69 +189,59 @@ get_actual_join_clauses(List *restrictinfo_list, * except one is pushed down into an outer join and the other isn't, * then they're not really redundant, because one constrains the * joined rows after addition of null fill rows, and the other doesn't. - * - * The result is a fresh List, but it points to the same member nodes - * as were in the input. */ -List * -remove_redundant_join_clauses(Query *root, List *restrictinfo_list, - JoinType jointype) +static bool +join_clause_is_redundant(Query *root, + RestrictInfo *rinfo, + List *reference_list, + JoinType jointype) { - List *result = NIL; - List *item; + /* always consider exact duplicates redundant */ + /* XXX would it be sufficient to use ptrMember here? */ + if (member(rinfo, reference_list)) + return true; - foreach(item, restrictinfo_list) + /* check for redundant merge clauses */ + if (rinfo->mergejoinoperator != InvalidOid) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); + bool redundant = false; + List *refitem; - /* always eliminate duplicates */ - if (member(rinfo, result)) - continue; + cache_mergeclause_pathkeys(root, rinfo); - /* check for redundant merge clauses */ - if (rinfo->mergejoinoperator != InvalidOid) + /* do the cheap tests first */ + foreach(refitem, reference_list) { - bool redundant = false; - List *olditem; + RestrictInfo *refrinfo = (RestrictInfo *) lfirst(refitem); - cache_mergeclause_pathkeys(root, rinfo); - - /* do the cheap tests first */ - foreach(olditem, result) + if (refrinfo->mergejoinoperator != InvalidOid && + rinfo->left_pathkey == refrinfo->left_pathkey && + rinfo->right_pathkey == refrinfo->right_pathkey && + (rinfo->ispusheddown == refrinfo->ispusheddown || + !IS_OUTER_JOIN(jointype))) { - RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem); - - if (oldrinfo->mergejoinoperator != InvalidOid && - rinfo->left_pathkey == oldrinfo->left_pathkey && - rinfo->right_pathkey == oldrinfo->right_pathkey && - (rinfo->ispusheddown == oldrinfo->ispusheddown || - !IS_OUTER_JOIN(jointype))) - { - redundant = true; - break; - } - } - - if (redundant) - { - /* - * It looks redundant, now check for "var = const" case. - * If left_relids/right_relids are set, then there are - * definitely vars on both sides; else we must check the - * hard way. - */ - if (rinfo->left_relids) - continue; /* var = var, so redundant */ - if (contain_var_clause(get_leftop(rinfo->clause)) && - contain_var_clause(get_rightop(rinfo->clause))) - continue; /* var = var, so redundant */ - /* else var = const, not redundant */ + redundant = true; + break; } } - /* otherwise, add it to result list */ - result = lappend(result, rinfo); + if (redundant) + { + /* + * It looks redundant, now check for "var = const" case. + * If left_relids/right_relids are set, then there are + * definitely vars on both sides; else we must check the + * hard way. + */ + if (rinfo->left_relids) + return true; /* var = var, so redundant */ + if (contain_var_clause(get_leftop(rinfo->clause)) && + contain_var_clause(get_rightop(rinfo->clause))) + return true; /* var = var, so redundant */ + /* else var = const, not redundant */ + } } - return result; + /* otherwise, not redundant */ + return false; } diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index bc42917a355..5bf17ceb14e 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_list.h,v 1.36 2003/05/28 22:32:50 tgl Exp $ + * $Id: pg_list.h,v 1.37 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -140,6 +140,8 @@ typedef struct FastList } FastList; #define FastListInit(fl) ( (fl)->head = (fl)->tail = NIL ) +#define FastListFromList(fl, l) \ + ( (fl)->head = (l), (fl)->tail = llastnode((fl)->head) ) #define FastListValue(fl) ( (fl)->head ) @@ -166,6 +168,7 @@ extern void FastConcFast(FastList *fl, FastList *fl2); extern void *nth(int n, List *l); extern int length(List *list); extern void *llast(List *list); +extern List *llastnode(List *list); extern bool member(void *datum, List *list); extern bool ptrMember(void *datum, List *list); extern bool intMember(int datum, List *list); diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 4f770b7ca10..8392ab505bd 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relation.h,v 1.80 2003/05/28 16:04:02 tgl Exp $ + * $Id: relation.h,v 1.81 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -340,6 +340,15 @@ typedef struct Path * Also note that indexquals lists do not contain RestrictInfo nodes, * just bare clause expressions. * + * 'indexjoinclauses' is NIL for an ordinary indexpath (one that does not + * use any join clauses in the index conditions). For an innerjoin indexpath, + * it has the same structure as 'indexqual', but references the RestrictInfo + * nodes from which the indexqual was built, rather than the bare clause + * expressions. (Note: there isn't necessarily a one-to-one correspondence + * between RestrictInfos and expressions, because of expansion of special + * indexable operators.) We need this so that we can eliminate redundant + * join clauses when plans are built. + * * 'indexscandir' is one of: * ForwardScanDirection: forward scan of an ordered index * BackwardScanDirection: backward scan of an ordered index @@ -360,6 +369,7 @@ typedef struct IndexPath Path path; List *indexinfo; List *indexqual; + List *indexjoinclauses; ScanDirection indexscandir; double rows; /* estimated number of result tuples */ } IndexPath; diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index 997d4ab8c52..19c3435c3e5 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: restrictinfo.h,v 1.16 2002/11/24 21:52:15 tgl Exp $ + * $Id: restrictinfo.h,v 1.17 2003/06/15 22:51:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,5 +23,9 @@ extern void get_actual_join_clauses(List *restrictinfo_list, extern List *remove_redundant_join_clauses(Query *root, List *restrictinfo_list, JoinType jointype); +extern List *select_nonredundant_join_clauses(Query *root, + List *restrictinfo_list, + List *reference_list, + JoinType jointype); #endif /* RESTRICTINFO_H */