In a nestloop inner indexscan, it's OK to use pushed-down baserestrictinfo
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 6 Dec 2005 16:50:55 +0000 (16:50 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 6 Dec 2005 16:50:55 +0000 (16:50 +0000)
clauses even if it's an outer join.  This is a corner case since such
clauses could only arise from weird OUTER JOIN ON conditions, but worth
fixing.  Per example from Ron at cheapcomplexdevices.com.

src/backend/optimizer/path/indxpath.c

index 5215ce28e5107033a6640331e27fee634c648268..b46d2f577ef48884b8f6c168dc15d306f3db3242 100644 (file)
@@ -64,9 +64,9 @@ static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
                                                          RestrictInfo *rinfo);
 static Oid indexable_operator(Expr *clause, Oid opclass,
                                   bool indexkey_on_left);
-static bool pred_test_recurse_pred(Expr *predicate, List *restrictinfo_list);
 static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);
 static bool pred_test_recurse_restrict(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 Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
 static Path *make_innerjoin_index_path(Query *root,
@@ -145,11 +145,16 @@ create_index_paths(Query *root, RelOptInfo *rel)
                 * 2. Compute pathkeys describing index's ordering, if any, then
                 * see how many of them are actually useful for this query.
                 */
-               index_pathkeys = build_index_pathkeys(root, rel, index,
-                                                                                         ForwardScanDirection);
-               index_is_ordered = (index_pathkeys != NIL);
-               useful_pathkeys = truncate_useless_pathkeys(root, rel,
-                                                                                                       index_pathkeys);
+               index_is_ordered = OidIsValid(index->ordering[0]);
+               if (index_is_ordered)
+               {
+                       index_pathkeys = build_index_pathkeys(root, rel, index,
+                                                                                                 ForwardScanDirection);
+                       useful_pathkeys = truncate_useless_pathkeys(root, rel,
+                                                                                                               index_pathkeys);
+               }
+               else
+                       useful_pathkeys = NIL;
 
                /*
                 * 3. Generate an indexscan path if there are relevant restriction
@@ -159,10 +164,15 @@ create_index_paths(Query *root, RelOptInfo *rel)
                 * If there is a predicate, consider it anyway since the index
                 * predicate has already been found to match the query.  The
                 * selectivity of the predicate might alone make the index useful.
+                *
+                * Note: not all index AMs support scans with no restriction clauses.
+                * We assume here that the AM does so if and only if it supports
+                * ordered scans.  (It would probably be better if there were a
+                * specific flag for this in pg_am, but there's not.)
                 */
                if (restrictclauses != NIL ||
                        useful_pathkeys != NIL ||
-                       index->indpred != NIL)
+                       (index->indpred != NIL && index_is_ordered))
                        add_path(rel, (Path *)
                                         create_index_path(root, rel, index,
                                                                           restrictclauses,
@@ -322,10 +332,6 @@ group_clauses_by_indexkey_for_join(Query *root,
                {
                        RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
 
-                       /* Can't use pushed-down clauses in outer join */
-                       if (isouterjoin && rinfo->is_pushed_down)
-                               continue;
-
                        if (match_clause_to_indexcol(rel,
                                                                                 index,
                                                                                 indexcol,
@@ -749,11 +755,25 @@ check_partial_indexes(Query *root, RelOptInfo *rel)
  *       Recursively checks whether the clauses in restrictinfo_list imply
  *       that the given predicate is true.
  *
- *       This routine (together with the routines it calls) first breaks down
- *       the predicate to its constituent AND/OR elements, then similarly
- *       breaks down the restriction clauses to AND/OR elements in an effort
- *       to prove that each predicate element is implied.  The top-level
- *       List structure of each list corresponds to an AND list.
+ *       This routine (together with the routines it calls) iterates over
+ *       ANDs in the predicate first, then breaks down the restriction list
+ *       to its constituent AND/OR elements, and iterates over ORs
+ *       in the predicate last.  This order is important to make the test
+ *       succeed whenever possible. --Nels, Jan '93
+ *
+ *       For example, a restriction (a OR b) certainly implies a predicate
+ *       (a OR b OR c), but no one element of the predicate is individually
+ *       implied by the restriction.  By expanding the predicate ORs last
+ *       we are able to prove that the whole predicate is implied by each arm
+ *       of the restriction.  Conversely consider predicate (a AND b) with
+ *       restriction (a AND b AND c).  This should be implied but we will
+ *       fail to prove it if we dissect the restriction first.
+ *
+ *       The top-level List structure of each list corresponds to an AND list.
+ *       We assume that canonicalize_qual() has been applied and so there
+ *       are no explicit ANDs immediately below the top-level List structure.
+ *       (If this is not true we might fail to prove an implication that is
+ *       valid, but no worse consequences will ensue.)
  */
 bool
 pred_test(List *predicate_list, List *restrictinfo_list)
@@ -779,65 +799,23 @@ pred_test(List *predicate_list, List *restrictinfo_list)
                return false;                   /* no restriction clauses: the test must
                                                                 * fail */
 
+       /* Take care of the AND semantics of the top-level predicate list */
        foreach(pred, predicate_list)
        {
                /*
                 * if any clause is not implied, the whole predicate is not
                 * implied.
                 */
-               if (!pred_test_recurse_pred(lfirst(pred), restrictinfo_list))
+               if (!pred_test_restrict_list(lfirst(pred), restrictinfo_list))
                        return false;
        }
        return true;
 }
 
 
-/*
- * pred_test_recurse_pred
- *       Does the "predicate inclusion test" for one conjunct of a predicate
- *       expression.  Here we recursively deal with the possibility that the
- *       predicate conjunct is itself an AND or OR structure.
- */
-static bool
-pred_test_recurse_pred(Expr *predicate, List *restrictinfo_list)
-{
-       List       *items;
-       ListCell   *item;
-
-       Assert(predicate != NULL);
-       if (or_clause((Node *) predicate))
-       {
-               items = ((BoolExpr *) predicate)->args;
-               foreach(item, items)
-               {
-                       /* if any item is implied, the whole predicate is implied */
-                       if (pred_test_recurse_pred(lfirst(item), restrictinfo_list))
-                               return true;
-               }
-               return false;
-       }
-       else if (and_clause((Node *) predicate))
-       {
-               items = ((BoolExpr *) predicate)->args;
-               foreach(item, items)
-               {
-                       /*
-                        * if any item is not implied, the whole predicate is not
-                        * implied
-                        */
-                       if (!pred_test_recurse_pred(lfirst(item), restrictinfo_list))
-                               return false;
-               }
-               return true;
-       }
-       else
-               return pred_test_restrict_list(predicate, restrictinfo_list);
-}
-
-
 /*
  * pred_test_restrict_list
- *       Does the "predicate inclusion test" for one element of a predicate
+ *       Does the "predicate inclusion test" for one AND clause of a predicate
  *       expression.  Here we take care of the AND semantics of the top-level
  *       restrictinfo list.
  */
@@ -859,10 +837,10 @@ pred_test_restrict_list(Expr *predicate, List *restrictinfo_list)
 
 /*
  * pred_test_recurse_restrict
- *       Does the "predicate inclusion test" for one element of a predicate
+ *       Does the "predicate inclusion test" for one AND clause of a predicate
  *       expression.  Here we recursively deal with the possibility that the
  *       restriction-list element is itself an AND or OR structure; also,
- *       we strip off RestrictInfo nodes to find bare predicate expressions.
+ *       we strip off RestrictInfo nodes to find bare qualifier expressions.
  */
 static bool
 pred_test_recurse_restrict(Expr *predicate, Node *clause)
@@ -903,6 +881,49 @@ pred_test_recurse_restrict(Expr *predicate, Node *clause)
                }
                return false;
        }
+       else
+               return pred_test_recurse_pred(predicate, clause);
+}
+
+
+/*
+ * pred_test_recurse_pred
+ *       Does the "predicate inclusion test" for one conjunct of a predicate
+ *       expression.  Here we recursively deal with the possibility that the
+ *       predicate conjunct is itself an AND or OR structure.
+ */
+static bool
+pred_test_recurse_pred(Expr *predicate, Node *clause)
+{
+       List       *items;
+       ListCell   *item;
+
+       Assert(predicate != NULL);
+       if (or_clause((Node *) predicate))
+       {
+               items = ((BoolExpr *) predicate)->args;
+               foreach(item, items)
+               {
+                       /* if any item is implied, the whole predicate is implied */
+                       if (pred_test_recurse_pred(lfirst(item), clause))
+                               return true;
+               }
+               return false;
+       }
+       else if (and_clause((Node *) predicate))
+       {
+               items = ((BoolExpr *) predicate)->args;
+               foreach(item, items)
+               {
+                       /*
+                        * if any item is not implied, the whole predicate is not
+                        * implied
+                        */
+                       if (!pred_test_recurse_pred(lfirst(item), clause))
+                               return false;
+               }
+               return true;
+       }
        else
                return pred_test_simple_clause(predicate, clause);
 }