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