Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+63-47
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)