From 4f5760d4afa9423fe4d38e4cbec48bf5e793e7e5 Mon Sep 17 00:00:00 2001 From: Dean Rasheed Date: Tue, 8 Dec 2020 20:10:11 +0000 Subject: [PATCH] Improve estimation of ANDs under ORs using extended statistics. 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 | 21 +++++++++++++++++++++ src/backend/statistics/extended_stats.c | 25 +++++++++++++++++++++++++ src/test/regress/expected/stats_ext.out | 4 ++-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index b88b29ec23..a7e535c27f 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -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; diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 555bc32561..6d26de37f4 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -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; diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index 6e1c4f3edd..7bfeaf85f0 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -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; -- 2.39.5