From 4f6d8f7e1cd16ec2c0c022479524497f271f821a Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Mon, 11 Nov 2019 01:34:11 +0100 Subject: [PATCH 2/3] Support clauses of the form Var op Var --- src/backend/statistics/extended_stats.c | 62 ++++++++++++++++++- src/backend/statistics/mcv.c | 61 ++++++++++++++++++ .../statistics/extended_stats_internal.h | 2 + 3 files changed, 122 insertions(+), 3 deletions(-) diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index ccf9565c75..d9e854228c 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -964,13 +964,15 @@ statext_is_compatible_clause_internal(PlannerInfo *root, Node *clause, RangeTblEntry *rte = root->simple_rte_array[relid]; OpExpr *expr = (OpExpr *) clause; Var *var; + Var *var2 = NULL; /* Only expressions with two arguments are considered compatible. */ if (list_length(expr->args) != 2) return false; /* Check if the expression the right shape (one Var, one Const) */ - if (!examine_opclause_expression(expr, &var, NULL, NULL)) + if ((!examine_opclause_expression(expr, &var, NULL, NULL)) && + (!examine_opclause_expression2(expr, &var, &var2))) return false; /* @@ -1010,8 +1012,16 @@ statext_is_compatible_clause_internal(PlannerInfo *root, Node *clause, !get_func_leakproof(get_opcode(expr->opno))) return false; - return statext_is_compatible_clause_internal(root, (Node *) var, - relid, attnums); + if (var2) + { + return statext_is_compatible_clause_internal(root, (Node *) var, + relid, attnums) && + statext_is_compatible_clause_internal(root, (Node *) var2, + relid, attnums); + } + else + return statext_is_compatible_clause_internal(root, (Node *) var, + relid, attnums); } /* AND/OR/NOT clause */ @@ -1450,3 +1460,49 @@ examine_opclause_expression(OpExpr *expr, Var **varp, Const **cstp, bool *varonl return true; } + +bool +examine_opclause_expression2(OpExpr *expr, Var **varap, Var **varbp) +{ + Var *vara; + Var *varb; + Node *leftop, + *rightop; + + /* enforced by statext_is_compatible_clause_internal */ + Assert(list_length(expr->args) == 2); + + leftop = linitial(expr->args); + rightop = lsecond(expr->args); + + /* strip RelabelType from either side of the expression */ + if (IsA(leftop, RelabelType)) + leftop = (Node *) ((RelabelType *) leftop)->arg; + + if (IsA(rightop, RelabelType)) + rightop = (Node *) ((RelabelType *) rightop)->arg; + + if (IsA(leftop, Var) && IsA(rightop, Var)) + { + vara = (Var *) leftop; + varb = (Var *) rightop; + } + else + return false; + + /* + * Both variables have to be for the same relation (otherwise it's a + * join clause, and we don't deal with those yet. + */ + if (vara->varno != varb->varno) + return false; + + /* return pointers to the extracted parts if requested */ + if (varap) + *varap = vara; + + if (varbp) + *varbp = varb; + + return true; +} diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c index 3f42713aa2..4b51af287e 100644 --- a/src/backend/statistics/mcv.c +++ b/src/backend/statistics/mcv.c @@ -1581,6 +1581,7 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, /* valid only after examine_opclause_expression returns true */ Var *var; + Var *var2; Const *cst; bool varonleft; @@ -1651,6 +1652,66 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses, matches[i] = RESULT_MERGE(matches[i], is_or, match); } } + else if (examine_opclause_expression2(expr, &var, &var2)) + { + int idx; + int idx2; + + /* match the attribute to a dimension of the statistic */ + idx = bms_member_index(keys, var->varattno); + idx2 = bms_member_index(keys, var2->varattno); + + /* + * Walk through the MCV items and evaluate the current clause. + * We can skip items that were already ruled out, and + * terminate if there are no remaining MCV items that might + * possibly match. + */ + for (i = 0; i < mcvlist->nitems; i++) + { + bool match = true; + MCVItem *item = &mcvlist->items[i]; + + /* + * When either of the MCV items is NULL we can treat this + * as a mismatch. We must not call the operator because + * of strictness. + */ + if (item->isnull[idx] || item->isnull[idx2]) + { + matches[i] = RESULT_MERGE(matches[i], is_or, false); + continue; + } + + /* + * Skip MCV items that can't change result in the bitmap. + * Once the value gets false for AND-lists, or true for + * OR-lists, we don't need to look at more clauses. + */ + if (RESULT_IS_FINAL(matches[i], is_or)) + continue; + + /* + * First check whether the constant is below the lower + * boundary (in that case we can skip the bucket, because + * there's no overlap). + * + * We don't store collations used to build the statistics, + * but we can use the collation for the attribute itself, + * as stored in varcollid. We do reset the statistics after + * a type change (including collation change), so this is + * OK. We may need to relax this after allowing extended + * statistics on expressions. + */ + match = DatumGetBool(FunctionCall2Coll(&opproc, + var->varcollid, + item->values[idx], + item->values[idx2])); + + /* update the match bitmap with the result */ + matches[i] = RESULT_MERGE(matches[i], is_or, match); + } + } } else if (IsA(clause, NullTest)) { diff --git a/src/include/statistics/extended_stats_internal.h b/src/include/statistics/extended_stats_internal.h index 5171895bba..23217497bb 100644 --- a/src/include/statistics/extended_stats_internal.h +++ b/src/include/statistics/extended_stats_internal.h @@ -98,6 +98,8 @@ extern SortItem *build_sorted_items(int numrows, int *nitems, HeapTuple *rows, extern bool examine_opclause_expression(OpExpr *expr, Var **varp, Const **cstp, bool *varonleftp); +extern bool examine_opclause_expression2(OpExpr *expr, + Var **varap, Var **varbp); extern Selectivity mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat, -- 2.21.0