Skip to content

Commit 3104a92

Browse files
committed
Another go at making pred_test() handle all reasonable combinations
of AND and OR clauses. The key point here is that an OR on the predicate side has to be treated gingerly: we may be able to prove that the OR is implied even when no one of its components is implied. For example (x OR y) implies (x OR y OR z) even though no one of x, y, or z can be individually proven. This code handles both the example shown recently by Sergey Koshcheyev and the one shown last October by Dawid Kuroczko.
1 parent 47ea714 commit 3104a92

File tree

1 file changed

+158
-117
lines changed

1 file changed

+158
-117
lines changed

src/backend/optimizer/path/indxpath.c

Lines changed: 158 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.168 2005/03/01 00:24:52 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.169 2005/03/02 04:10:53 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -64,9 +64,7 @@ static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
6464
RestrictInfo *rinfo);
6565
static Oid indexable_operator(Expr *clause, Oid opclass,
6666
bool indexkey_on_left);
67-
static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);
68-
static bool pred_test_recurse_restrict(Expr *predicate, Node *clause);
69-
static bool pred_test_recurse_pred(Expr *predicate, Node *clause);
67+
static bool pred_test_recurse(Node *clause, Node *predicate);
7068
static bool pred_test_simple_clause(Expr *predicate, Node *clause);
7169
static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
7270
static Path *make_innerjoin_index_path(Query *root,
@@ -749,30 +747,17 @@ check_partial_indexes(Query *root, RelOptInfo *rel)
749747
* Recursively checks whether the clauses in restrictinfo_list imply
750748
* that the given predicate is true.
751749
*
752-
* This routine (together with the routines it calls) iterates over
753-
* ANDs in the predicate first, then breaks down the restriction list
754-
* to its constituent AND/OR elements, and iterates over ORs
755-
* in the predicate last. This order is important to make the test
756-
* succeed whenever possible. --Nels, Jan '93
757-
*
758-
* For example, a restriction (a OR b) certainly implies a predicate
759-
* (a OR b OR c), but no one element of the predicate is individually
760-
* implied by the restriction. By expanding the predicate ORs last
761-
* we are able to prove that the whole predicate is implied by each arm
762-
* of the restriction. Conversely consider predicate (a AND b) with
763-
* restriction (a AND b AND c). This should be implied but we will
764-
* fail to prove it if we dissect the restriction first.
765-
*
766750
* The top-level List structure of each list corresponds to an AND list.
767-
* We assume that canonicalize_qual() has been applied and so there
768-
* are no explicit ANDs immediately below the top-level List structure.
769-
* (If this is not true we might fail to prove an implication that is
770-
* valid, but no worse consequences will ensue.)
751+
* We assume that canonicalize_qual() has been applied and so there are
752+
* no un-flattened ANDs or ORs (e.g., no AND immediately within an AND,
753+
* including AND just below the top-level List structure).
754+
* If this is not true we might fail to prove an implication that is
755+
* valid, but no worse consequences will ensue.
771756
*/
772757
bool
773758
pred_test(List *predicate_list, List *restrictinfo_list)
774759
{
775-
ListCell *pred;
760+
ListCell *item;
776761

777762
/*
778763
* Note: if Postgres tried to optimize queries by forming equivalence
@@ -793,133 +778,189 @@ pred_test(List *predicate_list, List *restrictinfo_list)
793778
return false; /* no restriction clauses: the test must
794779
* fail */
795780

796-
/* Take care of the AND semantics of the top-level predicate list */
797-
foreach(pred, predicate_list)
781+
/*
782+
* In all cases where the predicate is an AND-clause, pred_test_recurse()
783+
* will prefer to iterate over the predicate's components. So we can
784+
* just do that to start with here, and eliminate the need for
785+
* pred_test_recurse() to handle a bare List on the predicate side.
786+
*
787+
* Logic is: restriction must imply each of the AND'ed predicate items.
788+
*/
789+
foreach(item, predicate_list)
798790
{
799-
/*
800-
* if any clause is not implied, the whole predicate is not
801-
* implied.
802-
*/
803-
if (!pred_test_restrict_list(lfirst(pred), restrictinfo_list))
791+
if (!pred_test_recurse((Node *) restrictinfo_list, lfirst(item)))
804792
return false;
805793
}
806794
return true;
807795
}
808796

809797

810-
/*
811-
* pred_test_restrict_list
812-
* Does the "predicate inclusion test" for one AND clause of a predicate
813-
* expression. Here we take care of the AND semantics of the top-level
814-
* restrictinfo list.
815-
*/
816-
static bool
817-
pred_test_restrict_list(Expr *predicate, List *restrictinfo_list)
818-
{
819-
ListCell *item;
820-
821-
foreach(item, restrictinfo_list)
822-
{
823-
/* if any clause implies the predicate, return true */
824-
if (pred_test_recurse_restrict(predicate,
825-
(Node *) lfirst(item)))
826-
return true;
827-
}
828-
return false;
829-
}
830-
831-
832-
/*
833-
* pred_test_recurse_restrict
834-
* Does the "predicate inclusion test" for one AND clause of a predicate
835-
* expression. Here we recursively deal with the possibility that the
836-
* restriction-list element is itself an AND or OR structure; also,
837-
* we strip off RestrictInfo nodes to find bare qualifier expressions.
798+
/*----------
799+
* pred_test_recurse
800+
* Does the "predicate inclusion test" for non-NULL restriction and
801+
* predicate clauses.
802+
*
803+
* The logic followed here is ("=>" means "implies"):
804+
* atom A => atom B iff: pred_test_simple_clause says so
805+
* atom A => AND-expr B iff: A => each of B's components
806+
* atom A => OR-expr B iff: A => any of B's components
807+
* AND-expr A => atom B iff: any of A's components => B
808+
* AND-expr A => AND-expr B iff: A => each of B's components
809+
* AND-expr A => OR-expr B iff: A => any of B's components,
810+
* *or* any of A's components => B
811+
* OR-expr A => atom B iff: each of A's components => B
812+
* OR-expr A => AND-expr B iff: A => each of B's components
813+
* OR-expr A => OR-expr B iff: each of A's components => any of B's
814+
*
815+
* An "atom" is anything other than an AND or OR node. Notice that we don't
816+
* have any special logic to handle NOT nodes; these should have been pushed
817+
* down or eliminated where feasible by prepqual.c.
818+
*
819+
* We can't recursively expand either side first, but have to interleave
820+
* the expansions per the above rules, to be sure we handle all of these
821+
* examples:
822+
* (x OR y) => (x OR y OR z)
823+
* (x AND y AND z) => (x AND y)
824+
* (x AND y) => ((x AND y) OR z)
825+
* ((x OR y) AND z) => (x OR y)
826+
* This is still not an exhaustive test, but it handles most normal cases
827+
* under the assumption that both inputs have been AND/OR flattened.
828+
*
829+
* A bare List node on the restriction side is interpreted as an AND clause,
830+
* in order to handle the top-level restriction List properly. However we
831+
* need not consider a List on the predicate side since pred_test() already
832+
* expanded it.
833+
*
834+
* We have to be prepared to handle RestrictInfo nodes in the restrictinfo
835+
* tree, though not in the predicate tree.
836+
*----------
838837
*/
839838
static bool
840-
pred_test_recurse_restrict(Expr *predicate, Node *clause)
839+
pred_test_recurse(Node *clause, Node *predicate)
841840
{
842-
List *items;
843841
ListCell *item;
844842

845843
Assert(clause != NULL);
844+
/* skip through RestrictInfo */
846845
if (IsA(clause, RestrictInfo))
847846
{
848-
RestrictInfo *restrictinfo = (RestrictInfo *) clause;
849-
850-
return pred_test_recurse_restrict(predicate,
851-
(Node *) restrictinfo->clause);
847+
clause = (Node *) ((RestrictInfo *) clause)->clause;
848+
Assert(clause != NULL);
849+
Assert(!IsA(clause, RestrictInfo));
852850
}
853-
else if (or_clause(clause))
851+
Assert(predicate != NULL);
852+
853+
/*
854+
* Since a restriction List clause is handled the same as an AND clause,
855+
* we can avoid duplicate code like this:
856+
*/
857+
if (and_clause(clause))
858+
clause = (Node *) ((BoolExpr *) clause)->args;
859+
860+
if (IsA(clause, List))
854861
{
855-
items = ((BoolExpr *) clause)->args;
856-
foreach(item, items)
862+
if (and_clause(predicate))
857863
{
858-
/* if any OR item doesn't imply the predicate, clause doesn't */
859-
if (!pred_test_recurse_restrict(predicate, lfirst(item)))
860-
return false;
864+
/* AND-clause => AND-clause if A implies each of B's items */
865+
foreach(item, ((BoolExpr *) predicate)->args)
866+
{
867+
if (!pred_test_recurse(clause, lfirst(item)))
868+
return false;
869+
}
870+
return true;
871+
}
872+
else if (or_clause(predicate))
873+
{
874+
/* AND-clause => OR-clause if A implies any of B's items */
875+
/* Needed to handle (x AND y) => ((x AND y) OR z) */
876+
foreach(item, ((BoolExpr *) predicate)->args)
877+
{
878+
if (pred_test_recurse(clause, lfirst(item)))
879+
return true;
880+
}
881+
/* Also check if any of A's items implies B */
882+
/* Needed to handle ((x OR y) AND z) => (x OR y) */
883+
foreach(item, (List *) clause)
884+
{
885+
if (pred_test_recurse(lfirst(item), predicate))
886+
return true;
887+
}
888+
return false;
889+
}
890+
else
891+
{
892+
/* AND-clause => atom if any of A's items implies B */
893+
foreach(item, (List *) clause)
894+
{
895+
if (pred_test_recurse(lfirst(item), predicate))
896+
return true;
897+
}
898+
return false;
861899
}
862-
return true;
863900
}
864-
else if (and_clause(clause))
901+
else if (or_clause(clause))
865902
{
866-
items = ((BoolExpr *) clause)->args;
867-
foreach(item, items)
903+
if (or_clause(predicate))
868904
{
869905
/*
870-
* if any AND item implies the predicate, the whole clause
871-
* does
906+
* OR-clause => OR-clause if each of A's items implies any of
907+
* B's items. Messy but can't do it any more simply.
872908
*/
873-
if (pred_test_recurse_restrict(predicate, lfirst(item)))
874-
return true;
909+
foreach(item, ((BoolExpr *) clause)->args)
910+
{
911+
Node *citem = lfirst(item);
912+
ListCell *item2;
913+
914+
foreach(item2, ((BoolExpr *) predicate)->args)
915+
{
916+
if (pred_test_recurse(citem, lfirst(item2)))
917+
break;
918+
}
919+
if (item2 == NULL)
920+
return false; /* doesn't imply any of B's */
921+
}
922+
return true;
923+
}
924+
else
925+
{
926+
/* OR-clause => AND-clause if each of A's items implies B */
927+
/* OR-clause => atom if each of A's items implies B */
928+
foreach(item, ((BoolExpr *) clause)->args)
929+
{
930+
if (!pred_test_recurse(lfirst(item), predicate))
931+
return false;
932+
}
933+
return true;
875934
}
876-
return false;
877935
}
878936
else
879-
return pred_test_recurse_pred(predicate, clause);
880-
}
881-
882-
883-
/*
884-
* pred_test_recurse_pred
885-
* Does the "predicate inclusion test" for one conjunct of a predicate
886-
* expression. Here we recursively deal with the possibility that the
887-
* predicate conjunct is itself an AND or OR structure.
888-
*/
889-
static bool
890-
pred_test_recurse_pred(Expr *predicate, Node *clause)
891-
{
892-
List *items;
893-
ListCell *item;
894-
895-
Assert(predicate != NULL);
896-
if (or_clause((Node *) predicate))
897937
{
898-
items = ((BoolExpr *) predicate)->args;
899-
foreach(item, items)
938+
if (and_clause(predicate))
900939
{
901-
/* if any item is implied, the whole predicate is implied */
902-
if (pred_test_recurse_pred(lfirst(item), clause))
903-
return true;
940+
/* atom => AND-clause if A implies each of B's items */
941+
foreach(item, ((BoolExpr *) predicate)->args)
942+
{
943+
if (!pred_test_recurse(clause, lfirst(item)))
944+
return false;
945+
}
946+
return true;
904947
}
905-
return false;
906-
}
907-
else if (and_clause((Node *) predicate))
908-
{
909-
items = ((BoolExpr *) predicate)->args;
910-
foreach(item, items)
948+
else if (or_clause(predicate))
911949
{
912-
/*
913-
* if any item is not implied, the whole predicate is not
914-
* implied
915-
*/
916-
if (!pred_test_recurse_pred(lfirst(item), clause))
917-
return false;
950+
/* atom => OR-clause if A implies any of B's items */
951+
foreach(item, ((BoolExpr *) predicate)->args)
952+
{
953+
if (pred_test_recurse(clause, lfirst(item)))
954+
return true;
955+
}
956+
return false;
957+
}
958+
else
959+
{
960+
/* atom => atom is the base case */
961+
return pred_test_simple_clause((Expr *) predicate, clause);
918962
}
919-
return true;
920963
}
921-
else
922-
return pred_test_simple_clause(predicate, clause);
923964
}
924965

925966

0 commit comments

Comments
 (0)