]> granicus.if.org Git - postgresql/commitdiff
Improve check_partial_indexes() to consider join clauses in proof attempts.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 16 Nov 2012 00:29:05 +0000 (19:29 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 16 Nov 2012 00:29:05 +0000 (19:29 -0500)
Traditionally check_partial_indexes() has only looked at restriction
clauses while trying to prove partial indexes usable in queries.  However,
join clauses can also be used in some cases; mainly, that a strict operator
on "x" proves an "x IS NOT NULL" index predicate, even if the operator is
in a join clause rather than a restriction clause.  Adding this code fixes
a regression in 9.2, because previously we would take join clauses into
account when considering whether a partial index could be used in a
nestloop inner indexscan path.  9.2 doesn't handle nestloop inner
indexscans in the same way, and this consideration was overlooked in the
rewrite.  Moving the work to check_partial_indexes() is a better solution
anyway, since the proof applies whether or not we actually use the index
in that particular way, and we don't have to do it over again for each
possible outer relation.  Per report from Dave Cramer.

src/backend/optimizer/path/indxpath.c

index 7cbdbc5280d54e5e8df35fa9428d8fcca2ee47c7..a1a7defef63aa25d32b0b01b96be46cb7d191f80 100644 (file)
@@ -2589,12 +2589,83 @@ match_clause_to_ordering_op(IndexOptInfo *index,
 void
 check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
 {
-       List       *restrictinfo_list = rel->baserestrictinfo;
-       ListCell   *ilist;
+       List       *clauselist;
+       bool            have_partial;
+       Relids          otherrels;
+       ListCell   *lc;
 
-       foreach(ilist, rel->indexlist)
+       /*
+        * Frequently, there will be no partial indexes, so first check to make
+        * sure there's something useful to do here.
+        */
+       have_partial = false;
+       foreach(lc, rel->indexlist)
        {
-               IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
+               IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
+
+               if (index->indpred == NIL)
+                       continue;                       /* ignore non-partial indexes */
+
+               if (index->predOK)
+                       continue;                       /* don't repeat work if already proven OK */
+
+               have_partial = true;
+               break;
+       }
+       if (!have_partial)
+               return;
+
+       /*
+        * Construct a list of clauses that we can assume true for the purpose
+        * of proving the index(es) usable.  Restriction clauses for the rel are
+        * always usable, and so are any join clauses that are "movable to" this
+        * rel.  Also, we can consider any EC-derivable join clauses (which must
+        * be "movable to" this rel, by definition).
+        */
+       clauselist = list_copy(rel->baserestrictinfo);
+
+       /* Scan the rel's join clauses */
+       foreach(lc, rel->joininfo)
+       {
+               RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+               /* Check if clause can be moved to this rel */
+               if (!join_clause_is_movable_to(rinfo, rel->relid))
+                       continue;
+
+               clauselist = lappend(clauselist, rinfo);
+       }
+
+       /*
+        * Add on any equivalence-derivable join clauses.  Computing the correct
+        * relid sets for generate_join_implied_equalities is slightly tricky
+        * because the rel could be a child rel rather than a true baserel, and
+        * in that case we must remove its parent's relid from all_baserels.
+        */
+       if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+       {
+               /* Lookup parent->child translation data */
+               AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, rel);
+
+               otherrels = bms_difference(root->all_baserels,
+                                                                  bms_make_singleton(appinfo->parent_relid));
+       }
+       else
+               otherrels = bms_difference(root->all_baserels, rel->relids);
+
+       if (!bms_is_empty(otherrels))
+               clauselist =
+                       list_concat(clauselist,
+                                               generate_join_implied_equalities(root,
+                                                                                                                bms_union(rel->relids,
+                                                                                                                                  otherrels),
+                                                                                                                otherrels,
+                                                                                                                rel));
+
+       /* Now try to prove each index predicate true */
+       foreach(lc, rel->indexlist)
+       {
+               IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
 
                if (index->indpred == NIL)
                        continue;                       /* ignore non-partial indexes */
@@ -2602,8 +2673,7 @@ check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
                if (index->predOK)
                        continue;                       /* don't repeat work if already proven OK */
 
-               index->predOK = predicate_implied_by(index->indpred,
-                                                                                        restrictinfo_list);
+               index->predOK = predicate_implied_by(index->indpred, clauselist);
        }
 }