Skip to content

Commit ecc27d5

Browse files
committed
Support boolean columns in functional-dependency statistics.
There's no good reason that the multicolumn stats stuff shouldn't work on booleans. But it looked only for "Var = pseudoconstant" clauses, and it will seldom find those for boolean Vars, since earlier phases of planning will fold "boolvar = true" or "boolvar = false" to just "boolvar" or "NOT boolvar" respectively. Improve dependencies_clauselist_selectivity() to recognize such clauses as equivalent to equality restrictions. This fixes a failure of the extended stats mechanism to apply in a case reported by Vitaliy Garnashevich. It's not a complete solution to his problem because the bitmap-scan costing code isn't consulting extended stats where it should, but that's surely an independent issue. In passing, improve some comments, get rid of a NumRelids() test that's redundant with the preceding bms_membership() test, and fix dependencies_clauselist_selectivity() so that estimatedclauses actually is a pure output argument as stated by its API contract. Back-patch to v10 where this code was introduced. Discussion: https://postgr.es/m/73a4936d-2814-dc08-ed0c-978f76f435b0@gmail.com
1 parent 9f4992e commit ecc27d5

File tree

1 file changed

+63
-47
lines changed

1 file changed

+63
-47
lines changed

src/backend/statistics/dependencies.c

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -736,91 +736,104 @@ pg_dependencies_send(PG_FUNCTION_ARGS)
736736
* dependency_is_compatible_clause
737737
* Determines if the clause is compatible with functional dependencies
738738
*
739-
* Only OpExprs with two arguments using an equality operator are supported.
740-
* When returning True attnum is set to the attribute number of the Var within
741-
* the supported clause.
742-
*
743-
* Currently we only support Var = Const, or Const = Var. It may be possible
744-
* to expand on this later.
739+
* Only clauses that have the form of equality to a pseudoconstant, or can be
740+
* interpreted that way, are currently accepted. Furthermore the variable
741+
* part of the clause must be a simple Var belonging to the specified
742+
* relation, whose attribute number we return in *attnum on success.
745743
*/
746744
static bool
747745
dependency_is_compatible_clause(Node *clause, Index relid, AttrNumber *attnum)
748746
{
749747
RestrictInfo *rinfo = (RestrictInfo *) clause;
748+
Var *var;
750749

751750
if (!IsA(rinfo, RestrictInfo))
752751
return false;
753752

754-
/* Pseudoconstants are not really interesting here. */
753+
/* Pseudoconstants are not interesting (they couldn't contain a Var) */
755754
if (rinfo->pseudoconstant)
756755
return false;
757756

758-
/* clauses referencing multiple varnos are incompatible */
757+
/* Clauses referencing multiple, or no, varnos are incompatible */
759758
if (bms_membership(rinfo->clause_relids) != BMS_SINGLETON)
760759
return false;
761760

762761
if (is_opclause(rinfo->clause))
763762
{
763+
/* If it's an opclause, check for Var = Const or Const = Var. */
764764
OpExpr *expr = (OpExpr *) rinfo->clause;
765-
Var *var;
766-
bool varonleft = true;
767-
bool ok;
768765

769-
/* Only expressions with two arguments are considered compatible. */
766+
/* Only expressions with two arguments are candidates. */
770767
if (list_length(expr->args) != 2)
771768
return false;
772769

773-
/* see if it actually has the right */
774-
ok = (NumRelids((Node *) expr) == 1) &&
775-
(is_pseudo_constant_clause(lsecond(expr->args)) ||
776-
(varonleft = false,
777-
is_pseudo_constant_clause(linitial(expr->args))));
778-
779-
/* unsupported structure (two variables or so) */
780-
if (!ok)
770+
/* Make sure non-selected argument is a pseudoconstant. */
771+
if (is_pseudo_constant_clause(lsecond(expr->args)))
772+
var = linitial(expr->args);
773+
else if (is_pseudo_constant_clause(linitial(expr->args)))
774+
var = lsecond(expr->args);
775+
else
781776
return false;
782777

783778
/*
784-
* If it's not "=" operator, just ignore the clause, as it's not
779+
* If it's not an "=" operator, just ignore the clause, as it's not
785780
* compatible with functional dependencies.
786781
*
787782
* This uses the function for estimating selectivity, not the operator
788783
* directly (a bit awkward, but well ...).
784+
*
785+
* XXX this is pretty dubious; probably it'd be better to check btree
786+
* or hash opclass membership, so as not to be fooled by custom
787+
* selectivity functions, and to be more consistent with decisions
788+
* elsewhere in the planner.
789789
*/
790790
if (get_oprrest(expr->opno) != F_EQSEL)
791791
return false;
792792

793-
var = (varonleft) ? linitial(expr->args) : lsecond(expr->args);
794-
793+
/* OK to proceed with checking "var" */
794+
}
795+
else if (not_clause((Node *) rinfo->clause))
796+
{
795797
/*
796-
* We may ignore any RelabelType node above the operand. (There won't
797-
* be more than one, since eval_const_expressions() has been applied
798-
* already.)
798+
* "NOT x" can be interpreted as "x = false", so get the argument and
799+
* proceed with seeing if it's a suitable Var.
799800
*/
800-
if (IsA(var, RelabelType))
801-
var = (Var *) ((RelabelType *) var)->arg;
801+
var = (Var *) get_notclausearg(rinfo->clause);
802+
}
803+
else
804+
{
805+
/*
806+
* A boolean expression "x" can be interpreted as "x = true", so
807+
* proceed with seeing if it's a suitable Var.
808+
*/
809+
var = (Var *) rinfo->clause;
810+
}
802811

803-
/* We only support plain Vars for now */
804-
if (!IsA(var, Var))
805-
return false;
812+
/*
813+
* We may ignore any RelabelType node above the operand. (There won't be
814+
* more than one, since eval_const_expressions has been applied already.)
815+
*/
816+
if (IsA(var, RelabelType))
817+
var = (Var *) ((RelabelType *) var)->arg;
806818

807-
/* Ensure var is from the correct relation */
808-
if (var->varno != relid)
809-
return false;
819+
/* We only support plain Vars for now */
820+
if (!IsA(var, Var))
821+
return false;
810822

811-
/* we also better ensure the Var is from the current level */
812-
if (var->varlevelsup > 0)
813-
return false;
823+
/* Ensure Var is from the correct relation */
824+
if (var->varno != relid)
825+
return false;
814826

815-
/* Also skip system attributes (we don't allow stats on those). */
816-
if (!AttrNumberIsForUserDefinedAttr(var->varattno))
817-
return false;
827+
/* We also better ensure the Var is from the current level */
828+
if (var->varlevelsup != 0)
829+
return false;
818830

819-
*attnum = var->varattno;
820-
return true;
821-
}
831+
/* Also ignore system attributes (we don't allow stats on those) */
832+
if (!AttrNumberIsForUserDefinedAttr(var->varattno))
833+
return false;
822834

823-
return false;
835+
*attnum = var->varattno;
836+
return true;
824837
}
825838

826839
/*
@@ -891,12 +904,12 @@ find_strongest_dependency(StatisticExtInfo *stats, MVDependencies *dependencies,
891904

892905
/*
893906
* dependencies_clauselist_selectivity
894-
* Return the estimated selectivity of the given clauses using
895-
* functional dependency statistics, or 1.0 if no useful functional
907+
* Return the estimated selectivity of (a subset of) the given clauses
908+
* using functional dependency statistics, or 1.0 if no useful functional
896909
* dependency statistic exists.
897910
*
898911
* 'estimatedclauses' is an output argument that gets a bit set corresponding
899-
* to the (zero-based) list index of clauses that are included in the
912+
* to the (zero-based) list index of each clause that is included in the
900913
* estimated selectivity.
901914
*
902915
* Given equality clauses on attributes (a,b) we find the strongest dependency
@@ -932,6 +945,9 @@ dependencies_clauselist_selectivity(PlannerInfo *root,
932945
AttrNumber *list_attnums;
933946
int listidx;
934947

948+
/* initialize output argument */
949+
*estimatedclauses = NULL;
950+
935951
/* check if there's any stats that might be useful for us. */
936952
if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES))
937953
return 1.0;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy