Commit 5748f3a0 authored by Tom Lane's avatar Tom Lane

Improve predtest.c's internal docs, and enhance its functionality a bit.

Commit b08df9ca left things rather poorly documented as far as the
exact semantics of "clause_is_check" mode went.  Also, that mode did
not really work correctly for predicate_refuted_by; although given the
lack of specification as to what it should do, as well as the lack
of any actual use-case, that's perhaps not surprising.

Rename "clause_is_check" to "weak" proof mode, and provide specifications
for what it should do.  I defined weak refutation as meaning "truth of A
implies non-truth of B", which makes it possible to use the mode in the
part of relation_excluded_by_constraints that checks for mutually
contradictory WHERE clauses.  Fix up several places that did things wrong
for that definition.  (As far as I can see, these errors would only lead
to failure-to-prove, not incorrect claims of proof, making them not
serious bugs even aside from the fact that v10 contains no use of this
mode.  So there seems no need for back-patching.)

In addition, teach predicate_refuted_by_recurse that it can use
predicate_implied_by_recurse after all when processing a strong NOT-clause,
so long as it asks for the correct proof strength.  This is an optimization
that could have been included in commit b08df9ca, but wasn't.

Also, simplify and generalize the logic that checks for whether nullness of
the argument of IS [NOT] NULL would force overall nullness of the predicate
or clause.  (This results in a change in the partition_prune test's output,
as it is now able to prune an all-nulls partition that it did not recognize
before.)

In passing, in PartConstraintImpliedByRelConstraint, remove bogus
conversion of the constraint list to explicit-AND form and then right back
again; that accomplished nothing except forcing a useless extra level of
recursion inside predicate_implied_by.

Discussion: https://postgr.es/m/5983.1520487191@sss.pgh.pa.us
parent a63c3274
......@@ -13651,10 +13651,11 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
/*
* PartConstraintImpliedByRelConstraint
* Does scanrel's existing constraints imply the partition constraint?
* Do scanrel's existing constraints imply the partition constraint?
*
* Existing constraints includes its check constraints and column-level
* NOT NULL constraints and partConstraint describes the partition constraint.
* "Existing constraints" include its check constraints and column-level
* NOT NULL constraints. partConstraint describes the partition constraint,
* in implicit-AND form.
*/
bool
PartConstraintImpliedByRelConstraint(Relation scanrel,
......@@ -13724,10 +13725,15 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
make_ands_implicit((Expr *) cexpr));
}
if (existConstraint != NIL)
existConstraint = list_make1(make_ands_explicit(existConstraint));
/* And away we go ... */
/*
* Try to make the proof. Since we are comparing CHECK constraints, we
* need to use weak implication, i.e., we assume existConstraint is
* not-false and try to prove the same for partConstraint.
*
* Note that predicate_implied_by assumes its first argument is known
* immutable. That should always be true for partition constraints, so we
* don't test it here.
*/
return predicate_implied_by(partConstraint, existConstraint, true);
}
......
......@@ -1421,7 +1421,11 @@ relation_excluded_by_constraints(PlannerInfo *root,
safe_restrictions = lappend(safe_restrictions, rinfo->clause);
}
if (predicate_refuted_by(safe_restrictions, safe_restrictions, false))
/*
* We can use weak refutation here, since we're comparing restriction
* clauses with restriction clauses.
*/
if (predicate_refuted_by(safe_restrictions, safe_restrictions, true))
return true;
/*
......@@ -1469,6 +1473,9 @@ relation_excluded_by_constraints(PlannerInfo *root,
* an obvious optimization. Some of the clauses might be OR clauses that
* have volatile and nonvolatile subclauses, and it's OK to make
* deductions with the nonvolatile parts.
*
* We need strong refutation because we have to prove that the constraints
* would yield false, not just NULL.
*/
if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false))
return true;
......
This diff is collapsed.
......@@ -18,8 +18,8 @@
extern bool predicate_implied_by(List *predicate_list, List *clause_list,
bool clause_is_check);
bool weak);
extern bool predicate_refuted_by(List *predicate_list, List *clause_list,
bool clause_is_check);
bool weak);
#endif /* PREDTEST_H */
......@@ -16,6 +16,9 @@ select
case i%11 when 10 then null else i%11 end as x,
case (i/11)%11 when 10 then null else (i/11)%11 end as y
from generate_series(0, 11*11-1) i;
-- and a simple strict function that's opaque to the optimizer
create function strictf(bool, bool) returns bool
language plpgsql as $$begin return $1 and not $2; end$$ strict;
-- Basic proof rules for single boolean variables
select * from test_predtest($$
select x, x
......@@ -109,7 +112,7 @@ $$);
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | t
weak_refuted_by | f
weak_refuted_by | t
s_i_holds | f
w_i_holds | f
s_r_holds | t
......@@ -199,7 +202,7 @@ w_i_holds | t
s_r_holds | f
w_r_holds | t
-- XXX seems like we should be able to refute x is null here
-- Assorted not-so-trivial refutation rules
select * from test_predtest($$
select x is null, x
from booleans
......@@ -207,8 +210,8 @@ $$);
-[ RECORD 1 ]-----+--
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | f
weak_refuted_by | f
strong_refuted_by | t
weak_refuted_by | t
s_i_holds | f
w_i_holds | f
s_r_holds | t
......@@ -222,12 +225,68 @@ $$);
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | f
weak_refuted_by | f
weak_refuted_by | t
s_i_holds | f
w_i_holds | t
s_r_holds | f
w_r_holds | t
select * from test_predtest($$
select strictf(x,y), x is null
from booleans
$$);
-[ RECORD 1 ]-----+--
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | f
weak_refuted_by | t
s_i_holds | f
w_i_holds | t
s_r_holds | f
w_r_holds | t
select * from test_predtest($$
select (x is not null) is not true, x
from booleans
$$);
-[ RECORD 1 ]-----+--
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | t
weak_refuted_by | t
s_i_holds | f
w_i_holds | f
s_r_holds | t
w_r_holds | t
select * from test_predtest($$
select strictf(x,y), (x is not null) is false
from booleans
$$);
-[ RECORD 1 ]-----+--
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | f
weak_refuted_by | t
s_i_holds | f
w_i_holds | t
s_r_holds | f
w_r_holds | t
select * from test_predtest($$
select x > y, (y < x) is false
from integers
$$);
-[ RECORD 1 ]-----+--
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | t
weak_refuted_by | t
s_i_holds | f
w_i_holds | f
s_r_holds | t
w_r_holds | t
-- Tests involving AND/OR constructs
select * from test_predtest($$
select x, x and y
......@@ -369,6 +428,20 @@ w_i_holds | t
s_r_holds | f
w_r_holds | f
select * from test_predtest($$
select x and z, x and y and z
from booleans
$$);
-[ RECORD 1 ]-----+--
strong_implied_by | t
weak_implied_by | t
strong_refuted_by | f
weak_refuted_by | f
s_i_holds | t
w_i_holds | t
s_r_holds | f
w_r_holds | f
select * from test_predtest($$
select z or w, x or y
from booleans
......@@ -644,7 +717,7 @@ $$);
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | t
weak_refuted_by | f
weak_refuted_by | t
s_i_holds | f
w_i_holds | f
s_r_holds | t
......@@ -658,7 +731,7 @@ $$);
strong_implied_by | f
weak_implied_by | f
strong_refuted_by | t
weak_refuted_by | f
weak_refuted_by | t
s_i_holds | f
w_i_holds | f
s_r_holds | t
......@@ -708,7 +781,7 @@ w_i_holds | f
s_r_holds | f
w_r_holds | f
-- XXX ideally, we could prove this case too
-- XXX ideally, we could prove this case too, for strong implication
select * from test_predtest($$
select x <= 5, x in (1,3,5,null)
from integers
......
......@@ -21,6 +21,10 @@ select
case (i/11)%11 when 10 then null else (i/11)%11 end as y
from generate_series(0, 11*11-1) i;
-- and a simple strict function that's opaque to the optimizer
create function strictf(bool, bool) returns bool
language plpgsql as $$begin return $1 and not $2; end$$ strict;
-- Basic proof rules for single boolean variables
select * from test_predtest($$
......@@ -88,7 +92,8 @@ select x, x is unknown
from booleans
$$);
-- XXX seems like we should be able to refute x is null here
-- Assorted not-so-trivial refutation rules
select * from test_predtest($$
select x is null, x
from booleans
......@@ -99,6 +104,26 @@ select x, x is null
from booleans
$$);
select * from test_predtest($$
select strictf(x,y), x is null
from booleans
$$);
select * from test_predtest($$
select (x is not null) is not true, x
from booleans
$$);
select * from test_predtest($$
select strictf(x,y), (x is not null) is false
from booleans
$$);
select * from test_predtest($$
select x > y, (y < x) is false
from integers
$$);
-- Tests involving AND/OR constructs
select * from test_predtest($$
......@@ -151,6 +176,11 @@ select x or y or z, x or z
from booleans
$$);
select * from test_predtest($$
select x and z, x and y and z
from booleans
$$);
select * from test_predtest($$
select z or w, x or y
from booleans
......@@ -276,7 +306,7 @@ select x <= 5, x in (1,3,5,7)
from integers
$$);
-- XXX ideally, we could prove this case too
-- XXX ideally, we could prove this case too, for strong implication
select * from test_predtest($$
select x <= 5, x in (1,3,5,null)
from integers
......
......@@ -235,7 +235,7 @@ explain (costs off) select * from rlp where a = 1::bigint; /* same as above */
Filter: (a = '1'::bigint)
(3 rows)
explain (costs off) select * from rlp where a = 1::numeric; /* no pruning */
explain (costs off) select * from rlp where a = 1::numeric; /* only null can be pruned */
QUERY PLAN
-----------------------------------------------
Append
......@@ -265,11 +265,9 @@ explain (costs off) select * from rlp where a = 1::numeric; /* no pruning */
Filter: ((a)::numeric = '1'::numeric)
-> Seq Scan on rlp_default_30
Filter: ((a)::numeric = '1'::numeric)
-> Seq Scan on rlp_default_null
Filter: ((a)::numeric = '1'::numeric)
-> Seq Scan on rlp_default_default
Filter: ((a)::numeric = '1'::numeric)
(31 rows)
(29 rows)
explain (costs off) select * from rlp where a <= 10;
QUERY PLAN
......
......@@ -60,7 +60,7 @@ explain (costs off) select * from rlp where 1 > a; /* commuted */
explain (costs off) select * from rlp where a <= 1;
explain (costs off) select * from rlp where a = 1;
explain (costs off) select * from rlp where a = 1::bigint; /* same as above */
explain (costs off) select * from rlp where a = 1::numeric; /* no pruning */
explain (costs off) select * from rlp where a = 1::numeric; /* only null can be pruned */
explain (costs off) select * from rlp where a <= 10;
explain (costs off) select * from rlp where a > 10;
explain (costs off) select * from rlp where a < 15;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment