From cdd230d62899455cc07ba1caf68387fb834d5bd2 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Tue, 5 Jun 2001 17:13:52 +0000 Subject: [PATCH] Improve planning of OR indexscan plans: for quals like WHERE (a = 1 or a = 2) and b = 42 and an index on (a,b), include the clause b = 42 in the indexquals generated for each arm of the OR clause. Essentially this is an index- driven conversion from CNF to DNF. Implementation is a bit klugy, but better than not exploiting the extra quals at all ... --- src/backend/optimizer/path/allpaths.c | 4 +- src/backend/optimizer/path/indxpath.c | 109 +++++++++++++++--------- src/backend/optimizer/path/orindxpath.c | 29 +++---- src/include/optimizer/paths.h | 5 +- 4 files changed, 86 insertions(+), 61 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index bdc1c033296..fb55139a1e1 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.75 2001/06/05 05:26:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.76 2001/06/05 17:13:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -232,7 +232,7 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte) create_index_paths(root, rel); /* create_index_paths must be done before create_or_index_paths */ - create_or_index_paths(root, rel, rel->baserestrictinfo); + create_or_index_paths(root, rel); /* Now find the cheapest of the paths for this rel */ set_cheapest(rel); diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index a5f5bb151da..6a1fd6b50a4 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.105 2001/05/20 20:28:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.106 2001/06/05 17:13:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -397,7 +397,7 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, clause, false); } -/* +/*---------- * Given an OR subclause that has previously been determined to match * the specified index, extract a list of specific opclauses that can be * used as indexquals. @@ -406,10 +406,25 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, * given opclause. However, if the OR subclause is an AND, we have to * scan it to find the opclause(s) that match the index. (There should * be at least one, if match_or_subclause_to_indexkey succeeded, but there - * could be more.) Also, we apply expand_indexqual_conditions() to convert - * any special matching opclauses to indexable operators. + * could be more.) + * + * Also, we can look at other restriction clauses of the rel to discover + * additional candidate indexquals: for example, consider + * ... where (a = 11 or a = 12) and b = 42; + * If we are dealing with an index on (a,b) then we can include the clause + * b = 42 in the indexqual list generated for each of the OR subclauses. + * Essentially, we are making an index-specific transformation from CNF to + * DNF. (NOTE: when we do this, we end up with a slightly inefficient plan + * because create_indexscan_plan is not very bright about figuring out which + * restriction clauses are implied by the generated indexqual condition. + * Currently we'll end up rechecking both the OR clause and the transferred + * restriction clause as qpquals. FIXME someday.) + * + * Also, we apply expand_indexqual_conditions() to convert any special + * matching opclauses to indexable operators. * * The passed-in clause is not changed. + *---------- */ List * extract_or_indexqual_conditions(RelOptInfo *rel, @@ -417,54 +432,72 @@ extract_or_indexqual_conditions(RelOptInfo *rel, Expr *orsubclause) { List *quals = NIL; + int *indexkeys = index->indexkeys; + Oid *classes = index->classlist; - if (and_clause((Node *) orsubclause)) + /* + * Extract relevant indexclauses in indexkey order. This is essentially + * just like group_clauses_by_indexkey() except that the input and + * output are lists of bare clauses, not of RestrictInfo nodes. + */ + do { + int curIndxKey = indexkeys[0]; + Oid curClass = classes[0]; + List *clausegroup = NIL; + List *item; - /* - * Extract relevant sub-subclauses in indexkey order. This is - * just like group_clauses_by_indexkey() except that the input and - * output are lists of bare clauses, not of RestrictInfo nodes. - */ - int *indexkeys = index->indexkeys; - Oid *classes = index->classlist; + if (and_clause((Node *) orsubclause)) + { + foreach(item, orsubclause->args) + { + Expr *subsubclause = (Expr *) lfirst(item); - do + if (match_clause_to_indexkey(rel, index, + curIndxKey, curClass, + subsubclause, false)) + clausegroup = lappend(clausegroup, subsubclause); + } + } + else if (match_clause_to_indexkey(rel, index, + curIndxKey, curClass, + orsubclause, false)) { - int curIndxKey = indexkeys[0]; - Oid curClass = classes[0]; - List *clausegroup = NIL; - List *item; + clausegroup = makeList1(orsubclause); + } - foreach(item, orsubclause->args) + /* + * If we found no clauses for this indexkey in the OR subclause + * itself, try looking in the rel's top-level restriction list. + */ + if (clausegroup == NIL) + { + foreach(item, rel->baserestrictinfo) { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); + if (match_clause_to_indexkey(rel, index, curIndxKey, curClass, - lfirst(item), false)) - clausegroup = lappend(clausegroup, lfirst(item)); + rinfo->clause, false)) + clausegroup = lappend(clausegroup, rinfo->clause); } + } - /* - * If no clauses match this key, we're done; we don't want to - * look at keys to its right. - */ - if (clausegroup == NIL) - break; + /* + * If still no clauses match this key, we're done; we don't want to + * look at keys to its right. + */ + if (clausegroup == NIL) + break; - quals = nconc(quals, clausegroup); + quals = nconc(quals, clausegroup); - indexkeys++; - classes++; - } while (!DoneMatchingIndexKeys(indexkeys, index)); + indexkeys++; + classes++; + } while (!DoneMatchingIndexKeys(indexkeys, index)); - if (quals == NIL) - elog(ERROR, "extract_or_indexqual_conditions: no matching clause"); - } - else - { - /* we assume the caller passed a valid indexable qual */ - quals = makeList1(orsubclause); - } + if (quals == NIL) + elog(ERROR, "extract_or_indexqual_conditions: no matching clause"); return expand_indexqual_conditions(quals); } diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index 25cbc3e4fa2..889a3afee12 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.43 2001/05/20 20:28:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.44 2001/06/05 17:13:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,20 +38,17 @@ static void best_or_subclause_index(Query *root, RelOptInfo *rel, * create_index_paths() must already have been called. * * 'rel' is the relation entry for which the paths are to be created - * 'clauses' is the list of available restriction clause nodes * * Returns nothing, but adds paths to rel->pathlist via add_path(). */ void -create_or_index_paths(Query *root, - RelOptInfo *rel, - List *clauses) +create_or_index_paths(Query *root, RelOptInfo *rel) { - List *clist; + List *rlist; - foreach(clist, clauses) + foreach(rlist, rel->baserestrictinfo) { - RestrictInfo *clausenode = (RestrictInfo *) lfirst(clist); + RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(rlist); /* * Check to see if this clause is an 'or' clause, and, if so, @@ -59,13 +56,13 @@ create_or_index_paths(Query *root, * has been matched by an index. The information used was saved * by create_index_paths(). */ - if (restriction_is_or_clause(clausenode) && - clausenode->subclauseindices) + if (restriction_is_or_clause(restrictinfo) && + restrictinfo->subclauseindices) { bool all_indexable = true; List *temp; - foreach(temp, clausenode->subclauseindices) + foreach(temp, restrictinfo->subclauseindices) { if (lfirst(temp) == NIL) { @@ -75,7 +72,6 @@ create_or_index_paths(Query *root, } if (all_indexable) { - /* * OK, build an IndexPath for this OR clause, using the * best available index for each subclause. @@ -93,10 +89,7 @@ create_or_index_paths(Query *root, */ pathnode->path.pathkeys = NIL; - /* - * We don't actually care what order the index scans in - * ... - */ + /* We don't actually care what order the index scans in. */ pathnode->indexscandir = NoMovementScanDirection; /* This isn't a nestloop innerjoin, so: */ @@ -106,8 +99,8 @@ create_or_index_paths(Query *root, best_or_subclause_indices(root, rel, - clausenode->clause->args, - clausenode->subclauseindices, + restrictinfo->clause->args, + restrictinfo->subclauseindices, pathnode); add_path(rel, (Path *) pathnode); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 39afe74d2ad..f676e61d1f7 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: paths.h,v 1.53 2001/05/20 20:28:20 tgl Exp $ + * $Id: paths.h,v 1.54 2001/06/05 17:13:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,8 +47,7 @@ extern List *expand_indexqual_conditions(List *indexquals); * orindxpath.c * additional routines for indexable OR clauses */ -extern void create_or_index_paths(Query *root, RelOptInfo *rel, - List *clauses); +extern void create_or_index_paths(Query *root, RelOptInfo *rel); /* * tidpath.h -- GitLab