aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/util/predtest.c59
-rw-r--r--src/test/modules/test_predtest/expected/test_predtest.out3
-rw-r--r--src/test/modules/test_predtest/sql/test_predtest.sql1
-rw-r--r--src/test/regress/expected/inherit.out6
4 files changed, 46 insertions, 23 deletions
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index fb963d0a8fe..446207de30f 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -100,7 +100,7 @@ static Node *extract_not_arg(Node *clause);
static Node *extract_strong_not_arg(Node *clause);
static bool clause_is_strict_for(Node *clause, Node *subexpr);
static bool operator_predicate_proof(Expr *predicate, Node *clause,
- bool refute_it);
+ bool refute_it, bool weak);
static bool operator_same_subexprs_proof(Oid pred_op, Oid clause_op,
bool refute_it);
static bool operator_same_subexprs_lookup(Oid pred_op, Oid clause_op,
@@ -1137,7 +1137,7 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
}
/* Else try operator-related knowledge */
- return operator_predicate_proof(predicate, clause, false);
+ return operator_predicate_proof(predicate, clause, false, weak);
}
/*----------
@@ -1232,7 +1232,7 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
}
/* Else try operator-related knowledge */
- return operator_predicate_proof(predicate, clause, true);
+ return operator_predicate_proof(predicate, clause, true, weak);
}
@@ -1498,9 +1498,8 @@ static const StrategyNumber BT_refute_table[6][6] = {
* values, since then the operators aren't being given identical inputs. But
* we only support that for btree operators, for which we can assume that all
* non-null inputs result in non-null outputs, so that it doesn't matter which
- * two non-null constants we consider. Currently the code below just reports
- * "proof failed" if either constant is NULL, but in some cases we could be
- * smarter (and that likely would require checking strong vs. weak proofs).
+ * two non-null constants we consider. If either constant is NULL, we have
+ * to think harder, but sometimes the proof still works, as explained below.
*
* We can make proofs involving several expression forms (here "foo" and "bar"
* represent subexpressions that are identical according to equal()):
@@ -1528,7 +1527,8 @@ static const StrategyNumber BT_refute_table[6][6] = {
* and we dare not make deductions with those.
*/
static bool
-operator_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
+operator_predicate_proof(Expr *predicate, Node *clause,
+ bool refute_it, bool weak)
{
OpExpr *pred_opexpr,
*clause_opexpr;
@@ -1675,17 +1675,46 @@ operator_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
* We have two identical subexpressions, and two other subexpressions that
* are not identical but are both Consts; and we have commuted the
* operators if necessary so that the Consts are on the right. We'll need
- * to compare the Consts' values. If either is NULL, fail.
- *
- * Future work: in some cases the desired proof might hold even with NULL
- * constants. But beware that we've not yet identified the operators as
- * btree ops, so for instance it'd be quite unsafe to assume they are
- * strict without checking.
+ * to compare the Consts' values. If either is NULL, we can't do that, so
+ * usually the proof fails ... but in some cases we can claim success.
*/
- if (pred_const->constisnull)
- return false;
if (clause_const->constisnull)
+ {
+ /* If clause_op isn't strict, we can't prove anything */
+ if (!op_strict(clause_op))
+ return false;
+
+ /*
+ * At this point we know that the clause returns NULL. For proof
+ * types that assume truth of the clause, this means the proof is
+ * vacuously true (a/k/a "false implies anything"). That's all proof
+ * types except weak implication.
+ */
+ if (!(weak && !refute_it))
+ return true;
+
+ /*
+ * For weak implication, it's still possible for the proof to succeed,
+ * if the predicate can also be proven NULL. In that case we've got
+ * NULL => NULL which is valid for this proof type.
+ */
+ if (pred_const->constisnull && op_strict(pred_op))
+ return true;
+ /* Else the proof fails */
+ return false;
+ }
+ if (pred_const->constisnull)
+ {
+ /*
+ * If the pred_op is strict, we know the predicate yields NULL, which
+ * means the proof succeeds for either weak implication or weak
+ * refutation.
+ */
+ if (weak && op_strict(pred_op))
+ return true;
+ /* Else the proof fails */
return false;
+ }
/*
* Lookup the constant-comparison operator using the system catalogs and
diff --git a/src/test/modules/test_predtest/expected/test_predtest.out b/src/test/modules/test_predtest/expected/test_predtest.out
index 9dd8aecefda..5574e03204f 100644
--- a/src/test/modules/test_predtest/expected/test_predtest.out
+++ b/src/test/modules/test_predtest/expected/test_predtest.out
@@ -781,13 +781,12 @@ w_i_holds | f
s_r_holds | f
w_r_holds | f
--- 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
$$);
-[ RECORD 1 ]-----+--
-strong_implied_by | f
+strong_implied_by | t
weak_implied_by | f
strong_refuted_by | f
weak_refuted_by | f
diff --git a/src/test/modules/test_predtest/sql/test_predtest.sql b/src/test/modules/test_predtest/sql/test_predtest.sql
index 298b8bf5ea2..27347358439 100644
--- a/src/test/modules/test_predtest/sql/test_predtest.sql
+++ b/src/test/modules/test_predtest/sql/test_predtest.sql
@@ -306,7 +306,6 @@ select x <= 5, x in (1,3,5,7)
from integers
$$);
--- 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
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index d768dc02151..f56151fc1e7 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1739,11 +1739,7 @@ explain (costs off) select * from list_parted where a = 'ab' or a in (null, 'cd'
Append
-> Seq Scan on part_ab_cd
Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
- -> Seq Scan on part_ef_gh
- Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
- -> Seq Scan on part_null_xy
- Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-(7 rows)
+(3 rows)
explain (costs off) select * from list_parted where a = 'ab';
QUERY PLAN