Improve estimation of ANDs under ORs using extended statistics.
authorDean Rasheed <[email protected]>
Tue, 8 Dec 2020 20:10:11 +0000 (20:10 +0000)
committerDean Rasheed <[email protected]>
Tue, 8 Dec 2020 20:10:11 +0000 (20:10 +0000)
Formerly, extended statistics only handled clauses that were
RestrictInfos. However, the restrictinfo machinery doesn't create
sub-AND RestrictInfos for AND clauses underneath OR clauses.
Therefore teach extended statistics to handle bare AND clauses,
looking for compatible RestrictInfo clauses underneath them.

Dean Rasheed, reviewed by Tomas Vondra.

Discussion: https://postgr.es/m/CAEZATCW=J65GUFm50RcPv-iASnS2mTXQbr=CfBvWRVhFLJ_fWA@mail.gmail.com

src/backend/optimizer/path/clausesel.c
src/backend/statistics/extended_stats.c
src/test/regress/expected/stats_ext.out

index b88b29ec23ad240acb496679465397c70e0ee0d1..a7e535c27f5a15a0183210ca00a85b6acd2b9a66 100644 (file)
@@ -538,7 +538,28 @@ find_single_rel_for_clauses(PlannerInfo *root, List *clauses)
         * However, currently the extended-stats machinery won't do anything
         * with non-RestrictInfo clauses anyway, so there's no point in
         * spending extra cycles; just fail if that's what we have.
+        *
+        * An exception to that rule is if we have a bare BoolExpr AND clause.
+        * We treat this as a special case because the restrictinfo machinery
+        * doesn't build RestrictInfos on top of AND clauses.
         */
+       if (is_andclause(rinfo))
+       {
+           RelOptInfo *rel;
+
+           rel = find_single_rel_for_clauses(root,
+                                             ((BoolExpr *) rinfo)->args);
+
+           if (rel == NULL)
+               return NULL;
+           if (lastrelid == 0)
+               lastrelid = rel->relid;
+           else if (rel->relid != lastrelid)
+               return NULL;
+
+           continue;
+       }
+
        if (!IsA(rinfo, RestrictInfo))
            return NULL;
 
index 555bc325619830c7caccf7b7ec0413929b9035d8..6d26de37f4ddf65f2f7a940d46ca587a7bac9f7e 100644 (file)
@@ -1174,6 +1174,31 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid,
    RestrictInfo *rinfo = (RestrictInfo *) clause;
    Oid         userid;
 
+   /*
+    * Special-case handling for bare BoolExpr AND clauses, because the
+    * restrictinfo machinery doesn't build RestrictInfos on top of AND
+    * clauses.
+    */
+   if (is_andclause(clause))
+   {
+       BoolExpr   *expr = (BoolExpr *) clause;
+       ListCell   *lc;
+
+       /*
+        * Check that each sub-clause is compatible.  We expect these to be
+        * RestrictInfos.
+        */
+       foreach(lc, expr->args)
+       {
+           if (!statext_is_compatible_clause(root, (Node *) lfirst(lc),
+                                             relid, attnums))
+               return false;
+       }
+
+       return true;
+   }
+
+   /* Otherwise it must be a RestrictInfo. */
    if (!IsA(rinfo, RestrictInfo))
        return false;
 
index 6e1c4f3edd1cd851f893847742fe096f793e165a..7bfeaf85f0e06b1bd7edd96e9aa8887278d1bbb2 100644 (file)
@@ -1594,13 +1594,13 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE a = 0
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE (a = 0 AND b = 0 AND c = 0) OR (a = 1 AND b = 1 AND c = 1) OR (a = 2 AND b = 2 AND c = 2)');
  estimated | actual 
 -----------+--------
-       300 |    306
+       306 |    306
 (1 row)
 
 SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists_partial WHERE (a = 0 AND b = 0) OR (a = 0 AND c = 0) OR (b = 0 AND c = 0)');
  estimated | actual 
 -----------+--------
-       306 |    102
+       108 |    102
 (1 row)
 
 DROP TABLE mcv_lists_partial;