Improve planning of OR indexscan plans: for quals like
authorTom Lane <[email protected]>
Tue, 5 Jun 2001 17:13:52 +0000 (17:13 +0000)
committerTom Lane <[email protected]>
Tue, 5 Jun 2001 17:13:52 +0000 (17:13 +0000)
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
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/orindxpath.c
src/include/optimizer/paths.h

index bdc1c033296103e4fc23328b62ae8981ef29c852..fb55139a1e1b1c4b88e72eca6e1a68a7836a5394 100644 (file)
@@ -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);
index a5f5bb151da0d5b86ba005d865fc2e73c91fabe7..6a1fd6b50a416ed88f36620113742cda7e5f6fc3 100644 (file)
@@ -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);
 }
index 25cbc3e4fa206cae31741e5b6c05545fdb03f89e..889a3afee128c003f888bbad0c175b44a395c564 100644 (file)
@@ -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);
index 39afe74d2adc811be5da79f1ab3e99e7496b98ce..f676e61d1f7ce6ed99a043e8ea20c2edb30be3c5 100644 (file)
@@ -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