aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/indxpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/indxpath.c')
-rw-r--r--src/backend/optimizer/path/indxpath.c776
1 files changed, 464 insertions, 312 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 2dab5e54cdd..ae5db3634ff 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.124 2002/11/01 19:33:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.125 2002/11/24 21:52:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -46,15 +46,11 @@
/*
* DoneMatchingIndexKeys() - MACRO
*
- * Determine whether we should continue matching index keys in a clause.
- * Depends on if there are more to match or if this is a functional index.
- * In the latter case we stop after the first match since there can
- * be only 1 key (i.e. the function's return value) and the attributes in
- * keys list represent the arguments to the function. -mer 3 Oct. 1991
+ * Formerly this looked at indexkeys, but that's the wrong thing for a
+ * functional index.
*/
-#define DoneMatchingIndexKeys(indexkeys, index) \
- (indexkeys[0] == 0 || \
- (index->indproc != InvalidOid))
+#define DoneMatchingIndexKeys(indexkeys, classes) \
+ (classes[0] == InvalidOid)
#define is_indexable_operator(clause,opclass,indexkey_on_left) \
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
@@ -68,28 +64,25 @@ static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
IndexOptInfo *index,
Expr *clause);
-static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index,
- int *indexkeys, Oid *classes,
- List *restrictinfo_list);
-static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel,
+static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);
+static List *group_clauses_by_indexkey_for_join(RelOptInfo *rel,
IndexOptInfo *index,
- int *indexkeys, Oid *classes,
- List *join_cinfo_list,
- List *restr_cinfo_list);
+ Relids outer_relids,
+ bool isouterjoin);
static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
- int indexkey, Oid opclass,
- Expr *clause, bool join);
+ int indexkey, Oid opclass, Expr *clause);
+static bool match_join_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
+ int indexkey, Oid opclass, Expr *clause);
static bool pred_test(List *predicate_list, List *restrictinfo_list,
List *joininfo_list, int relvarno);
static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);
static bool pred_test_recurse_clause(Expr *predicate, Node *clause);
static bool pred_test_recurse_pred(Expr *predicate, Node *clause);
static bool pred_test_simple_clause(Expr *predicate, Node *clause);
-static void indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
- List *joininfo_list, List *restrictinfo_list,
- List **clausegroups, List **outerrelids);
-static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
- List *clausegroup_list, List *outerrelids_list);
+static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
+static Path *make_innerjoin_index_path(Query *root,
+ RelOptInfo *rel, IndexOptInfo *index,
+ List *clausegroup);
static bool match_index_to_operand(int indexkey, Var *operand,
RelOptInfo *rel, IndexOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
@@ -108,7 +101,6 @@ static Const *string_to_const(const char *str, Oid datatype);
* create_index_paths()
* Generate all interesting index paths for the given relation.
* Candidate paths are added to the rel's pathlist (using add_path).
- * Additional IndexPath nodes may also be added to rel's innerjoin list.
*
* To be considered for an index scan, an index must match one or more
* restriction clauses or join clauses from the query's qual condition,
@@ -123,12 +115,14 @@ static Const *string_to_const(const char *str, Oid datatype);
* in its join clauses. In that context, values for the other rels'
* attributes are available and fixed during any one scan of the indexpath.
*
- * An IndexPath is generated and submitted to add_path() for each index
- * this routine deems potentially interesting for the current query.
- * An innerjoin path is also generated for each interesting combination of
- * outer join relations. The innerjoin paths are *not* passed to add_path(),
- * but are appended to the "innerjoin" list of the relation for later
- * consideration in nested-loop joins.
+ * An IndexPath is generated and submitted to add_path() for each plain index
+ * scan this routine deems potentially interesting for the current query.
+ *
+ * We also determine the set of other relids that participate in join
+ * clauses that could be used with each index. The actually best innerjoin
+ * path will be generated for each outer relation later on, but knowing the
+ * set of potential otherrels allows us to identify equivalent outer relations
+ * and avoid repeated computation.
*
* 'rel' is the relation for which we want to generate index paths
*/
@@ -137,6 +131,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
{
List *restrictinfo_list = rel->baserestrictinfo;
List *joininfo_list = rel->joininfo;
+ Relids all_join_outerrelids = NIL;
List *ilist;
foreach(ilist, rel->indexlist)
@@ -146,8 +141,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
List *index_pathkeys;
List *useful_pathkeys;
bool index_is_ordered;
- List *joinclausegroups;
- List *joinouterrelids;
+ Relids join_outerrelids;
/*
* If this is a partial index, we can only use it if it passes the
@@ -178,11 +172,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
/*
* 2. Match the index against non-'or' restriction clauses.
*/
- restrictclauses = group_clauses_by_indexkey(rel,
- index,
- index->indexkeys,
- index->classlist,
- restrictinfo_list);
+ restrictclauses = group_clauses_by_indexkey(rel, index);
/*
* 3. Compute pathkeys describing index's ordering, if any, then
@@ -234,26 +224,19 @@ create_index_paths(Query *root, RelOptInfo *rel)
}
/*
- * 6. Create an innerjoin index path for each combination of other
- * rels used in available join clauses. These paths will be
- * considered as the inner side of nestloop joins against those
- * sets of other rels. indexable_joinclauses() finds sets of
- * clauses that can be used with each combination of outer rels,
- * and index_innerjoin builds the paths themselves. We add the
- * paths to the rel's innerjoin list, NOT to the result list.
+ * 6. Examine join clauses to see which ones are potentially
+ * usable with this index, and generate a list of all other relids
+ * that participate in such join clauses. We'll use this list later
+ * to recognize outer rels that are equivalent for joining purposes.
+ * We compute both per-index and overall-for-relation lists.
*/
- indexable_joinclauses(rel, index,
- joininfo_list, restrictinfo_list,
- &joinclausegroups,
- &joinouterrelids);
- if (joinclausegroups != NIL)
- {
- rel->innerjoin = nconc(rel->innerjoin,
- index_innerjoin(root, rel, index,
- joinclausegroups,
- joinouterrelids));
- }
+ join_outerrelids = indexable_outerrelids(rel, index);
+ index->outer_relids = join_outerrelids;
+ all_join_outerrelids = set_unioni(all_join_outerrelids,
+ join_outerrelids);
}
+
+ rel->index_outer_relids = all_join_outerrelids;
}
@@ -397,14 +380,14 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
foreach(item, clause->args)
{
if (match_clause_to_indexkey(rel, index, indexkey, opclass,
- lfirst(item), false))
+ lfirst(item)))
return true;
}
return false;
}
else
return match_clause_to_indexkey(rel, index, indexkey, opclass,
- clause, false);
+ clause);
}
/*----------
@@ -466,13 +449,13 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
if (match_clause_to_indexkey(rel, index,
curIndxKey, curClass,
- subsubclause, false))
+ subsubclause))
clausegroup = lappend(clausegroup, subsubclause);
}
}
else if (match_clause_to_indexkey(rel, index,
curIndxKey, curClass,
- orsubclause, false))
+ orsubclause))
clausegroup = makeList1(orsubclause);
/*
@@ -487,7 +470,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
if (match_clause_to_indexkey(rel, index,
curIndxKey, curClass,
- rinfo->clause, false))
+ rinfo->clause))
clausegroup = lappend(clausegroup, rinfo->clause);
}
}
@@ -503,7 +486,8 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
indexkeys++;
classes++;
- } while (!DoneMatchingIndexKeys(indexkeys, index));
+
+ } while (!DoneMatchingIndexKeys(indexkeys, classes));
if (quals == NIL)
elog(ERROR, "extract_or_indexqual_conditions: no matching clause");
@@ -523,15 +507,12 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
*
* 'rel' is the node of the relation itself.
* 'index' is a index on 'rel'.
- * 'indexkeys' are the index keys to be matched.
- * 'classes' are the classes of the index operators on those keys.
- * 'restrictinfo_list' is the list of available restriction clauses for 'rel'.
*
* Returns a list of all the RestrictInfo nodes for clauses that can be
* used with this index.
*
* The list is ordered by index key. (This is not depended on by any part
- * of the planner, as far as I can tell; but some parts of the executor
+ * of the planner, so far as I can tell; but some parts of the executor
* do assume that the indxqual list ultimately delivered to the executor
* is so ordered. One such place is _bt_orderkeys() in the btree support.
* Perhaps that ought to be fixed someday --- tgl 7/00)
@@ -544,15 +525,14 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
* clauses matching column C, because the executor couldn't use them anyway.
*/
static List *
-group_clauses_by_indexkey(RelOptInfo *rel,
- IndexOptInfo *index,
- int *indexkeys,
- Oid *classes,
- List *restrictinfo_list)
+group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
{
List *clausegroup_list = NIL;
+ List *restrictinfo_list = rel->baserestrictinfo;
+ int *indexkeys = index->indexkeys;
+ Oid *classes = index->classlist;
- if (restrictinfo_list == NIL || indexkeys[0] == 0)
+ if (restrictinfo_list == NIL)
return NIL;
do
@@ -560,18 +540,17 @@ group_clauses_by_indexkey(RelOptInfo *rel,
int curIndxKey = indexkeys[0];
Oid curClass = classes[0];
List *clausegroup = NIL;
- List *curCinfo;
+ List *i;
- foreach(curCinfo, restrictinfo_list)
+ foreach(i, restrictinfo_list)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(curCinfo);
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
if (match_clause_to_indexkey(rel,
index,
curIndxKey,
curClass,
- rinfo->clause,
- false))
+ rinfo->clause))
clausegroup = lappend(clausegroup, rinfo);
}
@@ -587,71 +566,84 @@ group_clauses_by_indexkey(RelOptInfo *rel,
indexkeys++;
classes++;
- } while (!DoneMatchingIndexKeys(indexkeys, index));
+ } while (!DoneMatchingIndexKeys(indexkeys, classes));
/* clausegroup_list holds all matched clauses ordered by indexkeys */
return clausegroup_list;
}
/*
- * group_clauses_by_ikey_for_joins
- * Generates a list of join clauses that can be used with an index
+ * group_clauses_by_indexkey_for_join
+ * Generates a list of clauses that can be used with an index
* to scan the inner side of a nestloop join.
*
* This is much like group_clauses_by_indexkey(), but we consider both
- * join and restriction clauses. For each indexkey in the index, we
- * accept both join and restriction clauses that match it, since both
- * will make useful indexquals if the index is being used to scan the
- * inner side of a nestloop join. But there must be at least one matching
- * join clause, or we return NIL indicating that this index isn't useful
- * for nestloop joining.
+ * join and restriction clauses. Any joinclause that uses only otherrels
+ * in the specified outer_relids is fair game. But there must be at least
+ * one such joinclause in the final list, otherwise we return NIL indicating
+ * that this index isn't interesting as an inner indexscan. (A scan using
+ * only restriction clauses shouldn't be created here, because a regular Path
+ * will already have been generated for it.)
*/
static List *
-group_clauses_by_ikey_for_joins(RelOptInfo *rel,
- IndexOptInfo *index,
- int *indexkeys,
- Oid *classes,
- List *join_cinfo_list,
- List *restr_cinfo_list)
+group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
+ Relids outer_relids, bool isouterjoin)
{
List *clausegroup_list = NIL;
bool jfound = false;
-
- if (join_cinfo_list == NIL || indexkeys[0] == 0)
- return NIL;
+ int *indexkeys = index->indexkeys;
+ Oid *classes = index->classlist;
do
{
int curIndxKey = indexkeys[0];
Oid curClass = classes[0];
List *clausegroup = NIL;
- List *curCinfo;
+ List *i;
- foreach(curCinfo, join_cinfo_list)
+ /* Look for joinclauses that are usable with given outer_relids */
+ foreach(i, rel->joininfo)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(curCinfo);
+ JoinInfo *joininfo = (JoinInfo *) lfirst(i);
+ List *j;
- if (match_clause_to_indexkey(rel,
- index,
- curIndxKey,
- curClass,
- rinfo->clause,
- true))
+ if (!is_subseti(joininfo->unjoined_relids, outer_relids))
+ continue;
+
+ foreach(j, joininfo->jinfo_restrictinfo)
{
- clausegroup = lappend(clausegroup, rinfo);
- jfound = true;
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
+
+ /* Can't use pushed-down clauses in outer join */
+ if (isouterjoin && rinfo->ispusheddown)
+ continue;
+
+ if (match_join_clause_to_indexkey(rel,
+ index,
+ curIndxKey,
+ curClass,
+ rinfo->clause))
+ {
+ clausegroup = lappend(clausegroup, rinfo);
+ jfound = true;
+ }
}
}
- foreach(curCinfo, restr_cinfo_list)
+
+ /* We can also use plain restriction clauses for the rel */
+ foreach(i, rel->baserestrictinfo)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(curCinfo);
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
+
+ /* Can't use pushed-down clauses in outer join */
+ if (isouterjoin && rinfo->ispusheddown)
+ continue;
if (match_clause_to_indexkey(rel,
index,
curIndxKey,
curClass,
- rinfo->clause,
- false))
+ rinfo->clause))
clausegroup = lappend(clausegroup, rinfo);
}
@@ -667,11 +659,10 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
indexkeys++;
classes++;
- } while (!DoneMatchingIndexKeys(indexkeys, index));
+ } while (!DoneMatchingIndexKeys(indexkeys, classes));
/*
- * if no join clause was matched then there ain't clauses for joins at
- * all.
+ * if no join clause was matched then forget it, per comments above.
*/
if (!jfound)
{
@@ -686,16 +677,12 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
/*
* match_clause_to_indexkey()
- * Determines whether a restriction or join clause matches
- * a key of an index.
+ * Determines whether a restriction clause matches a key of an index.
*
* To match, the clause:
*
- * (1a) for a restriction clause: must be in the form (indexkey op const)
- * or (const op indexkey), or
- * (1b) for a join clause: must be in the form (indexkey op others)
- * or (others op indexkey), where others is an expression involving
- * only vars of the other relation(s); and
+ * (1) must be in the form (indexkey op const) or (const op indexkey);
+ * and
* (2) must contain an operator which is in the same class as the index
* operator for this key, or is a "special" operator as recognized
* by match_special_index_operator().
@@ -706,18 +693,11 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
* We do not actually do the commuting here, but we check whether a
* suitable commutator operator is available.
*
- * Note that in the join case, we already know that the clause as a
- * whole uses vars from the interesting set of relations. But we need
- * to defend against expressions like (a.f1 OP (b.f2 OP a.f3)); that's
- * not processable by an indexscan nestloop join, whereas
- * (a.f1 OP (b.f2 OP c.f3)) is.
- *
* 'rel' is the relation of interest.
* 'index' is an index on 'rel'.
* 'indexkey' is a key of 'index'.
* 'opclass' is the corresponding operator class.
* 'clause' is the clause to be tested.
- * 'join' is true if we are considering this clause for joins.
*
* Returns true if the clause can be used with this index key.
*
@@ -729,8 +709,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
IndexOptInfo *index,
int indexkey,
Oid opclass,
- Expr *clause,
- bool join)
+ Expr *clause)
{
Var *leftop,
*rightop;
@@ -743,75 +722,124 @@ match_clause_to_indexkey(RelOptInfo *rel,
if (!leftop || !rightop)
return false;
- if (!join)
+ /*
+ * Check for clauses of the form:
+ * (indexkey operator constant) or (constant operator indexkey).
+ * Anything that is a "pseudo constant" expression will do.
+ */
+ if (match_index_to_operand(indexkey, leftop, rel, index) &&
+ is_pseudo_constant_clause((Node *) rightop))
{
+ if (is_indexable_operator(clause, opclass, true))
+ return true;
+
/*
- * Not considering joins, so check for clauses of the form:
- * (indexkey operator constant) or (constant operator indexkey).
- * Anything that is a "pseudo constant" expression will do.
+ * If we didn't find a member of the index's opclass, see
+ * whether it is a "special" indexable operator.
*/
+ if (match_special_index_operator(clause, opclass, true))
+ return true;
+ return false;
+ }
- if (match_index_to_operand(indexkey, leftop, rel, index) &&
- is_pseudo_constant_clause((Node *) rightop))
- {
- if (is_indexable_operator(clause, opclass, true))
- return true;
+ if (match_index_to_operand(indexkey, rightop, rel, index) &&
+ is_pseudo_constant_clause((Node *) leftop))
+ {
+ if (is_indexable_operator(clause, opclass, false))
+ return true;
- /*
- * If we didn't find a member of the index's opclass, see
- * whether it is a "special" indexable operator.
- */
- if (match_special_index_operator(clause, opclass, true))
- return true;
- return false;
- }
- if (match_index_to_operand(indexkey, rightop, rel, index) &&
- is_pseudo_constant_clause((Node *) leftop))
- {
- if (is_indexable_operator(clause, opclass, false))
- return true;
+ /*
+ * If we didn't find a member of the index's opclass, see
+ * whether it is a "special" indexable operator.
+ */
+ if (match_special_index_operator(clause, opclass, false))
+ return true;
+ return false;
+ }
- /*
- * If we didn't find a member of the index's opclass, see
- * whether it is a "special" indexable operator.
- */
- if (match_special_index_operator(clause, opclass, false))
- return true;
- return false;
- }
+ return false;
+}
+
+/*
+ * match_join_clause_to_indexkey()
+ * Determines whether a join clause matches a key of an index.
+ *
+ * To match, the clause:
+ *
+ * (1) must be in the form (indexkey op others) or (others op indexkey),
+ * where others is an expression involving only vars of the other
+ * relation(s); and
+ * (2) must contain an operator which is in the same class as the index
+ * operator for this key, or is a "special" operator as recognized
+ * by match_special_index_operator().
+ *
+ * As above, we must be able to commute the clause to put the indexkey
+ * on the left.
+ *
+ * Note that we already know that the clause as a whole uses vars from
+ * the interesting set of relations. But we need to defend against
+ * expressions like (a.f1 OP (b.f2 OP a.f3)); that's not processable by
+ * an indexscan nestloop join, whereas (a.f1 OP (b.f2 OP c.f3)) is.
+ *
+ * 'rel' is the relation of interest.
+ * 'index' is an index on 'rel'.
+ * 'indexkey' is a key of 'index'.
+ * 'opclass' is the corresponding operator class.
+ * 'clause' is the clause to be tested.
+ *
+ * Returns true if the clause can be used with this index key.
+ *
+ * NOTE: returns false if clause is an OR or AND clause; it is the
+ * responsibility of higher-level routines to cope with those.
+ */
+static bool
+match_join_clause_to_indexkey(RelOptInfo *rel,
+ IndexOptInfo *index,
+ int indexkey,
+ Oid opclass,
+ Expr *clause)
+{
+ Var *leftop,
+ *rightop;
+
+ /* Clause must be a binary opclause. */
+ if (!is_opclause((Node *) clause))
+ return false;
+ leftop = get_leftop(clause);
+ rightop = get_rightop(clause);
+ if (!leftop || !rightop)
+ return false;
+
+ /*
+ * Check for an indexqual that could be handled by a nestloop
+ * join. We need the index key to be compared against an
+ * expression that uses none of the indexed relation's vars and
+ * contains no volatile functions.
+ */
+ if (match_index_to_operand(indexkey, leftop, rel, index))
+ {
+ List *othervarnos = pull_varnos((Node *) rightop);
+ bool isIndexable;
+
+ isIndexable =
+ !intMember(lfirsti(rel->relids), othervarnos) &&
+ !contain_volatile_functions((Node *) rightop) &&
+ is_indexable_operator(clause, opclass, true);
+ freeList(othervarnos);
+ return isIndexable;
}
- else
+
+ if (match_index_to_operand(indexkey, rightop, rel, index))
{
- /*
- * Check for an indexqual that could be handled by a nestloop
- * join. We need the index key to be compared against an
- * expression that uses none of the indexed relation's vars and
- * contains no volatile functions.
- */
- if (match_index_to_operand(indexkey, leftop, rel, index))
- {
- List *othervarnos = pull_varnos((Node *) rightop);
- bool isIndexable;
-
- isIndexable =
- !intMember(lfirsti(rel->relids), othervarnos) &&
- !contain_volatile_functions((Node *) rightop) &&
- is_indexable_operator(clause, opclass, true);
- freeList(othervarnos);
- return isIndexable;
- }
- else if (match_index_to_operand(indexkey, rightop, rel, index))
- {
- List *othervarnos = pull_varnos((Node *) leftop);
- bool isIndexable;
-
- isIndexable =
- !intMember(lfirsti(rel->relids), othervarnos) &&
- !contain_volatile_functions((Node *) leftop) &&
- is_indexable_operator(clause, opclass, false);
- freeList(othervarnos);
- return isIndexable;
- }
+ List *othervarnos = pull_varnos((Node *) leftop);
+ bool isIndexable;
+
+ isIndexable =
+ !intMember(lfirsti(rel->relids), othervarnos) &&
+ !contain_volatile_functions((Node *) leftop) &&
+ is_indexable_operator(clause, opclass, false);
+ freeList(othervarnos);
+ return isIndexable;
}
return false;
@@ -1267,164 +1295,288 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
****************************************************************************/
/*
- * indexable_joinclauses
- * Finds all groups of join clauses from among 'joininfo_list' that can
- * be used in conjunction with 'index' for the inner scan of a nestjoin.
- *
- * Each clause group comes from a single joininfo node plus the current
- * rel's restrictinfo list. Therefore, every clause in the group references
- * the current rel plus the same set of other rels (except for the restrict
- * clauses, which only reference the current rel). Therefore, this set
- * of clauses could be used as an indexqual if the relation is scanned
- * as the inner side of a nestloop join when the outer side contains
- * (at least) all those "other rels".
- *
- * XXX Actually, given that we are considering a join that requires an
- * outer rel set (A,B,C), we should use all qual clauses that reference
- * any subset of these rels, not just the full set or none. This is
- * doable with a doubly nested loop over joininfo_list; is it worth it?
- *
- * Returns two parallel lists of the same length: the clause groups,
- * and the required outer rel set for each one.
+ * indexable_outerrelids
+ * Finds all other relids that participate in any indexable join clause
+ * for the specified index. Returns a list of relids.
*
* 'rel' is the relation for which 'index' is defined
- * 'joininfo_list' is the list of JoinInfo nodes for 'rel'
- * 'restrictinfo_list' is the list of restriction clauses for 'rel'
- * '*clausegroups' receives a list of clause sublists
- * '*outerrelids' receives a list of relid lists
*/
-static void
-indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
- List *joininfo_list, List *restrictinfo_list,
- List **clausegroups, List **outerrelids)
+static Relids
+indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index)
{
- List *cg_list = NIL;
- List *relid_list = NIL;
+ Relids outer_relids = NIL;
List *i;
- foreach(i, joininfo_list)
+ foreach(i, rel->joininfo)
{
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
- List *clausegroup;
+ bool match_found = false;
+ List *j;
+
+ /*
+ * Examine each joinclause in the JoinInfo node's list to see if
+ * it matches any key of the index. If so, add the JoinInfo's
+ * otherrels to the result. We can skip examining other joinclauses
+ * in the same list as soon as we find a match (since by definition
+ * they all have the same otherrels).
+ */
+ foreach(j, joininfo->jinfo_restrictinfo)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
+ Expr *clause = rinfo->clause;
+ int *indexkeys = index->indexkeys;
+ Oid *classes = index->classlist;
+
+ do
+ {
+ int curIndxKey = indexkeys[0];
+ Oid curClass = classes[0];
+
+ if (match_join_clause_to_indexkey(rel,
+ index,
+ curIndxKey,
+ curClass,
+ clause))
+ {
+ match_found = true;
+ break;
+ }
+
+ indexkeys++;
+ classes++;
- clausegroup = group_clauses_by_ikey_for_joins(rel,
- index,
- index->indexkeys,
- index->classlist,
- joininfo->jinfo_restrictinfo,
- restrictinfo_list);
+ } while (!DoneMatchingIndexKeys(indexkeys, classes));
- if (clausegroup != NIL)
+ if (match_found)
+ break;
+ }
+
+ if (match_found)
{
- cg_list = lappend(cg_list, clausegroup);
- relid_list = lappend(relid_list, joininfo->unjoined_relids);
+ outer_relids = set_unioni(outer_relids,
+ joininfo->unjoined_relids);
}
}
- *clausegroups = cg_list;
- *outerrelids = relid_list;
+ return outer_relids;
}
-/****************************************************************************
- * ---- PATH CREATION UTILITIES ----
- ****************************************************************************/
-
/*
- * index_innerjoin
- * Creates index path nodes corresponding to paths to be used as inner
- * relations in nestloop joins.
- *
- * 'rel' is the relation for which 'index' is defined
- * 'clausegroup_list' is a list of lists of restrictinfo nodes which can use
- * 'index'. Each sublist refers to the same set of outer rels.
- * 'outerrelids_list' is a list of the required outer rels for each sublist
- * of join clauses.
+ * best_inner_indexscan
+ * Finds the best available inner indexscan for a nestloop join
+ * with the given rel on the inside and the given outer_relids outside.
+ * May return NULL if there are no possible inner indexscans.
*
- * Returns a list of index pathnodes.
+ * We ignore ordering considerations (since a nestloop's inner scan's order
+ * is uninteresting). Also, we consider only total cost when deciding which
+ * of two possible paths is better --- this assumes that all indexpaths have
+ * negligible startup cost. (True today, but someday we might have to think
+ * harder.) Therefore, there is only one dimension of comparison and so it's
+ * sufficient to return a single "best" path.
*/
-static List *
-index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
- List *clausegroup_list, List *outerrelids_list)
+Path *
+best_inner_indexscan(Query *root, RelOptInfo *rel,
+ Relids outer_relids, JoinType jointype)
{
- List *path_list = NIL;
- List *i;
+ Path *cheapest = NULL;
+ bool isouterjoin;
+ List *ilist;
+ List *jlist;
+ InnerIndexscanInfo *info;
- foreach(i, clausegroup_list)
+ /*
+ * Nestloop only supports inner and left joins.
+ */
+ switch (jointype)
{
- List *clausegroup = lfirst(i);
- IndexPath *pathnode = makeNode(IndexPath);
- List *indexquals = NIL;
- bool alljoinquals = true;
- List *temp;
-
- /* XXX this code ought to be merged with create_index_path? */
-
- pathnode->path.pathtype = T_IndexScan;
- pathnode->path.parent = rel;
+ case JOIN_INNER:
+ isouterjoin = false;
+ break;
+ case JOIN_LEFT:
+ isouterjoin = true;
+ break;
+ default:
+ return NULL;
+ }
+ /*
+ * If there are no indexable joinclauses for this rel, exit quickly.
+ * Otherwise, intersect the given outer_relids with index_outer_relids
+ * to find the set of outer relids actually relevant for this index.
+ * If there are none, again we can fail immediately.
+ */
+ if (!rel->index_outer_relids)
+ return NULL;
+ outer_relids = set_intersecti(rel->index_outer_relids, outer_relids);
+ if (!outer_relids)
+ return NULL;
+ /*
+ * Look to see if we already computed the result for this set of
+ * relevant outerrels. (We include the isouterjoin status in the
+ * cache lookup key for safety. In practice I suspect this is not
+ * necessary because it should always be the same for a given innerrel.)
+ */
+ foreach(jlist, rel->index_inner_paths)
+ {
+ info = (InnerIndexscanInfo *) lfirst(jlist);
+ if (sameseti(info->other_relids, outer_relids) &&
+ info->isouterjoin == isouterjoin)
+ {
+ freeList(outer_relids);
+ return info->best_innerpath;
+ }
+ }
+ /*
+ * For each index of the rel, find the best path; then choose the
+ * best overall. We cache the per-index results as well as the overall
+ * result. (This is useful because different indexes may have different
+ * relevant outerrel sets, so different overall outerrel sets might still
+ * map to the same computation for a given index.)
+ */
+ foreach(ilist, rel->indexlist)
+ {
+ IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
+ Relids index_outer_relids;
+ Path *path = NULL;
+
+ /* skip quickly if index has no useful join clauses */
+ if (!index->outer_relids)
+ continue;
+ /* identify set of relevant outer relids for this index */
+ index_outer_relids = set_intersecti(index->outer_relids, outer_relids);
+ if (!index_outer_relids)
+ continue;
/*
- * There's no point in marking the path with any pathkeys, since
- * it will only ever be used as the inner path of a nestloop, and
- * so its ordering does not matter.
+ * Look to see if we already computed the result for this index.
*/
- pathnode->path.pathkeys = NIL;
+ foreach(jlist, index->inner_paths)
+ {
+ info = (InnerIndexscanInfo *) lfirst(jlist);
+ if (sameseti(info->other_relids, index_outer_relids) &&
+ info->isouterjoin == isouterjoin)
+ {
+ path = info->best_innerpath;
+ freeList(index_outer_relids); /* not needed anymore */
+ break;
+ }
+ }
- /* extract bare indexqual clauses, check whether all from JOIN/ON */
- foreach(temp, clausegroup)
+ if (jlist == NIL) /* failed to find a match? */
{
- RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
+ List *clausegroup;
+
+ /* find useful clauses for this index and outerjoin set */
+ clausegroup = group_clauses_by_indexkey_for_join(rel,
+ index,
+ index_outer_relids,
+ isouterjoin);
+ if (clausegroup)
+ {
+ /* remove duplicate and redundant clauses */
+ clausegroup = remove_redundant_join_clauses(root,
+ clausegroup,
+ jointype);
+ /* make the path */
+ path = make_innerjoin_index_path(root, rel, index,
+ clausegroup);
+ }
- indexquals = lappend(indexquals, clause->clause);
- if (clause->ispusheddown)
- alljoinquals = false;
+ /* Cache the result --- whether positive or negative */
+ info = makeNode(InnerIndexscanInfo);
+ info->other_relids = index_outer_relids;
+ info->isouterjoin = isouterjoin;
+ info->best_innerpath = path;
+ index->inner_paths = lcons(info, index->inner_paths);
}
- /* expand special operators to indexquals the executor can handle */
- indexquals = expand_indexqual_conditions(indexquals);
+ if (path != NULL &&
+ (cheapest == NULL ||
+ compare_path_costs(path, cheapest, TOTAL_COST) < 0))
+ cheapest = path;
+ }
- /*
- * Note that we are making a pathnode for a single-scan indexscan;
- * therefore, both indexinfo and indexqual should be
- * single-element lists.
- */
- pathnode->indexinfo = makeList1(index);
- pathnode->indexqual = makeList1(indexquals);
+ /* Cache the result --- whether positive or negative */
+ info = makeNode(InnerIndexscanInfo);
+ info->other_relids = outer_relids;
+ info->isouterjoin = isouterjoin;
+ info->best_innerpath = cheapest;
+ rel->index_inner_paths = lcons(info, rel->index_inner_paths);
- /* We don't actually care what order the index scans in ... */
- pathnode->indexscandir = NoMovementScanDirection;
+ return cheapest;
+}
- /* joinrelids saves the rels needed on the outer side of the join */
- pathnode->joinrelids = lfirst(outerrelids_list);
+/****************************************************************************
+ * ---- PATH CREATION UTILITIES ----
+ ****************************************************************************/
- pathnode->alljoinquals = alljoinquals;
+/*
+ * make_innerjoin_index_path
+ * Create an index path node for a path to be used as an inner
+ * relation in a nestloop join.
+ *
+ * 'rel' is the relation for which 'index' is defined
+ * 'clausegroup' is a list of restrictinfo nodes that can use 'index'
+ */
+static Path *
+make_innerjoin_index_path(Query *root,
+ RelOptInfo *rel, IndexOptInfo *index,
+ List *clausegroup)
+{
+ IndexPath *pathnode = makeNode(IndexPath);
+ List *indexquals;
- /*
- * We must compute the estimated number of output rows for the
- * indexscan. This is less than rel->rows because of the
- * additional selectivity of the join clauses. Since clausegroup
- * may contain both restriction and join clauses, we have to do a
- * set union to get the full set of clauses that must be
- * considered to compute the correct selectivity. (We can't just
- * nconc the two lists; then we might have some restriction
- * clauses appearing twice, which'd mislead
- * restrictlist_selectivity into double-counting their
- * selectivity.)
- */
- pathnode->rows = rel->tuples *
- restrictlist_selectivity(root,
- set_union(rel->baserestrictinfo,
- clausegroup),
- lfirsti(rel->relids));
- /* Like costsize.c, force estimate to be at least one row */
- if (pathnode->rows < 1.0)
- pathnode->rows = 1.0;
-
- cost_index(&pathnode->path, root, rel, index, indexquals, true);
-
- path_list = lappend(path_list, pathnode);
- outerrelids_list = lnext(outerrelids_list);
- }
- return path_list;
+ /* XXX this code ought to be merged with create_index_path? */
+
+ pathnode->path.pathtype = T_IndexScan;
+ pathnode->path.parent = rel;
+
+ /*
+ * There's no point in marking the path with any pathkeys, since
+ * it will only ever be used as the inner path of a nestloop, and
+ * so its ordering does not matter.
+ */
+ pathnode->path.pathkeys = NIL;
+
+ /* Extract bare indexqual clauses from restrictinfos */
+ indexquals = get_actual_clauses(clausegroup);
+
+ /* expand special operators to indexquals the executor can handle */
+ indexquals = expand_indexqual_conditions(indexquals);
+
+ /*
+ * Note that we are making a pathnode for a single-scan indexscan;
+ * therefore, both indexinfo and indexqual should be single-element lists.
+ */
+ pathnode->indexinfo = makeList1(index);
+ pathnode->indexqual = makeList1(indexquals);
+
+ /* We don't actually care what order the index scans in ... */
+ pathnode->indexscandir = NoMovementScanDirection;
+
+ /*
+ * We must compute the estimated number of output rows for the
+ * indexscan. This is less than rel->rows because of the
+ * additional selectivity of the join clauses. Since clausegroup
+ * may contain both restriction and join clauses, we have to do a
+ * set union to get the full set of clauses that must be
+ * considered to compute the correct selectivity. (We can't just
+ * nconc the two lists; then we might have some restriction
+ * clauses appearing twice, which'd mislead
+ * restrictlist_selectivity into double-counting their
+ * selectivity.)
+ */
+ pathnode->rows = rel->tuples *
+ restrictlist_selectivity(root,
+ set_union(rel->baserestrictinfo,
+ clausegroup),
+ lfirsti(rel->relids));
+ /* Like costsize.c, force estimate to be at least one row */
+ if (pathnode->rows < 1.0)
+ pathnode->rows = 1.0;
+
+ cost_index(&pathnode->path, root, rel, index, indexquals, true);
+
+ return (Path *) pathnode;
}
/****************************************************************************