*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.66 1999/07/27 03:51:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.67 1999/07/30 04:07:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List **clausegroups, List **outerrelids);
static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
List *clausegroup_list, List *outerrelids_list);
-static List *create_index_path_group(Query *root, RelOptInfo *rel, RelOptInfo *index,
- List *clausegroup_list, bool join);
+static bool useful_for_mergejoin(RelOptInfo *index, List *clausegroup_list);
static bool match_index_to_operand(int indexkey, Expr *operand,
RelOptInfo *rel, RelOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
* create_index_paths()
* Generate all interesting index paths for the given relation.
*
- * 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.
+ * 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.
*
- * Note: an index scan might also be used simply to order the result,
- * either for use in a mergejoin or to satisfy an ORDER BY request.
- * That possibility is handled elsewhere.
+ * There are two basic kinds of index scans. A "plain" index scan uses
+ * only restriction clauses (possibly none at all) in its indexqual,
+ * so it can be applied in any context. An "innerjoin" index scan uses
+ * join clauses (plus restriction clauses, if available) in its indexqual.
+ * Therefore it can only be used as the inner relation of a nestloop
+ * join against an outer rel that includes all the other rels mentioned
+ * in its join clauses. In that context, values for the other rels'
+ * attributes are available and fixed during any one scan of the indexpath.
+ *
+ * This routine's return value is a list of plain IndexPaths for each
+ * index the routine deems potentially interesting for the current query
+ * (at most one IndexPath per index on the given relation). An innerjoin
+ * path is also generated for each interesting combination of outer join
+ * relations. The innerjoin paths are *not* in the return list, but are
+ * appended to the "innerjoin" list of the relation itself.
+ *
+ * XXX An index scan might also be used simply to order the result. We
+ * probably should create an index path for any index that matches the
+ * query's ORDER BY condition, even if it doesn't seem useful for join
+ * or restriction clauses. But currently, such a path would never
+ * survive the path selection process, so there's no point. The selection
+ * process needs to award bonus scores to indexscans that produce a
+ * suitably-ordered result...
*
* 'rel' is the relation for which we want to generate index paths
* 'indices' is a list of available indexes for 'rel'
* 'restrictinfo_list' is a list of restrictinfo nodes for 'rel'
* 'joininfo_list' is a list of joininfo nodes for 'rel'
*
- * Returns a list of IndexPath access path descriptors.
+ * Returns a list of IndexPath access path descriptors. Additional
+ * IndexPath nodes may also be added to the rel->innerjoin list.
*/
List *
create_index_paths(Query *root,
foreach(ilist, indices)
{
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
- List *scanclausegroups;
+ List *restrictclauses;
List *joinclausegroups;
List *joinouterrelids;
* of the index), but our poor brains are hurting already...
*
* We don't even think about special handling of 'or' clauses that
- * involve more than one relation, since they can't be processed by
- * a single indexscan path anyway. Currently, cnfify() is certain
- * to have restructured any such toplevel 'or' clauses anyway.
+ * involve more than one relation (ie, are join clauses).
+ * Can we do anything useful with those?
*/
match_index_orclauses(rel,
index,
* restriction clauses, then create a path using those clauses
* as indexquals.
*/
- scanclausegroups = group_clauses_by_indexkey(rel,
- index,
- index->indexkeys,
- index->classlist,
- restrictinfo_list);
-
- if (scanclausegroups != NIL)
- retval = nconc(retval,
- create_index_path_group(root,
- rel,
- index,
- scanclausegroups,
- false));
+ restrictclauses = group_clauses_by_indexkey(rel,
+ index,
+ index->indexkeys,
+ index->classlist,
+ restrictinfo_list);
+
+ if (restrictclauses != NIL)
+ retval = lappend(retval,
+ create_index_path(root, rel, index,
+ restrictclauses));
/*
* 3. If this index can be used with any join clause, then create
- * pathnodes for each group of usable clauses. An index can be
- * used with a join clause if its ordering is useful for a
- * mergejoin, or if the index can possibly be used for scanning
- * the inner relation of a nestloop join.
+ * an index path for it even if there were no restriction clauses.
+ * (If there were, there is no need to make another index path.)
+ * This will allow the index to be considered as a base for a
+ * mergejoin in later processing.
+ * Also, 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 clauses that are potentially
+ * applicable to either case. useful_for_mergejoin() tests to
+ * see whether any of the join clauses might support a mergejoin.
+ * index_innerjoin() builds an innerjoin index path for each
+ * potential set of outer rels, which we add to the rel's
+ * innerjoin list.
*/
indexable_joinclauses(rel, index,
joininfo_list, restrictinfo_list,
if (joinclausegroups != NIL)
{
- retval = nconc(retval,
- create_index_path_group(root,
- rel,
- index,
- joinclausegroups,
- true));
+ /* no need to create a plain path if we already did */
+ if (restrictclauses == NIL &&
+ useful_for_mergejoin(index, joinclausegroups))
+ retval = lappend(retval,
+ create_index_path(root, rel, index,
+ NIL));
+
rel->innerjoin = nconc(rel->innerjoin,
index_innerjoin(root, rel, index,
joinclausegroups,
* 'classes' are the classes of the index operators on those keys.
* 'restrictinfo_list' is the list of available restriction clauses for 'rel'.
*
- * Returns NIL if no clauses can be used with this index.
- * Otherwise, a list containing a single sublist is returned (indicating
- * to create_index_path_group() that a single IndexPath should be created).
- * The sublist contains the RestrictInfo nodes for all clauses that can be
+ * Returns a list of all the RestrictInfo nodes for clauses that can be
* used with this index.
*
- * The sublist is ordered by index key (but as far as I can tell, this is
+ * The list is ordered by index key (but as far as I can tell, this is
* an implementation artifact of this routine, and is not depended on by
* any user of the returned list --- tgl 7/99).
*
* Note that in a multi-key index, we stop if we find a key that cannot be
* used with any clause. For example, given an index on (A,B,C), we might
- * return ((C1 C2 C3 C4)) if we find that clauses C1 and C2 use column A,
- * clauses C3 and C4 use column B, and no clauses use column C. But if no
- * clauses match B we will return ((C1 C2)), whether or not there are
+ * return (C1 C2 C3 C4) if we find that clauses C1 and C2 use column A,
+ * clauses C3 and C4 use column B, and no clauses use column C. But if
+ * no clauses match B we will return (C1 C2), whether or not there are
* clauses matching column C, because the executor couldn't use them anyway.
*/
static List *
} while (!DoneMatchingIndexKeys(indexkeys, index));
/* clausegroup_list holds all matched clauses ordered by indexkeys */
- if (clausegroup_list != NIL)
- return lcons(clausegroup_list, NIL);
- return NIL;
+ return clausegroup_list;
}
/*
*
* 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
+ * 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 join). But there must be at least one matching
+ * 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 joining.
*/
} while (!DoneMatchingIndexKeys(indexkeys, index));
- /* clausegroup_list holds all matched clauses ordered by indexkeys */
-
- if (clausegroup_list != NIL)
+ /*
+ * if no join clause was matched then there ain't clauses for
+ * joins at all.
+ */
+ if (!jfound)
{
- /*
- * if no join clause was matched then there ain't clauses for
- * joins at all.
- */
- if (!jfound)
- {
- freeList(clausegroup_list);
- return NIL;
- }
- return lcons(clausegroup_list, NIL);
+ freeList(clausegroup_list);
+ return NIL;
}
- return NIL;
+
+ /* clausegroup_list holds all matched clauses ordered by indexkeys */
+ return clausegroup_list;
}
foreach(i, joininfo_list)
{
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
- List *clausegroups;
-
- if (joininfo->jinfo_restrictinfo == NIL)
- continue;
- clausegroups = group_clauses_by_ikey_for_joins(rel,
- index,
- index->indexkeys,
- index->classlist,
+ List *clausegroup;
+
+ clausegroup = group_clauses_by_ikey_for_joins(rel,
+ index,
+ index->indexkeys,
+ index->classlist,
joininfo->jinfo_restrictinfo,
- restrictinfo_list);
-
- /*----------
- * This code knows that group_clauses_by_ikey_for_joins() returns
- * either NIL or a list containing a single sublist of clauses.
- * The line
- * cg_list = nconc(cg_list, clausegroups);
- * is better read as
- * cg_list = lappend(cg_list, lfirst(clausegroups));
- * That is, we are appending the only sublist returned by
- * group_clauses_by_ikey_for_joins() to the list of clause sublists
- * that this routine will return. By using nconc() we recycle
- * a cons cell that would be wasted ... whoever wrote this code
- * was too clever by half...
- *----------
- */
- if (clausegroups != NIL)
+ restrictinfo_list);
+
+ if (clausegroup != NIL)
{
- cg_list = nconc(cg_list, clausegroups);
+ cg_list = lappend(cg_list, clausegroup);
relid_list = lappend(relid_list, joininfo->unjoined_relids);
}
}
- /* Make sure above clever code didn't screw up */
- Assert(length(cg_list) == length(relid_list));
-
*clausegroups = cg_list;
*outerrelids = relid_list;
}
*
* 'rel' is the relation for which 'index' is defined
* 'clausegroup_list' is a list of lists of restrictinfo nodes which can use
- * 'index' on their inner relation.
+ * 'index'. Each sublist refers to the same set of outer rels.
* 'outerrelids_list' is a list of the required outer rels for each group
* of join clauses.
*
* therefore, both indexid and indexqual should be single-element
* lists.
*/
+ Assert(length(index->relids) == 1);
pathnode->indexid = index->relids;
- pathnode->indexkeys = index->indexkeys;
pathnode->indexqual = lcons(indexquals, NIL);
+ pathnode->indexkeys = index->indexkeys;
+
/* joinid saves the rels needed on the outer side of the join */
pathnode->path.joinid = lfirst(outerrelids_list);
}
/*
- * create_index_path_group
- * Creates a list of index path nodes for each group of clauses
- * (restriction or join) that can be used in conjunction with an index.
- *
- * 'rel' is the relation for which 'index' is defined
- * 'clausegroup_list' is the list of clause groups (lists of restrictinfo
- * nodes) grouped by mergejoinorder
- * 'join' is a flag indicating whether or not the clauses are join
- * clauses
- *
- * Returns a list of new index path nodes.
+ * useful_for_mergejoin
+ * Determine whether the given index can support a mergejoin based
+ * on any join clause within the given list. The clauses have already
+ * been found to be relevant to the index by indexable_joinclauses.
+ * We just need to check whether any are mergejoin material.
*
+ * 'index' is the index of interest.
+ * 'clausegroup_list' is a list of clause groups (sublists of restrictinfo
+ * nodes)
*/
-static List *
-create_index_path_group(Query *root,
- RelOptInfo *rel,
- RelOptInfo *index,
- List *clausegroup_list,
- bool join)
+static bool
+useful_for_mergejoin(RelOptInfo *index,
+ List *clausegroup_list)
{
- List *path_list = NIL;
List *i;
foreach(i, clausegroup_list)
{
List *clausegroup = lfirst(i);
- bool usable = true;
+ List *j;
- if (join)
+ foreach(j, clausegroup)
{
- List *j;
+ RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
- foreach(j, clausegroup)
- {
- RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
- if (!(is_joinable((Node *) restrictinfo->clause) &&
- equal_path_merge_ordering(index->ordering,
- restrictinfo->mergejoinorder)))
- {
- usable = false;
- break;
- }
- }
- }
-
- if (usable)
- {
- path_list = lappend(path_list,
- create_index_path(root, rel, index,
- clausegroup, join));
+ if (is_joinable((Node *) restrictinfo->clause) &&
+ equal_path_merge_ordering(index->ordering,
+ restrictinfo->mergejoinorder))
+ return true;
}
}
- return path_list;
+ return false;
}
/****************************************************************************